Home > Net >  Django Prefetch Related Issue - Understand it correctly
Django Prefetch Related Issue - Understand it correctly

Time:04-15

I do have the following Issue - I want to display all of the bundles with their component relations in a template:

Here is my ORM-Model:

    class Component(models.Model):
        plenty_var_number = models.CharField(max_length=120, default=None, unique=True, null=True)
        plenty_var_id = models.CharField(max_length=120, default=None, unique=True)
        description = models.TextField(max_length=1000)
        category = models.ForeignKey(Category, on_delete=models.DO_NOTHING, null=False)
        updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
        created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)

        def __str__(self):
            return f"{self.category.name} - {self.plenty_var_number}"


    class Bundle(models.Model):
        active = models.BooleanField(default=True)
        plenty_var_number = models.CharField(max_length=120, default=None, unique=True, null=True)
        plenty_var_id = models.CharField(max_length=120, null=True, default=None)
        car = models.ForeignKey(Car, on_delete=models.DO_NOTHING)
        # m2m defined by BundleComponentRelation
        components = models.ManyToManyField(Component, through="BundleComponentRelation")
        linked_to_plenty = models.BooleanField(default=False)
        price = models.DecimalField(max_digits=10, decimal_places=2, default=-1.00)
        updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
        created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)


    class BundleComponentRelation(models.Model):
        component = models.ForeignKey(Component, on_delete=models.DO_NOTHING)
        bundle = models.ForeignKey(Bundle, on_delete=models.DO_NOTHING)
        qty = models.IntegerField(default=1)
        updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
        created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)

I have played around with select_related and prefetch_related in my view to pass them via context to the template to display it for my users:

html-template:

                    {% for bundle in bundles %}
                        <tr>
                            <td><p >{{ bundle.plenty_var_number }}</p></td>
                            <td>{{ bundle.price }}</td>

                            <td><p >{{ bundle.car }}</p>
                                <p >{{ bundle.car.roof_type }}</p>
                                <p >BJ: {{ bundle.car.production_start }}
                                    - {{ bundle.car.production_end }}</p>
                            </td>

                            {# Bundle Component Relations here #}


                            <td style="font-size: 1em;">
                                <a href={% url "edit_bundle" bundle.pk %}><i
                                        ></i></a>
                                <a href={% url "sync_bundle" bundle.pk %}><i
                                        ></i></a>
                            </td>
                        </tr>
                    {% endfor %}

views.py

def bundle_view(request):
    bundles = Bundle.objects.prefetch_related('components').all()
    print(bundles[0].components)
    return render(request, "all_bundles.html", context={"bundles": bundles})

The output of print(bundles[0].components) is bundle.Component.None

I understood the forward usage of select_related but I do have trouble understanding the reverse thinking of the prefetch_related in my situation.

I think my problem is the lookup syntax of prefetch_related, but I might be wrong here.

What am I missing here?

Thanks in advance.

EDIT

I tried:

         {% for bundle in bundles %}
                        <tr>
                            <td><p >{{ bundle.plenty_var_number }}</p></td>
                            <td>{{ bundle.price }}</td>

                            <td><p >{{ bundle.car }}</p>
                                <p >{{ bundle.car.roof_type }}</p>
                                <p >BJ: {{ bundle.car.production_start }}
                                    - {{ bundle.car.production_end }}</p>
                            </td>

                            {% for comp_rel in bundle.components.all %}
                                {{ comp_rel }}
                            {% endfor %}
                            <td style="font-size: 1em;">
                                <a href={% url "edit_bundle" bundle.pk %}><i
                                        ></i></a>
                                <a href={% url "sync_bundle" bundle.pk %}><i
                                        ></i></a>
                            </td>
                        </tr>
                    {% endfor %}

I wanted to get only the related components to the currently iterated bundle. The problem I get here is that the template triggers the database again:

monitoring

Simply using the bundle.component in the template led to

ManyRelatedManager object is not iterable TypError

CodePudding user response:

There is no problem with your prefetch_related, but the way you want to access these objects, you should do bundles[0].components.all() because objects fetched with reverse relation can be accessed same way as M2M fields

CodePudding user response:

Are you sure it must be prefetch_related? I think it must be select_related.

I think you should use Bundle.objects.select_related('components').all() and Component.objects.prefetch_related('bundle_set').all() with your models. But I'm not sure.

And what about template error - you shoud use {% for component in bundle.components.all %} in template.

And there same problem, mb will helpfull.

  • Related