首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >Django查询集附加或注释相关对象字段

Django查询集附加或注释相关对象字段
EN

Stack Overflow用户
提问于 2013-11-22 07:28:03
回答 2查看 21.3K关注 0票数 18

需要附加到查询集结果相关的对象字段。

型号:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class User(models.Model):
    name = models.CharField(max_length=50)
    friends = models.ManyToManyField('self', through='Membership',
        blank=True, null=True, symmetrical=False)


class Membership(models.Model):
    status = models.CharField(choices=SOME_CHOICES, max_length=50)
    from_user = models.ForeignKey(User, related_name="member_from")
    to_user = models.ForeignKey(User, related_name="member_to")

我可以这样做:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> User.objects.all().values('name', 'member_from__status')
[{'member_from__status': u'accepted', 'name': 'Ann'}, {'member_from__status': u'thinking', 'name': 'John'}]

'member_from__status'包含了我需要的信息。但除此之外,我还需要一个模型实例。

我想要的是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> users_with_status = User.objects.all().do_something('member_from__status')
>>> users_with_status
[<User 1>, <User 2>]

>>> users_with_status[0] # <-- this is an object, so i can access to all its methods

queryset中的每个实例都有一个带有相应值的'member_from__status‘字段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> users_with_status[0].member_from__status
u'accepted'

如何才能做到这一点?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-12-09 03:36:46

您可以使用annotateF

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> from django.db.models import F
>>> users = User.objects.all().annotate(member_from__status=F('member_from__status'))
>>> users[0].member_from__status
'accepted'

使用Django版本1.11和2.2进行了测试

回复评论

这里,我猜我们更感兴趣的是member_to__status而不是member_from__status (我们希望被请求的用户接受友谊,而不是相反)

下面是我的membership表的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
status   |  from|   to
---------+------+-----
accepted |     1|    2
accepted |     2|    3
 refused |     3|    1

由于"it's not possible to have a symmetrical, recursive ManyToManyField“(例如,用户1接受了用户2,因此用户1现在在用户2的朋友列表中,但用户2也在用户1的朋友列表中),这就是我检索所有实际朋友的方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> for user in User.objects.all():
...     friends = user.friends.filter(member_to__status='accepted')
...     print(str(user.id) + ': ' + ','.join([str(friend.id) for friend in friends]))
1: 2
2: 3
3: 

如果你想在<User 2>的朋友中加入<User 1>,我建议在1到2的朋友关系已经被接受的情况下添加另一个Membership

这里我们假设一个Membership只能被接受一次,因此添加unique-together选项可能是个好主意。尽管如此,如果我们有几个具有相同from_userMembership,则会发生以下情况:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> # <Insert several Membership from one to another User>
>>> Membership.objects.filter(from_user__id=1, to_user__id=2).count()
3
>>> users = User.objects.annotate(member_from__id=F('member_from__id'))
>>> print('User Membership')
... for user in users:
...    print("%4d %10d" % (user.id, user.member_from__id))
User Membership
   1          1
   2          2
   1          3  # User 1 again
   1          4  # User 1 again
   3          None
票数 22
EN

Stack Overflow用户

发布于 2013-11-24 11:27:53

目前,我发现了一个仅使用原始查询的解决方案。

user.friends.all()的简化查询为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SELECT "users_user"."id", "users_user"."name", FROM "users_user" INNER JOIN "users_membership" ON ("users_user"."id" = "users_membership"."to_user_id") WHERE "users_membership"."from_user_id" = 10;

正如我们所看到的,users_membership表已经被连接。因此,我复制了这个查询并只添加了一个"users_membership"."status"字段。然后,我在我的模型friends_with_status中创建了一个方法,并将新的SQL query插入到raw查询集方法中。我的用户模型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class User(models.Model):
    name = models.CharField(max_length=50)
    friends = models.ManyToManyField('self', through='Membership',
        blank=True, null=True, symmetrical=False)


    def friends_with_status(self):
        return User.objects.raw('SELECT "users_membership"."status", "users_user"."id", "users_user"."name", FROM "users_user" INNER JOIN "users_membership" ON ("users_user"."id" = "users_membership"."to_user_id") WHERE "users_membership"."from_user_id" = %s;', [self.pk])

现在,我使用这个:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> user = User.objects.get(name="John")
>>> friends = user.friends_with_status()
>>> friends[0].status
'accepted'
>>> friends[1].status
'thinking'

附注:

当然,这包括原始查询的所有缺点:不可能对它应用任何进一步的queryset方法,即这将不起作用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> friends = user.friends_with_status().filter()
>>> friends = user.friends_with_status().exclude()

诸若此类。此外,如果我修改模型字段,我还必须修改原始查询。

但至少,这种方法在一个查询中就满足了我的需求。

我认为,编写一些注释方法会很有用,比如CountAvg,它们允许从连接表中附加字段。如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> from todo_my_annotations import JoinedField
>>> user = User.objects.get(name="John")
>>> friends = user.friends.annotate(status=JoinedField('member_from__status'))
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20139372

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文