Call manager of select_related field

Viewed 342

I have the following example:

class Profile(models.Model):
    ...

class Person(models.Model):
    profile = models.ForeignKey(Profile, ...)

I have complex Model manager for Profile class and I built a view to list a big amount of Person. I try to compute everything in database so I would like to call the Profile Manager from Person QuerySet.

To do that, I need to do something like:

Person.objects.filter(...).select_related('profile', queryset=Profile.objects.with_computed_revenue().all())

And then I should be able to get person.profile.computed_revenue retrieved from SQL, with the function "with_computed_revenue" being a function of the ProfileManager that annotate computed_revenue.

The final goal is to add in person queryset :

.values('profile__computed_revenue')

It seems possible with Prefetch for prefetch_related, but I cannot find an equivalent with select_related.

2 Answers

If i have got correctly what you mean, As Django docs says in https://docs.djangoproject.com/en/3.2/ref/models/querysets/#prefetch-related :

select_related works by creating an SQL join and including the fields of the related object in the SELECT statement. For this reason, select_related gets the related objects in the same database query. However, to avoid the much larger result set that would result from joining across a ‘many’ relationship, select_related is limited to single-valued relationships - foreign key and one-to-one. prefetch_related, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related, in addition to the foreign key and one-to-one relationships that are supported by select_related.

You should use select_related for FK relations and prefetch_related for Many-To-One relation

In your case, Person model has many-to-one relation to Profile, thus you have to use prefetch_related

To use a custom queryset with select_related you can use prefetch_related and a Prefetch object

Person.objects.prefetch_related(
    Prefetch('profile', queryset=Profile.objects.with_computed_revenue())
)

This will not make the annotations available in values() however

Related