【问题标题】:Ordering django many-to-many with through model通过模型订购 django 多对多
【发布时间】:2016-09-12 17:16:49
【问题描述】:

考虑到 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 的查询集,其中首先包含由order 排序的组中的所有Person 实例,然后是由@987654330 排序的剩余Person 实例(不在组中) @。

我可以用两个查询集来做到这一点,比如

# 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 实例。但是,我需要一个 combined 两者的查询集,而不仅仅是一个列表或一些 itertools “链”对象,因为我想提供它来初始化一个需要查询集实例的表单字段。

我试过了

result_qs = qs1 | qs2

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

如果我这样做

result_qs = (qs1 | qs2).distinct()

它似乎确实有效,但我怀疑我正在做的事情是否总是有效,并且是实现这一目标的好方法。

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

【问题讨论】:

  • 混合上述组不等于只搜索所有人员? “属于披头士的人”和“不属于披头士的人”?
  • @raratiru 你能给我举个例子来说明你的意思吗?我认为第一个qs1 的顺序会有问题吗?
  • 我想要的是所有 Person 对象的集合,这样“beatles”组中的对象按其“order”字段排序,而不在 beatles 中的对象按“order”字段排序默认(名称/ID)。

标签: django django-queryset


【解决方案1】:

来自 cmets 的更新:

正如我在问题下的第一条评论中所指出的,将两个查询混合在一起并对其进行部分排序就像为以下查询请求“两层”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 对他们进行排序。在结果中,您会两次找到 George,因为他属于两个不同的组。

>>>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)>]>   

【讨论】:

  • 我不是在寻找两个单一的查询集。如果你检查我的 OP,我已经自己实现了两个单人(第一个是按顺序排序的披头士成员,第二个是所有不在披头士中的人,按名称排序)。我需要的是一个单一的查询集,它结合了这两个东西,以及它们的给定顺序。
猜你喜欢
  • 2010-10-30
  • 2022-01-21
  • 2015-11-05
  • 2014-12-08
  • 2014-05-27
  • 2017-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多