Django如何通过连接获取相关对象?

时间:2017-11-08 17:27:41

标签: django django-models

我的models与以下内容类似:

class Reporter(models.Model):
    def gold_star(self):
        return self.article_set.get().total_views >= 100000

class Article(models.Model):
    reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
    total_views = models.IntegerField(default=0, blank=True)

然后在其中一个templates我有这一行:

{% if r.gold_star %}<img src="{% static 'gold-star.png' %}">{% endif %}

显然django发送与页面上的记者一样多的查询...理想情况下,这可能只是一个查询,它将按标准和join适当的文章选择记者。有办法吗?

修改

select_relatedprefetch_related似乎都不起作用,因为我在Reporter表上选择然后使用RelatedManager来访问相关数据在Article

换句话说,django在知道非空queryset之前不知道要预取的内容。

因为文章只能有一个记者,所以可以将这些表连接在一起,然后将过滤器应用于子查询,我只是无法找到它在{{1}中的完成方式。查询语言。

还有其他选择 - 在django表格中选择并按Article字段过滤,但这种方法存在问题。如果我删除了某位记者的所有文章,那么我就无法将该记者列入名单,从Reporter的角度来看,这样的记者不存在,但记者却在Article表。

EDIT2

我尝试了人们在评论中建议的内容。以下生成所需的查询:

Reporter

reporters = Reporter.objects.filter(**query).select_related().annotate( gold_star=Case( When(article__total_views__gte=0, then=Value(1)), default=Value(0), output_field=IntegerField() ) ) 生成的查询:

django

现在我只需要找出一种方法,如何生成类似的查询但没有SELECT `portal_reporter`.`id`, ..., CASE WHEN `portal_article`.`total_views` >= 0 THEN 1 ELSE 0 END AS `gold_star` FROM `portal_reporter` LEFT OUTER JOIN `portal_article` ON (`portal_reporter`.`id` = `portal_article`.`reporter_id`) WHERE ... / Case语句。

EDIT3

如果我选择略有不同的策略,那么When会选择错误的联接类型:

django

此代码产生类似的查询但使用query['article__id__gte'] = 0 reporters = Reporter.objects.filter(**query).select_related() 代替所需的INNER JOIN

LEFT OUTER JOIN

2 个答案:

答案 0 :(得分:1)

您可以使用select_relatedhttps://docs.djangoproject.com/en/1.11/ref/models/querysets/#select-related)在相关表格上进行联接。

还有prefetch_relatedhttps://docs.djangoproject.com/en/1.11/ref/models/querysets/#prefetch-related)使用IN子句通过额外查询获取相关对象。这些差异在文档中有解释,但转载如下:

  

select_related通过创建SQL连接并在SELECT语句中包含相关对象的字段来工作。因此,select_related在同一数据库查询中获取相关对象。但是,为了避免加入“多”关系会产生更大的结果集,select_related仅限于单值关系 - 外键和一对一。

     另一方面,

prefetch_related对每个关系进行单独查找,并在Python中进行“加入”。这允许它预取多对多和多对一对象,除了select_related支持的外键和一对一关系之外,这些对象无法使用select_related完成。它还支持预取GenericRelation和GenericForeignKey,但是,它必须限制为一组同类结果。例如,仅当查询限制为一个ContentType时,才支持预取GenericForeignKey引用的对象。

答案 1 :(得分:0)

如果记者有一篇文章超过100000 gold_star,请尝试注释新字段total_views并将其设置为1:

from django.db.models import Case, When, Value, IntegerField

reporters = Reporter.objects.annotate(
    gold_star=Case(
        When(article__total_views__gte=100000, then=Value(1)), 
        default=Value(0),
        output_field=IntegerField()
    )
)

您可以保留模板代码。