We have two models (simplified versions):
class Contestant(models.Model):
email = models.EmailField(max_length=255, unique=True)
# plus some other fields
@property
def total_points(self):
return self.points.aggregate(total=Sum('value'))['total'] or 0
class Points(models.Model):
contestant = models.ForeignKey(Contestant, related_name='points')
value = models.PositiveIntegerField()
# plus some other fields which determine based on what we
# awarded ``Points.value``
When we display a list of contestants along with their total_points
value, it
results in an extra query for each result - i.e. the following queries are performed:
total_points
value of 1st contestanttotal_points
value of 2nd contestantI tried altering the queryset to prefetch the data as follows:
Contestant.objects.filter(...).prefetch_related('points')
..., however even though it works, the prefetched data is not utilized when
listing contestants (so each result still tries to fetch total_points
in a separate query).
Is it possible to:
Contestant.total_points
@property method)?(I'm listing results in tastypie
, if it matters.)
Thank you.
When your aim is to add aggregated values to each item, you should use annotate
, instead of aggregate
.
For example (a simple query, no additional methods required):
Contestant.objects.filter(...).annotate(total_points=Sum('points__value'))
If you really want to put this code out of your query: you can, but a model method is not a right way to do this. Methods on models are for operations on single instances. If you want to do something on a whole QuerySet use an ORM Manager instead.
With a Manager this would look like this:
class TotalPointsManager(models.Manager):
def get_queryset(self):
return super(TotalPointsManager, self).get_queryset().annotate(total_points=Sum('points__value'))
class Contestant(models.Model):
email = models.EmailField(max_length=255, unique=True)
objects = TotalPointsManager() # You are overriding the default manager!
and then you would construct your query as usual (you can drop prefetch_related
):
Contestant.objects.filter(...)
...and total_points
field would become "magically" available for every object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With