需要附加到queryset结果相关的对象字段。
型号:
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")
我可以这样做:
>>> 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'
包含我需要的信息。但与此同时,我还需要一个模型实例。
我想要的是:
>>> 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'字段,其中包含相应的值:
>>> users_with_status[0].member_from__status
u'accepted'
如何实现这一目标?
答案 0 :(得分:12)
>>> from django.db.models import F
>>> u = User.objects.all().annotate(member_from__status=F('member_from__status'))
>>> u[0].member_from__status
'accepted'
使用Django版本1.11&amp;测试2.2
在这里,我想我们对member_to__status
感兴趣而不是member_from__status
(我们希望用户被要求接受友谊,而不是相反)
这是membership
表的内容:
status | from| to
---------+------+-----
accepted | 1| 2
accepted | 2| 3
refused | 3| 1
由于&#34; it's not possible to have a symmetrical, recursive ManyToManyField
&#34;,这就是我检索所有实际朋友的方式:
>>> for u in User.objects.all():
... friends = u.friends.filter(member_to__status='accepted')
... print(str(u.id) + ': ' + ','.join([str(f.id) for f in friends]))
1: 2
2: 3
3:
如果您想在<User 1>
的朋友中拥有<User 2>
,我建议在接受1到2的友谊时添加另一个Membership
我们假设Membership
只能被接受一次,因此添加unique-together选项可能是一个好主意,因为如果我们有多个Membership
具有相同(from_user, to_user)
夫妻,这就是发生的事情:
>>> # Insert several Membership from one to another User
>>> Membership.objects.filter(from_user__id=1, to_user__id=2).count()
3
>>> u = User.objects.annotate(member_from__id=F('member_from__id'))
>>> print('User Membership')
... for user in u:
... 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
答案 1 :(得分:3)
目前我发现只使用原始查询的解决方案。
user.friends.all()
的简化查询是:
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查询插入raw
queryset方法。我的用户模型:
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])
现在,我用这个:
>>> user = User.objects.get(name="John")
>>> friends = user.friends_with_status()
>>> friends[0].status
'accepted'
>>> friends[1].status
'thinking'
当然,这包括原始查询的所有缺点:无法对其应用任何进一步的查询集方法,即这不起作用:
>>> friends = user.friends_with_status().filter()
>>> friends = user.friends_with_status().exclude()
等等。另外,如果我修改模型字段,我也必须修改原始查询。
但至少,这种方法在一次查询中给了我所需要的东西。
我认为,编写一些注释方法会很有用,例如Count
或Avg
,它将允许从连接表中附加字段。
像这样:
>>> from todo_my_annotations import JoinedField
>>> user = User.objects.get(name="John")
>>> friends = user.friends.annotate(status=JoinedField('member_from__status'))