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

时间:2012-02-13 15:22:02

标签: python django django-orm

我在Django中有两个模型通过ManyToMany关系链接在一起,如下所示:

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 个答案:

答案 0 :(得分:5)

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]

答案 1 :(得分:3)

默认情况下,关系数据库没有任何排序概念。没有“第一”这样的东西。您需要添加一个显式的“订单”字段来保存订单,并按顺序排序。

答案 2 :(得分:1)

你非常接近:

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

(但正如Alex Gaynor在他的回答中所说,对于可预测的行为,你需要一个合适的字段来排序)

答案 3 :(得分:0)

为了可靠地记录谁是组中的“主要”人,您可能希望看一下使用through表来保存此元数据