Django - 过滤相关对象

Posted

技术标签:

【中文标题】Django - 过滤相关对象【英文标题】:Django - filtering on related objects 【发布时间】:2010-09-20 08:34:25 【问题描述】:

对于我的 Django 应用,我有事件、评级和用户。评级通过外键与事件和用户相关。在显示事件列表时,我想通过 user_id 过滤事件的评级,这样我就知道用户是否对事件进行了评级。

如果我这样做:

event_list = Event.objects.filter(rating__user=request.user.id)

(request.user.id 给出当前登录用户的 user_id)...然后我只得到用户评分的事件,而不是整个事件列表。

我需要的可以通过自定义SQL生成:

SELECT *
FROM `events_event`
LEFT OUTER JOIN (
  SELECT *
  FROM `events_rating`
  WHERE user_id = ##
  ) AS temp 
ON events_event.id = temp.user_id

有没有更简单的方法让我不必使用自定义 SQL?

【问题讨论】:

FilteredRelation 对象可能对一些来这里的人有用 【参考方案1】:

为了充分利用 Django,您必须避免尝试进行连接。

“左外连接”实际上是具有可选关系的对象列表。

这只是一个事件列表,Event.objects.all()。有些 Event 对象有等级,有些没有。

您会在视图中获得事件列表。您处理模板中的可选关系。

% for e in event_list %
     e 
    % if e.rating_set.all % e.rating_set % endif %
% endfor %

是一个起点。

【讨论】:

rating_set 是管理与评级关系的经理,因此它将始终存在。即使您确实添加了 .count 或 .all ,也会为您提供所有评级的详细信息,而不仅仅是当前用户的评级。 这不是我的问题 :-) e.rating_set 将是 django.db.models.fields.related.RelatedManager,所以 % if e.rating_set % 总会通过。尝试迭代它会给你 TypeError: 'RelatedManager' object is not iterable。您必须使用 e.rating_set.all 来获得您的预期行为。【参考方案2】:

除了 S.Lott 的建议,你可以考虑使用 select_related() 来限制数据库查询的数量;否则您的模板将对每个事件通过循环进行查询。

Event.objects.all().select_related(depth=1)

depth 参数不是必需的,但如果你的其他模型有额外的外键,它将限制连接的数量。

【讨论】:

【参考方案3】:

filter 方法用于根据指定条件过滤返回哪些对象,所以这里不是你想要的。一种选择是进行第二次查询,以检索当前User 的给定Event 对象的所有评级。

型号:

import collections

from django.db import models

class RatingManager(models.Manager):
    def get_for_user(self, events, user):
        ratings = self.filter(event__in=[event.id for event in events],
                              user=user)
        rating_dict = collections.defaultdict(lambda: None)
        for rating in ratings:
            rating_dict[rating.event_id] = rating
        return rating_dict

class Rating(models.Model):
    # ...
    objects = RatingManager()

查看:

events = Event.objects.all()
user_ratings = Rating.objects.get_for_user(events, request.user)
context = 
    'events': [(event, user_ratings[event.id]) for event in events],

模板:

% for event, user_rating in events %
  % if user_rating % ... % endif %
% endfor %

【讨论】:

【参考方案4】:

我认为你必须这样做。

events=Event.objects.filter(rating__user=request.user.id)
ratings='(select rating from ratings where user_id=%d and event_id=event_events.id '%request.user.id
events=events.extra(select='rating':ratings)

【讨论】:

【参考方案5】:

最好的做法是

from django.db.models import Prefetch


event_list = Event.objects.all().prefetch_related(Prefetch(<related_name>, queryset=Rating.objects.filter(<criteria>)))

= '评级' 如果:

class Rating(models.Model):
    event = models.ForeignKey(Event, related_name='ratings')

这会返回这些事件对象的事件和过滤评级

【讨论】:

以上是关于Django - 过滤相关对象的主要内容,如果未能解决你的问题,请参考以下文章

Django 按最新的相关对象过滤

如何使用附加过滤的相关对象作为 Django 中的字段来获取结果?

Django ORM - 过滤相关对象

如何仅过滤嵌套的相关 django 对象?

在 Django 中注释相关和多重过滤的对象

Django Queryset 过滤器检查相关对象是不是存在