通过模型订购django多对多

时间:2016-09-12 17:16:50

标签: django django-queryset

考虑到Django的example of using a "through" model指定多对多关系的额外数据,只将Membership模型更改为

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    order = models.PositiveIntegerField()

    class Meta:
         ordering = ('order', )

这样存储的额外数据就是一个订单参数,用于指定PersonGroup的顺序。

我的目标是获取给定Group的查询集,该查询集包含由Person排序的组中的所有order个实例,其次是剩余的Person个实例(不是在name订购的小组中。

我可以使用两个查询集来完成此操作,例如

# Get all beatles members ordered by the through model
qs1 = beatles.members.all().order_by('membership__order')
# Get the remaining Persons (those not in the beatles) ordered by name
qs2 = Person.objects.exclude(id__in=qs1).order_by('name')

这些查询集似乎包含预期的Person实例。但是,我需要两者的组合查询集,而不仅仅是列表或一些itertools“链”对象,因为我想提供它来初始化需要查询集实例的表单字段。

我试过

result_qs = qs1 | qs2

但是我发现result_qs可以包含重复项(即使我认为联合应该删除重复项?为什么有重复项?有时甚至可能是qs1和qs2不重叠的情况!!)。

如果我这样做

result_qs = (qs1 | qs2).distinct()
它看起来似乎有效,但如果我正在做的事情总能发挥作用,我很怀疑,并且是实现这一目标的好方法。

注意,执行Person.objects.all().order_by('membership__order')会导致Person实例所属的每个Person有大量重复Group个实例。

1 个答案:

答案 0 :(得分:0)

从评论中更新:

正如我在问题的第一个评论中所指出的那样,将两个查询混合在一起并对其进行部分排序就像要求一个"两个分层的"以下查询的order_by属性:

Membership.objects.all()

我不确定目前是否支持此功能。

这是我们在名为Beatles的虚构应用程序中的模型:

甲壳虫/ models.py:

from django.db import models

# Create your models here.

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
    order = models.PositiveIntegerField()

    def __str__(self):
        return '{0}: {1} (order {2})'.format(self.group, self.person, self.order)

    class Meta:
        ordering = ('order', )
  

我想要的是所有订购的Person对象的集合   在集团"披头士乐队"按他们的订单排序"订购"字段。

让我们准备一个会员资格变量:

$manage.py shell

>>>from beatles.models import Membership
>>>memberships = Membership.objects.all().select_related('person', 'group')

现在我们将根据需要操纵成员资格变量。让我们找到所有披头士乐队成员作为模型class Meta要求订购:

>>>beatles_membership_results = memberships.filter(group__name='Beatles')
>>>beatles_membership_results
<QuerySet [<Membership: Beatles: Ringo (order 1)>, <Membership: Beatles: Paul (order 2)>]>
  

,默认情况下订购那些不在披头士乐队中的人   (名称/编号)。

在这里,我获得所有其他成员,并按组ID订购。在结果中你会发现乔治两次,因为他属于两个不同的群体。

>>>general_membership_results = memberships.exclude(group__name='Beatles').order_by('group__id')
>>>general_membership_results
<QuerySet [<Membership: Daring Flowers: George (order 3)>, <Membership: Misty November: Lulu (order 4)>, <Membership: Daring Flowers: Terrenton (order 5)>, <Membership: Misty November: George (order 6)>]>

您可以按名称订购:

>>>general_membership_results = memberships.exclude(group__name='Beatles').order_by('person__name')
>>>general_membership_results
 <QuerySet [<Membership: Daring Flowers: George (order 3)>, <Membership: Misty November: George (order 6)>, <Membership: Misty November: Lulu (order 4)>, <Membership: Daring Flowers: Terrenton (order 5)>]>