关于python:如何在django的queryset中进行子查询?

how to subquery in queryset in django?

如何在 django\\ 的查询集中创建子查询?例如,如果我有:

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

这就是我所做的。

1
2
Person.objects.value('name', 'age')
Employee.objects.filter(company='Private')

但它不起作用,因为它返回两个输出...


正如 ypercube 所提到的,您的用例不需要子查询。

但无论如何,由于许多人进入此页面以学习如何进行子查询,这里是如何完成的。

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

来源:
http://mattrobenolt.com/the-django-orm-and-subqueries/


1
2
ids = Employee.objects.filter(company='Private').values_list('id', flat=True)
Person.objects.filter(id__in=ids).values('name', 'age')


您的问题的正确答案在这里 https://docs.djangoproject.com/en/2.1/ref/models/expressions/#subquery-expressions

例如:

1
2
3
>>> 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]))

您可以在 Django 中创建子查询,方法是使用未评估的查询集来过滤您的主查询集。在你的情况下,它看起来像这样:

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

我假设您有一个从 PersonEmployee 的反向关系,名为 Employee。当我试图了解过滤器的工作原理时,我发现查看由查询集生成的 SQL 查询很有帮助。

1
print people.query

正如其他人所说,您的示例并不真正需要子查询。您可以加入员工表:

1
people2 = Person.objects.filter(employee__company='Private')

1
2
hero_qs = Hero.objects.filter(category=OuterRef("pk")).order_by("-benevolence_factor")
Category.objects.all().annotate(most_benevolent_hero=Subquery(hero_qs.values('name')[:1]))

生成的sql

1
2
3
4
5
6
7
8
SELECT"entities_category"."id",
      "entities_category"."name",
  (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"

有关详细信息,请参阅此文章。


如果您的子查询没有选择主键,请注意 only

示例:

1
2
3
4
5
6
7
8
9
10
class Customer:
    pass

class Order:
    customer: Customer
    pass

class OrderItem:
    order: Order
    is_recalled: bool
  • 客户有订单
  • 订单有订单项

现在我们正在尝试找到至少有一件被召回的订单商品的所有客户。(1)

这将无法正常工作

1
2
3
4
5
6
7
8
9
10
11
order_ids = OrderItem.objects \\
    .filter(is_recalled=True) \\
    .only("order_id")

customer_ids = OrderItem.objects \\
    .filter(id__in=order_ids) \\
    .only('customer_id')


# BROKEN! BROKEN
customers = Customer.objects.filter(id__in=customer_ids)

上面的代码看起来很不错,但它产生了以下查询:

1
2
3
4
5
6
7
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))

相反,应该使用 select

1
2
3
4
5
6
7
8
9
10
order_ids = OrderItem.objects \\
    .filter(is_recalled=True) \\
    .select("order_id")

customer_ids = OrderItem.objects \\
    .filter(id__in=order_ids) \\
    .select('customer_id')


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

(1) 注意:在实际情况中,我们可能会考虑\\'WHERE EXISTS\\'