добавление одного и того же объекта дважды в ManyToManyField - PullRequest
7 голосов
/ 13 сентября 2009

У меня есть два класса моделей Django:

class A(models.Model):
     name = models.CharField(max_length = 128)    #irrelevant

class B(models.Model):
     a = models.ManyToManyField(A)
     name = models.CharField(max_length = 128)    #irrelevant

Я хочу сделать следующее:

a1 = A()
a2 = A()
b = B()

b.a.add(a1)
b.a.add(a1)    #I want to have a1 twice
b.a.add(a2)

assert len(b.a.all()) == 3 #this fails; the length of all() is 2

Я предполагаю, что add () использует набор семантики, но как я могу обойти это? Я пытался изучить пользовательские менеджеры, но я не уверен, что это правильный путь (кажется сложным) ...

Заранее спасибо!

Ответы [ 3 ]

4 голосов
/ 15 сентября 2009

Я думаю, что вы хотите использовать посредническую модель для формирования отношения M2M с помощью ключевого аргумента through в ManyToManyField. Вроде как первый ответ выше, но больше "Django-y".

class A(models.Model):
    name = models.CharField(max_length=200)

class B(models.Model):
    a = models.ManyToManyField(A, through='C')
    ...

class C(models.Model):
    a = models.ForeignKey(A)
    b = models.ForeignKey(B)

При использовании ключевого слова through обычные методы манипуляции M2M больше недоступны (это означает add, create, remove или присвоение с оператором =). Вместо этого вы должны создать саму модель-посредник, например:

 >>> C.objects.create(a=a1, b=b)

Однако вы по-прежнему сможете использовать обычные операции запроса для модели, содержащей ManyToManyField. Другими словами, все еще будет работать следующее:

 >>> b.a.filter(a=a1)

Но, может быть, лучшим примером будет что-то вроде этого:

>>> B.objects.filter(a__name='Test')

Пока поля FK в промежуточной модели не обозначены как unique, вы сможете создавать несколько экземпляров с одинаковыми FK. Вы также можете прикрепить дополнительную информацию об отношениях, добавив любые другие поля, которые вам нравятся, к C.

Промежуточные модели задокументированы здесь .

3 голосов
/ 13 сентября 2009

Django использует реляционную БД для своего базового хранилища, и это по сути имеет «установленную семантику»: нет способа обойти это. Поэтому, если вам нужен «множественный набор» чего-либо, вы должны представить его в числовом поле, которое подсчитывает, сколько раз «встречается» каждый элемент. ManyToManyField этого не делает, поэтому вам понадобится отдельный подкласс Model, который явно указывает A и B, к которым он относится, и имеет целочисленное свойство для «подсчета сколько раз».

0 голосов
/ 13 сентября 2009

Один из способов сделать это:

class A(models.Model):
    ...

class B(models.Model):
    ...

class C(models.Model):
    a = models.ForeignKey(A)
    b = models.ForeignKey(B)

Тогда:

>>> a1 = A()
>>> a2 = A()
>>> b = B()
>>> c1 = C(a=a1, b=b)
>>> c2 = C(a=a2, b=b)
>>> c3 = C(a=a1, b=b)

Итак, мы просто имеем:

>>> assert C.objects.filter(b=b).count == 3
>>> for c in C.objects.filter(b=b):
...     # do something with c.a
...