Chain-Model Getting all objects for template

Question:

models.py

class Sirket(models.Model):
    isim = models.CharField(max_length=20)
    def __str__(self):
        return f"{self.isim}"

class Proje(models.Model):
    ad = models.CharField(max_length=20)
    total = models.DecimalField(max_digits=20,decimal_places=0)
    kalan = models.DecimalField(max_digits=20,decimal_places=0)
    tarih = models.DateField()
    firma = models.ForeignKey(Sirket, on_delete=models.CASCADE)
    def __str__(self):
        return f"{self.ad}"

class Santiye(models.Model):
    isim = models.CharField(max_length=20)
    kasa = models.DecimalField(max_digits=20, decimal_places=0, default=0)
    araclar = models.ManyToManyField(Arac)
    baglanti = models.ForeignKey(Proje, on_delete=models.CASCADE)
    def __str__(self):
        return f"{self.isim}"
    
class Personel(models.Model):
    ad = models.CharField(max_length=50)
    cinsiyet = models.CharField(max_length=10)
    yas = models.DecimalField(max_digits=4, decimal_places=0)
    baslamaTarih = models.DateField()
    resim = models.FileField(default="static/images/avatar.jpg")
    birim = models.ForeignKey(Santiye, on_delete=models.CASCADE, null=True)
    def __str__(self):
        return f"{self.ad}"   

views.py

def firma(request):
    sirket = Sirket.objects.all().prefetch_related('proje_set').order_by('id')
    # i have multiple classes with connect each other.
    # like Proje class connected to Sirket class with many-to-one relationship.
    # It goes like : Sirket -> Proje -> Santiye -> Personel 
    # I'm trying to access the Personel.ad objects that are linked to the Sirket.

template

<div class="container">

    <div class="row">
    
        {% for sirket in sirket %}
            <div class="card mb-3">
                <div class="row">
                    <div class="col-md-3">
                    </div>
                    <div class="col-md-9">
                        <div class="card-body">
                                <p class="card-text"><b>Çalışan Personeller:</b></p>
                                    <p>{% for personel in santiye.personel_set.all %}
                                        <li>{{personel.ad}}</li>
                                    {% endfor %}</p> 

                                 **<!-- I try to list santiye personels like above -->  **    

                        </div>
                    </div>
                </div>
            </div>

        {% endfor %}
    </div>

I have multiple classes with connect each other.
Like Proje class connected to Sirket class with many-to-one relationship.
It goes like : Sirket -> Proje -> Santiye -> Personel.
I’m trying to access the Personel.ad objects that are linked to the Sirket.

I tried something like this:

sirket = Sirket.objects.all().prefetch_related('proje__santiye__personel_set').order_by('id')

But not working of course, and gives the following error:

AttributeError at /products/firma
Cannot find ‘proje’ on Sirket object, ‘proje__santiye__personel_set’ is an invalid parameter to prefetch_related()

Is there any way to make this work with view or in template?

Asked By: Ozan Gözlüklü

||

Answers:

This is a bit complex to debug without mirroring the same project on my machine. But here is a possible solution you can try.

In your view, you can use a prefetch-related method to fetch all related objects to the Sirket model.

sirket = Sirket.objects.all().prefetch_related(
    'proje_set__santiye_set__personel_set'
).order_by('id')

This should allow you to access the related Personel objects from the Sirket object in your template.

{% for sirket in sirket %}
    <div class="card mb-3">
        <div class="row">
            <div class="col-md-3">
            </div>
            <div class="col-md-9">
                <div class="card-body">
                    <p class="card-text"><b>Çalışan Personeller:</b></p>
                    <p>
                        {% for proje in sirket.proje_set.all %}
                            {% for santiye in proje.santiye_set.all %}
                                {% for personel in santiye.personel_set.all %}
                                    <li>{{personel.ad}}</li>
                                {% endfor %}
                            {% endfor %}
                        {% endfor %}
                    </p>
                </div>
            </div>
        </div>
    </div>
{% endfor %}
Answered By: dostogircse171

The error message suggests that there is no attribute named 'proje' on the sirket object. This is because you have defined the foreign key relation on the Proje model, not on the Sirket model.

So use the following view:

def firma(request):
    sirket = Sirket.objects.all().prefetch_related('proje_set__santiye_set__personel_set').order_by('id')
    return render(request, "firma.html", {"sirket": sirket})

Here, we are prefetching the related objects from Proje to Personel using the "__" syntax to traverse through the foreign key relations.

Then use in the template as:

<div class="container">
    <div class="row">
        {% for s in sirket %}
            <div class="card mb-3">
                <div class="row">
                    <div class="col-md-3">
                    </div>
                    <div class="col-md-9">
                        <div class="card-body">
                            <p class="card-text"><b>{{s.isim}}:</b></p>
                            {% for p in s.proje_set.all %}
                                <p class="card-text"><b>{{p.ad}}:</b></p>
                                {% for san in p.santiye_set.all %}
                                    <p class="card-text"><b>{{san.isim}}:</b></p>
                                    {% for per in san.personel_set.all %}
                                        <li>{{per.ad}}</li>
                                    {% endfor %}
                                {% endfor %}
                            {% endfor %}
                        </div>
                    </div>
                </div>
            </div>
        {% endfor %}
    </div>
</div>
Answered By: Sunderam Dubey