Django queryset - 在对象上应用过滤器时仅返回匹配的多记录

Posted

技术标签:

【中文标题】Django queryset - 在对象上应用过滤器时仅返回匹配的多记录【英文标题】:Django queryset - Return only matching manytomany records when applying filter on object 【发布时间】:2013-07-30 03:47:32 【问题描述】:

我似乎无法找到一种方法来限制在对对象应用过滤器时返回的多元素。如果有人能提供帮助,我将不胜感激。

假设我有以下模型:

## models.py

class Address(models.Model):
    street = models.CharField(max_length=128)
    city = models.CharField(max_length=128)
    state_province = models.CharField(max_length=128)
    zipcode = models.CharField(max_length=5)
    country = models.CharField(max_length=128)

class Venue(models.Model):
    address = models.ForeignKey(Address)
    phone_number = models.CharField(max_length=10)


class Event(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    venues = models.ManyToManyField(Venue)

现在,如果我有一个活动将在两个场地举行,一个活动将在一个场地举行,如下所示(为说明目的而简化):

e1 = Event1
e2 = Event2

v1 = Venue1
v2 = Venue2

e1.venues.add(v1,v2)
e2.venues.add(v1)

我想生成一个查询集,它将带回在“Venue1”发生的所有事件,只有“Venue1”作为这些事件的返回地点。

我试过了

event_list = Event.objects.filter(venues__address__city='CityVenue1')

但是 event_list[0].venues.all()

返回“Venue1”和“Venue2”。

我的偏好是:

    过滤视图中的 Event 对象而不是 Venue 对象。 不通过模板中的标签/过滤器应用任何逻辑,并将其留给视图返回性能所需的查询集 原因。

我的模板遍历事件列表如下:

% for event in event_list %
    <p>event.name 
    % for venue in event.venues.all %
         at  venue.address.city 
    % endfor %
    </p>
% endfor %

我希望它返回:

Event1 at CityVenue1
Event2 at CityVenue1

但目前我得到了

Event1 at CityVenue1
       at CityVenue2

Event2 at CityVenue1

提前感谢您的帮助。

【问题讨论】:

【参考方案1】:

当然,您要返回两个场所,您正在从您的模板中查询所有场所。

% for venue in event.venues.all %

这一行执行一个全新的查询,获取活动的所有场地。

相反;在您看来,请进行过滤并明确传递您感兴趣的场所:

target_venue = Venue.objects.get(address__city='CityVenue1')
event_list   = target_venue.event_set.filter(name='EventName')

return render(request, 'events/event-list.html',
    'Events': event_list, 'Venue': target_venue)

然后在你的模板中,

% for Event in Events %
    <p>  Event.name  at  Venue.address.city  </p>
% endfor %

编辑:通过@limelights 的建议改进

【讨论】:

这很好,但您可以使用target_venue.event_set.all() 稍微简化一下,而不是进一步过滤事件模型。 @limelights 从问题中,很明显他只想要符合特定标准的事件,而不是所有事件。 I would like to generate a queryset that would bring back all events going on at 'Venue1'。因此,过滤器是必要的。 根据文档,event_set.all() 应该可以工作。 docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/… 道歉@limelights,你是对的。我将您的评论误读为 Event.objects.all()。我已根据您的建议更新了我的答案。 @Thomas-limelight...不会得到那个场地的所有活动。如果我想根据“名称”等其他元素过滤事件怎么办。我正在尝试在两侧进行过滤,即仅显示符合特定条件的事件,并且仅返回与搜索位置(即城市/州)匹配的场所(街道、城市、州)?我可能要求太多了。我考虑的另一种可能性是返回具有事件的元组列表和基于位置过滤器的匹配场所列表,例如 [(Event1,[Street1-CityVenue1, Street2-CityVenue1]), (Event2,[Street3-CityVenue1 ])].【参考方案2】:

为什么要在一个查询集中所有这些? Venue.objects.get(id=&lt;id_venue1&gt;).event_set.all() 应该在一个查询集中返回所有与场所 1 相关联的事件,并且您可以在另一个查询集中拥有场所 1。


你也可以使用 Venue.objects.get(id=&lt;id_venue1&gt;).values('adress', 'phone_number', 'event__name', 'event__description') 得到类似下面的字典数组:

[
   'adress': '<venue1adress>', 'phone_number': '<venue1phone>', 'event__name': 'foo', 'event__description': 'I am a foo event',
   'adress': '<venue1adress>', 'phone_number': '<venue1phone>', 'event__name': 'bar', 'event__description': 'I am a bar event',
  ...
]

最后,我认为,您还可以修改默认查询集(因为它是一个 python 对象,并且使用 python 对象您可以做任何您想做的事情):

events_for_venur1 = Event.objects.filter(venues=venue1)
for event in events_for_venur1:
  for venue in event.venue_set:
    if venue != venue1:
      venue = None
# Here your queryset is ready, but DO NOT try to save it in the DB !!

【讨论】:

event &lt;-&gt; venue 是一个 m2m 关系,你不会得到你期望的 dict。此外,以这种方式弄乱查询集只是自找麻烦。 哦,对了,我在 .values() 文档末尾的 Django 文档中没有看到警告..

以上是关于Django queryset - 在对象上应用过滤器时仅返回匹配的多记录的主要内容,如果未能解决你的问题,请参考以下文章

QuerySet 对象在 Django Rest Framework 上没有属性“用户”

django的queryset和objects对象

在Django QuerySet上计算vs len

如何从 Django 中的 get_queryset 方法返回多个查询集对象或添加查询集结果

Django - 如何将 QuerySet 转换为 Q 对象?

获取 Django 对象并将其返回包装在 QuerySet 中的函数?