【问题标题】:Why do values lists retain filters in Django?为什么值列表在 Django 中保留过滤器?
【发布时间】:2017-08-03 21:39:11
【问题描述】:

给定下面的模型类:

from django.db import models

class User(models.Model):
    friends = models.ManyToManyField('self', symmetrical=False, blank=True)

如果我有一个包含 n 个用户的 QuerySet,其中至少有一个有两个朋友,然后通过按朋友 ID 过滤该 QuerySet 来检索用户,并对该 QuerySet 执行.values_list() 调用,如下所示:

all_users = User.objects.all()
single_user = User.objects.filter(friends=2)
single_user.values_list('friends', flat=True)

输出结果如下:

>>> [2]

但如果我对对象的朋友字段本身执行相同的 .values_list 调用,如下所示:

single_user.first().friends.values_list('id', flat=True)

导致输出:

>>> [2, 3]

如果这些调用是在同一个对象上进行的,为什么输出不同?

作为参考,这是使用 Django 1.10 和 Python 3.5.2 实现的

编辑:我想澄清一些困惑。我的问题不是我误解了查询本身的结果。在这两种情况下,我最终都得到了正确的用户对象。不同之处在于,在一种情况下,我得到一个计数为 1 的 QuerySet,而在另一种情况下,我得到了 User 对象的实例。

所以换一种方式来陈述我的问题:我感到困惑的是,在 same 对象的不同表示上调用 values_list 如何导致不同的输出?

例如,我有以下查询集

<QuerySet [<User: John Doe, ID: 1]>

还有下面的对象,各有自己的变量

<User: John Doe, ID: 1>

如果我在同一个字段上执行 values_list,以必要的方式,我会得到两个不同的结果。我想解释一下为什么。对我来说,我觉得结果应该是一样的。既然不是,我对这些事情的运作方式有误解,正在寻求澄清。

【问题讨论】:

  • 用户。修正错别字
  • 举例说明一点。这个问题不是关于应该命名什么模型,而是关于查询集。不过感谢您的意见。
  • 我选择了单数,希望能说明它是一个可迭代的。在我们的实际代码中,我们遵循您详细说明的准则。编辑了这个问题,主要是希望提高可读性。
  • 这个问题最初是由于以下查询引发的:User.objects.filter(friends=2).values_list('friends', flat=True)。我觉得这很重要,因为它消除了很多关于他如何得到不同结果的困惑。问题不在于 values 列表在同一个对象上给出了两个不同的结果,而是 values_list 直接与过滤器链接。标题中的问题与此事件有关,而不是显示的示例。

标签: python django django-queryset python-3.5


【解决方案1】:

如果您使用多对多关系,您将获得一个新表来存储两个表之间的关系。

yourapplabel_user_friends 示例

id | from_user_id | to_user_id |
1    1              2  
2    2              1
3    1              1
4    3              1

User.objects.filter(friends=2)

User.objects.filter(friends=2)
# return : 1 instances because there are only one 2 in to_user_id column
User.objects.filter(friends=1)
# return : 3 instances
User.objects.filter(friends=3)
# return : 0 instances

single_user.filter(friends=2).values_list('friends', flat=True)
# return : [2]
single_user.filter(friends=2).values_list('id', flat=True)
# return : [1] 
#because id 1 User instance has friends which is id 2 as ManyToMany relationship

# this row
id | from_user_id | to_user_id |      
1    1              2

single_user.objects.first()

single_user.objects.first()     
# single_user.objects.first() return first instance in user table. id 1 instance

single_user.objects.first().friends.values_list('id', flat=True)
# return : [2,1]
# id 1 instance has 2 instances in ManytoMany relationship.
# so it returns [2,1]

single_user.objects.last().friends.values_list('id', flat=True)
# return : [1]
# single_user.objects.last() returns id 3
# because I have 1,2,3 instances in User table as example.
# if you see yourapplabel_user_friends table, 3 id instance have 1 id as friends.

【讨论】:

  • 感谢您的回答。在我的情况下,单个用户对象在 ManyToManyField 中有两个朋友。但是,当我在实例的字段上调用 ​​values_list(在本例中为朋友)以及在包含该实例的 QuerySet 上调用 values_list 时,它给了我两个不同的结果。如果我尝试获取数据的字段相同,为什么它们的行为不同?您的答案使用 Model 类的 .objects 。在已经过滤后,我正在使用 QuerySets。我希望澄清。对不起,冗长的评论。
  • 我想先看看你是否完全理解我的回答。我以为你无法理解 single_user.objects.filter(friends=2).values_list('friends', flat=True)single_user.objects.first().friends.values_list('id', flat=True) 之间的区别。我给你示例数据和示例返回让你理解。
  • 你用'self'定义了manytomanyfield。你可以认为 from_user_id 是用户,to_user_id 是朋友。它可能会被混淆,因为它将自己称为“自我”多字段定义。
  • 我完全理解查询和两条不同线路上数据状态的区别。但是,如果您有一个对象实例:user = User.objects.all().first() 并执行user.friends.values_list('id', flat=True),它正在执行从 M2M 管理器返回的 QuerySet 上的 values_list。这应该产生与users = User.objects.filter(id=1) 相同的结果,然后执行users.values_list('friends', flat=True)。据我了解,两者都在访问同一个字段和同一个表。
  • 好的。你明白User.objects.filter(friends=something) 是不同的。现在,您将其更改为User.objects.filter(id=1)。我认为User.objects.first().friends.values_list('id', flat=True)User.objects.filter(id=1).values_list('friends', flat=True) 应该返回相同的结果。你能分享User.objects.filter(id=1)的回报吗? User.objects.first().friends.values_list('id', flat=True)User.objects.filter(id=1).values_list('friends', flat=True)
【解决方案2】:

它们是不同的,因为两个查询的含义不同

user.values_list('friends', flat=True) ...返回用户

的好友ID

user.first().friends.values_list('id', flat=True) ...返回“用户”的第一个朋友的朋友的 ID。换句话说,您实际上是在这里查找不同用户的朋友。


调试此类事情的一个好方法是查看从查询生成的 SQL。这可以帮助您了解 Django 是如何翻译您的查询的。

str(user.first().friends.values_list('id', flat=True).query)

对比

str(user.values_list('friends', flat=True).query)

无法读取输出?使用 sqlparse

sqlparse.format(str(user.first().friends.values_list('id', flat=True).query))

【讨论】:

  • 不会 user.first().friends.values_list() 返回用户 QuerySet 中第一个用户的朋友的 id 列表吗?在我的例子中,两个版本都作用于一个实例:一个在 QuerySet 中,一个作为用户实例。
  • 虽然 QuerySet 生成了两个完全不同的 SQL 查询,但我认为 @david-lam 对它们的解释并不完全正确。区别似乎确实在于一个人是作用于模型实例还是作用于中间查询集。
  • @das-g 知道为什么 Instance 和 QuerySet 的结果不同吗?
  • 好吧,因为 Django 构建查询的方式(有点天真)。尽管来自 QuerySet 的查询结果是否是预期的(或者更确切地说,是计算预期结果的结果,尽管结果并不那么明显)或者这是否是 Django 错误并且查询的结果实际上应该是相同的,我不知道。
猜你喜欢
  • 2010-09-15
  • 1970-01-01
  • 2021-02-21
  • 2015-12-29
  • 1970-01-01
  • 2014-12-21
  • 2021-08-19
  • 2013-12-12
  • 1970-01-01
相关资源
最近更新 更多