我正在 Django 中创建一个稀疏的首选项表。我的模型很简单:
class Preference(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='preferences')
preference = models.CharField(max_length=255, db_index=True)
value = models.BooleanField()
某些首选项具有默认状态,因此我需要能够向数据库提出两个问题:“哪些用户将此首选项设置为某个值?”以及“哪些用户没有将此偏好设置为该值(因为他们没有设置偏好,或者因为他们主动将偏好设置为另一个值)?”
我的问题是前一个问题有效,但后一个问题(相同的查询子句,但使用
exclude()
而不是 filter()
)不起作用。例如:我的测试数据库有 14 个用户,单个用户设置了两个首选项:
'PREF_A'
设置为 True
和 'PREF_B'
设置为 False
.>>> User.objects.all().count()
14
>>> User.objects.filter(preferences__preference="PREF_A", preferences__value=True).count()
1
>>> User.objects.exclude(preferences__preference="PREF_A", preferences__value=True).count()
13
>>> User.objects.filter(preferences__preference="PREF_A", preferences__value=False).count()
0
>>> User.objects.exclude(preferences__preference="PREF_A", preferences__value=False).count()
13
所以,我的结果是:
共有 14 个用户
此查询哪里出错了,我如何编写查询以正确排除将特定首选项设置为特定值的人?
我试过使用
Q
和 ~Q
看看行为是否会有所不同,但结果是一样的。
最佳答案
这是 Django 中仍然存在的问题,其中 exclude()
不作为 filter()
的反面.这是the documentation explaining the difference :
Note
The behavior of
filter()
for queries that span multi-value relationships, as described above, is not implemented equivalently forexclude()
. Instead, the conditions in a singleexclude()
call will not necessarily refer to the same item.For example, the following query would exclude blogs that contain both entries with “Lennon” in the headline and entries published in 2008:
Blog.objects.exclude( entry__headline__contains='Lennon', entry__pub_date__year=2008, )
However, unlike the behavior when using
filter()
, this will not limit blogs based on entries that satisfy both conditions. In order to do that, i.e. to select all blogs that do not contain entries published with “Lennon” that were published in 2008, you need to make two queries:Blog.objects.exclude( entry__in=Entry.objects.filter( headline__contains='Lennon', pub_date__year=2008, ), )
你所做的可能是要走的路。
关于带有多个相关字段子句的 Django 查询集 exclude(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16704560/