Django Q对象和m2m查询

时间:2015-04-17 01:49:43

标签: python django django-models django-q

我完全被这种行为搞糊涂了。我显然不理解Q对象,就像我认为的那样,或者我正在做一些非常愚蠢和明显的事情。这就是我遇到的问题。 accepted_attendee_ *是与OfficialProfile的所有m2m关系。在django shell中,为了便于演示。

>>> profile = OfficialProfile.objects.get(user__username='testofficial3')
>>> r = SchedulerEvent.objects.filter(accepted_attendee_referee=profile)
>>> l = SchedulerEvent.objects.filter(accepted_attendee_linesman=profile)
>>> o = SchedulerEvent.objects.filter(accepted_attendee_official=profile)
>>> r
[<SchedulerEvent: andrew>]
>>> l
[]
>>> o
[]

这一切都符合预期。现在,如果我与Q对象结合在一起,事情会变得奇怪。

>>> qevents = SchedulerEvent.objects.filter(Q(accepted_attendee_referee=profile)|Q(accepted_attendee_official=profile)|Q(accepted_attendee_linesman=profile))
>>> qevents
[<SchedulerEvent: andrew>, <SchedulerEvent: andrew>]

返回两个对象,两个对象都有相同的PK - 两个重复的对象。应该只有一个,基于个人查询。但是再一次,当我这样做时:

>>> r|l|o
[<SchedulerEvent: andrew>, <SchedulerEvent: andrew>]

这个OR查询返回两个对象的时候是什么,我相信很清楚,只有一个?

修改

所以我查看了生成的查询,似乎“答案”与Q对象或OR进行无关;相反,它是ORM加入表格的方式。这是SQL及其生成的结果,没有OR:

mysql> SELECT `scheduler_schedulerevent`.`id`, `scheduler_schedulerevent`.`user_id`, `scheduler_schedulerevent`.`title`, `scheduler_schedulerevent`.`description`, `scheduler_schedulerevent`.`start`, `scheduler_schedulerevent`.`end`, `scheduler_schedulerevent`.`location_id`, `scheduler_schedulerevent`.`age_level_id`, `scheduler_schedulerevent`.`skill_level_id`, `scheduler_schedulerevent`.`officiating_system_id`, `scheduler_schedulerevent`.`auto_schedule`, `scheduler_schedulerevent`.`is_scheduled` 
FROM `scheduler_schedulerevent` 
LEFT OUTER JOIN `scheduler_schedulerevent_accepted_attendee_referee` 
ON ( `scheduler_schedulerevent`.`id` = `scheduler_schedulerevent_accepted_attendee_referee`.`schedulerevent_id` ) 
LEFT OUTER JOIN `scheduler_schedulerevent_accepted_attendee_linesman` 
ON ( `scheduler_schedulerevent`.`id` = `scheduler_schedulerevent_accepted_attendee_linesman`.`schedulerevent_id` ) 
LEFT OUTER JOIN `scheduler_schedulerevent_accepted_attendee_official` 
ON ( `scheduler_schedulerevent`.`id` = `scheduler_schedulerevent_accepted_attendee_official`.`schedulerevent_id` );

+----+---------+---------------+-------------+---------------------+---------------------+-------------+--------------+----------------+-----------------------+---------------+--------------+
| id | user_id | title         | description | start               | end                 | location_id | age_level_id | skill_level_id | officiating_system_id | auto_schedule | is_scheduled |
+----+---------+---------------+-------------+---------------------+---------------------+-------------+--------------+----------------+-----------------------+---------------+--------------+
|  1 |       1 | Test Event    |             | 2015-04-09 02:00:00 | 2015-04-09 02:30:00 |         161 |            1 |              1 |                     3 |             0 |            0 |
|  2 |       1 | Test          |             | 2015-04-07 20:00:00 | 2015-04-07 21:00:00 |         161 |            1 |              1 |                     3 |             1 |            0 |
|  3 |       1 | Test Auto     |             | 2015-04-07 20:00:00 | 2015-04-07 20:30:00 |         161 |            1 |              1 |                     2 |             0 |            0 |
|  4 |       1 | Test Official |             | 2015-04-16 19:00:00 | 2015-04-16 20:30:00 |         161 |            1 |              1 |                     3 |             0 |            1 |
|  4 |       1 | Test Official |             | 2015-04-16 19:00:00 | 2015-04-16 20:30:00 |         161 |            1 |              1 |                     3 |             0 |            1 |
+----+---------+---------------+-------------+---------------------+---------------------+-------------+--------------+----------------+-----------------------+---------------+--------------+

然后很明显,当您添加OR时,它会根据连接的结果满足两个查询条件。因此,虽然为查询添加不同似乎是不必要的,但这是非常必要的。

2 个答案:

答案 0 :(得分:3)

如果你得到两个对象并且它是相同的,那是因为该对象满足至少两个queryset。

换句话说,<SchedulerEvent: andrew>满足至少两个查询集r l o

如果您不想要重复的对象,请使用.distinct()函数。

SchedulerEvent.objects.filter(Q(accepted_attendee_referee=profile)|Q(accepted_attendee_official=profile)
|Q(accepted_attendee_linesman=profile)).distinct()

答案 1 :(得分:1)

不,你不是很厚,但只是接受底层系统(django的ORM)和Q对象将匹配2个相同的元素,因此只有一个实际匹配。正确使用Q对象非常强大,例如在数据库中搜索查询。在此示例中,您不需要Q对象,但简单的过滤器可以正常工作。

您是否尝试过简单的.filter(...)在您的案例中表现如何?

最后,您并没有尝试理解为什么Q对象将返回该查询集;你试图获得一定的结果,distinct()工作得很好:)