Получение первого элемента в отношении «многие ко многим» в Django - PullRequest
6 голосов
/ 13 февраля 2012

У меня есть две модели в 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правильная последовательность выполнения.

Если я пытаюсь выбрать главного члена группы через Group.objects.get(id=1).members[0], тогда Джанго говорит, что менеджер не индексируется.

Если я пытаюсь это Group.objects.get(id=1).members.all().order_by('id')[0]Я получаю участника с самым низким идентификатором в таблице Person.

Как я могу решить эту проблему?

Спасибо

Ответы [ 4 ]

5 голосов
/ 13 февраля 2012

Джанго не обращает внимания на порядок звонков на 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)

Затем добавьте "Том", как:

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

И, наконец, получить "Том" через:

Group.objects.get(id=1).members.filter(is_main_user=True)[0]
3 голосов
/ 13 февраля 2012

По умолчанию в реляционной базе данных нет понятия упорядочения.Там нет такого понятия, как «первый».Вам необходимо добавить явное поле «порядок», в котором хранится порядок и порядок по нему.

1 голос
/ 13 февраля 2012

Вы были довольно близко:

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

(Но, как говорит Алекс Гейнор в своем ответе, для предсказуемого поведения вам нужно поле для сортировки)

0 голосов
/ 13 февраля 2012

, чтобы надежно вести учет того, кто является «главным» человеком в группе, вы можете захотеть взглянуть на использование таблицы от до , чтобы сохранить эти метаданные

...