how to subquery in queryset in django?


how can i have a subquery in django’s queryset? for example if i have:

select name, age from person, employee where = and in (select id from employee where = 'Private')

this is what i have done yet.

Person.objects.value('name', 'age')

but it not working because it returns two output…

Asked By: gadss



ids = Employee.objects.filter(company='Private').values_list('id', flat=True)
Person.objects.filter(id__in=ids).values('name', 'age')
Answered By: Jan Pöschko

as mentioned by ypercube your use case doesn’t require subquery.

but anyway since many people land into this page to learn how to do sub-query here is how its done.

employee_query = Employee.objects.filter(company='Private').only('id').all()
Person.objects.value('name', 'age').filter(id__in=employee_query)


Answered By: Ramast

You can create subqueries in Django by using an unevaluated queryset to filter your main queryset. In your case, it would look something like this:

employee_query = Employee.objects.filter(company='Private')
people = Person.objects.filter(employee__in=employee_query)

I’m assuming that you have a reverse relationship from Person to Employee named employee. I found it helpful to look at the SQL query generated by a queryset when I was trying to understand how the filters work.

print people.query

As others have said, you don’t really need a subquery for your example. You could just join to the employee table:

people2 = Person.objects.filter(employee__company='Private')
Answered By: Don Kirkby

The correct answer on your question is here

As an example:

>>> from django.db.models import OuterRef, Subquery
>>> newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')
>>> Post.objects.annotate(newest_commenter_email=Subquery(newest.values('email')[:1]))
Answered By: Dmitry Anch
hero_qs = Hero.objects.filter(category=OuterRef("pk")).order_by("-benevolence_factor")

the generated sql

SELECT "entities_category"."id",
  (SELECT U0."name"
   FROM "entities_hero" U0
   WHERE U0."category_id" = ("entities_category"."id")
   ORDER BY U0."benevolence_factor" DESC
   LIMIT 1) AS "most_benevolent_hero"
FROM "entities_category"

For more details, see this article.

Answered By: vikas thakur

Take good care with onlyif your subqueries don’t select the primary key.


class Customer:

class Order:
    customer: Customer

class OrderItem:
    order: Order
    is_recalled: bool
  • Customer has Orders
  • Order has OrderItems

Now we are trying to find all customers with at least one recalled order-item.(1)

This will not work properly

order_ids = OrderItem.objects 

customer_ids = OrderItem.objects 

customers = Customer.objects.filter(id__in=customer_ids)

The code above looks very fine, but it produces the following query:

select * from customer where id in (
    select id  -- should be customer_id
    from orders 
    where id in (
        select id -- should be order_id
        from order_items 
        where is_recalled = true))

Instead one should use select

order_ids = OrderItem.objects 

customer_ids = OrderItem.objects 

customers = Customer.objects.filter(id__in=customer_ids)

(1) Note: in a real case we might consider ‘WHERE EXISTS’

Answered By: reto
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.