2009-09-13 7 views
5

mam dwie klasy modelu Django:dodając ten sam obiekt dwa razy do ManyToManyField

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 

Co chcę zrobić jest następujący:

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 

Jestem zgadywania, że ​​add() używa ustawić semantyczną, ale jak mogę to obejść? Próbowałem zajrzeć do niestandardowych menedżerów, ale nie jestem pewien, czy to właściwa droga (wydaje się skomplikowana) ...

Z góry dziękuję!

Odpowiedz

3

myślę co chcesz jest użycie modelu pośrednika aby utworzyć relację M2M za pomocą argumentu słowa kluczowego through w ManyToManyField. Coś w rodzaju pierwszej odpowiedzi powyżej, ale więcej "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) 

Przy użyciu słowa kluczowego through, zwykłymi metodami manipulacji M2M nie są już dostępne (to znaczy add, create, remove lub przypisanie z = operatora). Zamiast tego należy utworzyć model pośredni sama, jak w przykładzie:

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

Jednak nadal będzie w stanie wykorzystać zwykłe operacje odpytywanie na modelu zawierającym ManyToManyField. Innymi słowy dodaje będą nadal działać:

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

ale może lepszym przykładem jest coś takiego:

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

Dopóki pola FK na modelu pośredniczącej nie są oznaczone jako unique Ci będzie móc tworzyć wiele instancji z tymi samymi FK. Możesz również załączyć dodatkowe informacje na temat relacji, dodając inne pola, które lubisz, aby C.

Modele pośrednie są udokumentowane here.

4

Django używa relacyjnej bazy danych dla swojego bazowego magazynu, a to samo w sobie ma "ustawioną semantykę": nie można tego obejść. Więc jeśli chcesz mieć "zbiór wszystkiego", musisz go reprezentować za pomocą pola liczbowego, które zlicza ile razy każdy element "występuje". ManyToManyField tego nie robi - więc zamiast tego będziesz potrzebować oddzielnej podklasy Model, która wyraźnie wskazuje na A i B, z którymi jest związana, AND ma właściwość całkowitą, aby "policzyć ile razy".

+1

Myślę, że to nie do końca poprawne. Relacja od A do B jest realizowana za pomocą tabeli relacji składającej się z trzech kolumn: id, aid i b_id. id jest kluczem podstawowym tej tabeli, więc byłoby dobrze mieć trzy wiersze w tej tabeli, gdzie dwa mają ten sam wpis odpowiednio dla aid i b_id. I tego właśnie chcę, ale nie mogę wymyślić, jak powiedzieć Django, że ... – qollin

+0

@qolin, odpowiedź sos-skil oferuje sposób na zrobienie takiego stołu (oczywiście nie przez wiele stron) - moim pomysłem jest dodanie pola licznika do tego stołu zamiast polegania na duplikatach (to tylko bardziej zwarty sposób do wdrażania multiseksów, a także toreb). –

+1

@qollin: Nie jestem pewien, czy możesz polegać na tym, że zawsze istnieje identyfikator w tabeli relacji - może to być szczegół implementacji. Możesz zbudować wiele-do-wielu używając tabeli "do", która może zawierać dodatkowe kolumny, takie jak pole liczenia, które zasugerował Alex. –

0

Jednym ze sposobów, aby to zrobić:

class A(models.Model): 
    ... 

class B(models.Model): 
    ... 

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

Następnie:

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

Więc po prostu:

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

Wiem, nie dokładnie to, czego szukasz, ale sposób na coś podobnego: / –

Powiązane problemy