I have a situation where I need to add additional data to the objects within a Queryset, and I have troubles understanding when it is really beneficial to perform queries that have annotations, over just implementing custom methods on the model class.
As a generic example:
This:
MyModel.objects
.filter(
some_field=some_value
)
.annotate(
has_something=Exists(
Something.objects.filter(
whatever=OuterRef('pk'),
start_date__gte=SIX_MONTHS_AGO
)
),
something_else= ... # Some complex query
)
Versus this:
class MyModel(models.Model):
some_field = CharField()
def has_something(self):
return (self.something_set
.filter(start_date__gte=SIX_MONTHS_AGO)
.count() > 0)
def something_else(self):
... # Make some other queries
class Something(models.Model):
whatever = models.ForeignKey(MyModel)
start_date = models.DateField()
So in both cases I can do something like this wherever I need to (for example, in the templates):
{% for obj in my_model_queryset %}
{{ obj.has_something }}
{% endfor %}
As far as I know, when doing annotations all the work is done by the database engine and everything can be done in a single database hit (I think?). On the other hand, when implementing a method on the model class, additional queries might be performed if you need to get reverse lookups, or data from a foreign model.
I just want to ask, is there any rule of thumb, or any guidance that you all consider for when to use one approach over the another? Especially if the annotations get really complex (e.g, using Q objects, Subqueries, GroupConcat, and so on)
Thanks.