I have a field that is a property, because it's the average of all reviews:
ratings = models.FloatField(null=True, default=0)
@property
def ratings(self):
return self.reviews.aggregate(avg_score=Avg('score'))['avg_score']
The problem is that if I do this, then the field "disappears." In fact, if I run makemigrations
, it deletes the field. The problem is that I use django-filter
, and I want to have the option of filtering by ratings (show me all books that have between a 7 and a 10). If I make it into a property, I can't do it anymore because django-filter
can't "detect" the field anymore.
Is there a way for me to still have property and the field at the same time? Can I maybe make a surrogate field that gets updated everytime ratings is changed and make it equal to that?
Well you can not make something both a field and a property at the same time. Furthermore I would advice not to use a field in the first place, since that will introduce data duplication: you save the data in an aggregated form somewhere else. It turns out that synchronizing data, even on the same database, is a much harder problem than it appears to be.
You can annotate(..)
[Django-doc] the queryset such that the database will calculate the aggregates automatically per item it retrieves. Such queryset looks like:
from django.db.models import Avg
MyModel.objects.annotate(
ratings=Avg('reviews__score')
)
If you need this value often, you can do that in the Manager
[Django-doc] of the model:
class MyModelManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
ratings=Avg('reviews__score')
)
class MyModel(models.Model):
# … model without a ratings field
objects = MyModelManager()