2009-09-13 16 views
5

Ho due classi del modello Django:aggiungendo lo stesso oggetto due volte per un 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 

Quello che voglio fare è il seguente:

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 

sto indovinando che aggiungono() utilizza un impostare semantico, ma come posso aggirare questo? Ho provato a controllare i gestori personalizzati, ma non sono sicuro se questo sia il modo giusto (sembra complicato) ...

Grazie in anticipo!

risposta

3

penso che ciò che si vuole è quello di utilizzare un modello di intermediazione per formare la relazione M2M utilizzando l'argomento parola chiave through in ManyToManyField. Un po 'come la prima risposta sopra, ma più "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) 

Quando si utilizza la parola chiave through, i soliti metodi di manipolazione M2M non sono più disponibili (questo significa add, create, remove, o cessione con = operatore). Invece è necessario creare il modello intermediario stesso, in questo modo:

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

Tuttavia, sarà ancora in grado di utilizzare le normali operazioni di interrogazione sul modello che contiene il ManyToManyField. In altre parole il seguente continua a funzionare:

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

Ma forse un esempio migliore è qualcosa di simile:

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

Finché i campi FK sul modello dell'intermediario non sono designati come unique si vuole essere in grado di creare più istanze con gli stessi FK. Puoi anche allegare ulteriori informazioni sulla relazione aggiungendo altri campi che ti piace a C.

I modelli intermedi sono documentati here.

4

Django utilizza un DB relazionale per la sua memoria sottostante, e che intrinsecamente ha "semantica fissa": nessun modo per aggirare QUESTO. Quindi, se vuoi un "multi-set" di qualcosa, devi rappresentarlo con un campo numerico che conta quante volte ogni elemento "si verifica". ManyToManyField non lo fa - quindi, invece, avrai bisogno di una sottoclasse di Model separata che indichi esplicitamente A e B che si relazionano, AND ha una proprietà intera per "contare quante volte".

+1

Penso che non sia del tutto corretto. La relazione da A a B è realizzata tramite una tabella delle relazioni composta da tre colonne: id, a_id e b_id. id è la chiave primaria di quella tabella, quindi sarebbe OK avere tre righe in quella tabella dove due hanno la stessa voce rispettivamente per a_id e b_id. E questo è quello che voglio, ma non riesco a capire come dire a Django che ... – qollin

+0

@qolin, la risposta di sos-skil ti offre un modo per creare un tale tavolo (non attraverso molti ovviamente) - la mia idea è solo di aggiungere un campo count a quel tavolo piuttosto che fare affidamento su duplicati (è solo un modo più compatto implementare i multisets aka bags). –

+1

@qollin: non sono sicuro che si possa fare affidamento sul fatto che ci sia sempre un id nella tabella delle relazioni, che potrebbe essere un dettaglio di implementazione. E puoi costruire un numero molti-a-molti usando una tabella "attraverso" che può avere colonne aggiuntive, come il campo di conteggio suggerito da Alex. –

0

Un modo per farlo:

class A(models.Model): 
    ... 

class B(models.Model): 
    ... 

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

Poi:

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

Quindi, dobbiamo semplicemente:

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

Lo so, non esattamente quello che stai cercando, ma un modo per fare qualcosa di simile: / –

Problemi correlati