django Queryset filter(a__b__c=d): get hold of b for further filtering

Viewed 130

I am writing a method that computes a complex Django QuerySet. It starts like this

qs1 = A.objects.filter(b_set__c_obj__user=user)
  • qs1 will eventually become the result, but before it does, the method goes on with several further steps of filtering and annotation.
  • b_set is an 1:n relationship, but I know that at most one of the c_obj can actually match.
  • I need to reference this c_obj, because I need another attribute email from it for one of the filtering steps (which is against instances of another model D selected based on c_obj's email).
  • user can be either a User model instance or an OuterRef Django ORM expression, because the whole queryset created by the method is subsequently also to be used in a subquery.
  • Therefore, any solution that involves evaluating querysets is not suitable for me. I can only build a single queryset.

Hmm?

1 Answers

I need to reference this c_obj, because I need another attribute email from it for one of the filtering steps (which is against instances of another model D selected based on c_obj's email).

If you do all the filtering with the same .filter(…) clause then that c_obj will be the same for all conditions. In other words if you want to filter A such that it has a related B and a related C for example that has as email='foo@bar.com', and as type='user', you can filter with:

qs1 = A.objects.filter(
    b_set__c_obj__email='foo@bar.com',
    b_set__c_obj__type='user'
)

This is thus different from:

qs1 = A.objects.filter(
    b_set__c_obj__email='foo@bar.com'
).filter(
    b_set__c_obj__type='user'
)

since here it will look for an A object that has a related c_obj with email='foo@bar.com and for a c_obj (not per se the same one) with type='user'.

Related