Django:如何在没有N + 1请求的情况下为查询集的每个元素添加自定义属性?

时间:2017-08-17 22:43:09

标签: sql django

假设我有一个非常基本的用户模型,名称和年龄:

class User(models.Model):
    name = CharField()  # e.g. John Smith
    age = IntegerField()  # e.g. 21

我希望过滤18岁以上的用户,并为每个用户添加一个特殊属性namesakes(例如,对于"John Smith",它将类似于["John Williams", "John for Neumann", ...])。

我知道,如何在N + 1个请求中执行此操作:

for user in User.objects.filter(age__gt=18):
    user.namesakes = User.objects.filter(name__startswith=user.name.split()).\
                                   all()

但是如何在一个请求中执行此操作?理想情况下,对于查询集中的每个User对象,我想创建一个包含同名查询集的自定义属性namesakes。这与annotate()prefetch_related() with to_attr非常相似,但不完全相同。

如果可能的话,我也更愿意避免使用原始SQL。

谢谢。

1 个答案:

答案 0 :(得分:1)

我找到了一个blog post,做了一件与我需要的东西非常相似的东西......亲爱的上帝......

>>> from django.db.models import F, Q, Case, IntegerField, Sum, Value, When
>>> from django.db.models.functions import Coalesce
>>> pizzas = Pizza.objects.annotate(
...     uses_name_of_another_pizza_as_name_prefix=Coalesce(
...         Sum(
...             Case(
...                 When(
...                     Q(
...                         ~Q(pk=F('category__pizzas')) &
...                         Q(name__startswith=F('category__pizzas__name'))
...                     ),
...                     then=Value(1)
...                 ),
...                 output_field=IntegerField(),
...             ),
...         ),
...         0,
...     ),
... )
>>> [p, p.uses_name_of_another_pizza_as_name_prefix for p in pizzas]
[
    (<Pizza: Hawaiian>, 0),
    (<Pizza: Hawaiian King>, 1),
    (<Pizza: Pepperoni>, 0),
]

尽管如此,它仍然不能满足我的需要。所以,我更喜欢使用原始SQL。