user3348051
user3348051

Reputation:

Django get rid of duplicated queries in nested models

I have models as shown below,

class Manufacturer(models.Model):
    name = models.CharField(max_length=100)

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    name = models.CharField(max_length=300)
    
    @property
    def latest_variant(self):
        return self.carvariant_set.last()

class CarVariant(models.Model):
    car = models.ForeignKey(Car, on_delete=models.CASCADE)
    name = models.CharField(max_length=300)

and I am making a query to get the latest variant of all cars, I am getting much duplicated queries. I couldn't eliminate it with prefetch_related

Car.objects.all().prefetch_related('carvariant_set')

How can I eliminate the duplicated queries?

Upvotes: 3

Views: 393

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477308

If you use .prefetch_related it will populate the carvariant_set value, but only for a .all() query, not for a .last(), that will trigger a new query.

What we can do is define a property like:

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    name = models.CharField(max_length=300)
    
    @property
    def latest_variant(self):
        items = getattr(self, '_latest_variants', ())
        if items:
            return items[-1]
        return self.carvariant_set.last()

Then we can prefetch the related object with:

from django.db.models import Prefetch

Car.objects.prefetch_related(
    Prefetch(
        'carvariant_set',
        queryset=CarVariant.objects.order_by('pk'),
        to_attr='_latest_variants'
    )
)

Upvotes: 3

yo1122
yo1122

Reputation: 321

To get rid of duplicates you use "distinct()". For example Car.objects.all().prefetch_related('carvariant_set').distinct(). You can read about it here: https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.query.QuerySet.distinct

sometimes you might need to tell the "distinct" function which fields make an object distinct. By default it's the id, but you can do something like "distinct('name')" in order to avoid getting 2 instances with the same name for example.

Upvotes: 0

Related Questions