在Django中获取多对多关系的第一个项

10 投票
4 回答
14367 浏览
提问于 2025-04-17 12:58

我在Django中有两个模型,它们通过多对多关系连接在一起,像这样:

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

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

我需要找到这个组里的主要成员,也就是组里的第一个人。我该怎么获取第一个人呢?

这是我添加成员的方式:

grp = Group.objects.create(name="Group 1")
grp.save()
prs = Person.objects.create(name="Tom")
grp.members.add(prs) #This is the main person of the group.
prs = Person.objects.create(name="Dick")
grp.members.add(prs)
prs = Person.objects.create(name="Harry")
grp.members.add(prs)

我觉得我不需要额外的列,因为表 group_members 的ID是一个递增的序列,对吧。

如果我尝试通过 Group.objects.get(id=1).members[0] 来获取组的主要成员,Django会说管理器不能被索引。

如果我尝试这个 Group.objects.get(id=1).members.all().order_by('id')[0],我会得到在 Person 表中ID最小的成员。

我该怎么解决这个问题呢?

谢谢

4 个回答

2

关系型数据库默认是没有排序的概念的,也就是说,它们并不知道哪个是“第一个”。如果你想要有顺序,就需要自己添加一个“顺序”字段来记录这个顺序,然后根据这个字段来进行排序。

3

你已经很接近了:

Group.objects.get(id=1).members.all()[0]

(不过正如Alex Gaynor在他的回答中提到的,要想有可预测的行为,你需要一个合适的字段来进行排序)

7

Django在处理add的调用顺序时并不在意。Django为这种关系隐式创建的表大概是这样的,如果它是一个Django模型:

class GroupMembers(models.Model):
    group = models.ForeignKey(Group)
    person = models.ForeignKey(Person)

显然,这里没有提到“顺序”或者哪个应该先来。默认情况下,它可能会按照你描述的那样返回最小的主键(pk),但这只是因为它没有其他依据。

如果你想要强制规定一个顺序,你需要使用一个中间表。简单来说,就是不让Django自己创建那个隐式模型,而是你自己创建一个。然后,你可以添加一个像order这样的字段来指定顺序:

class GroupMembers(models.Model):
    class Meta:
        ordering = ['order']

    group = models.ForeignKey(Group)
    person = models.ForeignKey(Person)
    order = models.PositiveIntegerField(default=0)

接着,你告诉Django使用这个模型来处理关系:

class GroupMembers(models.Model):
    group = models.ForeignKey(Group)
    person = models.ForeignKey(Person, through='GroupMembers')

现在,当你添加成员时,就不能再使用add了,因为需要额外的信息来完成这个关系。相反,你必须使用中间模型:

prs = Person.objects.create(name="Tom")
GroupMembers.objects.create(person=prs, group=grp, order=1)
prs = Person.objects.create(name="Dick")
GroupMembers.objects.create(person=prs, group=grp, order=2)
prs = Person.objects.create(name="Harry")
GroupMembers.objects.create(person=prs, group=grp, order=3)

然后,只需使用:

Group.objects.get(id=1).members.all()[0]

另外,你也可以简单地添加一个BooleanField来指定哪个是主要用户:

class GroupMembers(models.Model):
    group = models.ForeignKey(Group)
    person = models.ForeignKey(Person)
    is_main_user = models.BooleanField(default=False)

然后,像这样添加“Tom”:

prs = Person.objects.create(name="Tom")
GroupMembers.objects.create(person=prs, group=grp, is_main_user=True)

最后,通过以下方式获取“Tom”:

Group.objects.get(id=1).members.filter(is_main_user=True)[0]

撰写回答