2011-02-24 20 views
5

Supponiamo che io ho tre modelli Django:modello di Django get_or_create con il campo ManyToMany

class Section(models.Model): 
    name = models.CharField() 

class Size(models.Model): 
    section = models.ForeignKey(Section) 
    size = models.IntegerField() 

class Obj(models.Model): 
    name = models.CharField() 
    sizes = models.ManyToManyField(Size) 

Vorrei importare una grande quantità di dati Obj dove molti dei campi formati saranno identici. Tuttavia, dal momento che Obj ha un campo ManyToMany, non posso testare per l'esistenza come farei normalmente. Vorrei essere in grado di fare qualcosa del genere:

try: 
    x = Obj(name='foo') 
    x.sizes.add(sizemodel1) # these can be looked up with get_or_create 
    ... 
    x.sizes.add(sizemodelN) # these can be looked up with get_or_create 
    # Now test whether x already exists, so I don't add a duplicate 
    try: 
     Obj.objects.get(x) 
    except Obj.DoesNotExist: 
     x.save() 

Tuttavia, io non sono a conoscenza di un modo per ottenere un oggetto in questo modo, devi passare solo nei parametri di parole chiave, che non funzionano per ManyToManyFields.

C'è qualche buon modo per farlo? L'unica idea che ho avuto è quello di costruire un insieme di oggetti Q passare per ottenere:

myq = myq & Q(sizes__id=sizemodelN.id) 

Ma io non sono sicuro che questo sarà anche il lavoro ...

risposta

0

Il tuo esempio non ha molto senso, perché si puo' t aggiungere le relazioni m2m prima che venga salvato uno x, ma illustra ciò che si sta cercando di fare abbastanza bene. Avete un elenco di oggetti Size creati tramite get_or_create() e volete creare un Obj se non esiste una relazione di dimensioni obj duplicate?

Sfortunatamente, questo non è possibile molto facilmente. Il concatenamento Q(id=F) & Q(id=O) & Q(id=O) non funziona per m2m.

Si potrebbe certamente utilizzare Obj.objects.filter(size__in=Sizes) ma ciò significa che si otterrà una corrispondenza per un Obj con 1 size in un elenco enorme di dimensioni.

Check out this post for an __in exact question, risponde Malcolm, quindi mi fido un po '.

Ho scritto un po 'di python per divertimento che potrebbe occuparsi di questo.
Questa è una importazione una tantum, giusto?

def has_exact_m2m_match(match_list): 
    """ 
    Get exact Obj m2m match 
    """ 
    if isinstance(match_list, QuerySet): 
     match_list = [x.id for x in match_list] 

    results = {} 
    match = set(match_list) 
    for obj, size in \ 
     Obj.sizes.through.objects.filter(size__in=match).values_list('obj', 'size'): 
     # note: we are accessing the auto generated through model for the sizes m2m 
     try: 
      results[obj].append(size) 
     except KeyError: 
      results[obj] = [size] 

    return bool(filter(lambda x: set(x) == match, results.values())) 
    # filter any specific objects that have the exact same size IDs 
    # if there is a match, it means an Obj exists with exactly 
    # the sizes you provided to the function, no more. 


sizes = [size1, size2, size3, sizeN...] 
if has_exact_m2m_match(sizes): 
    x = Obj.objects.create(name=foo) # saves so you can use x.sizes.add 
    x.sizes.add(sizes) 
+0

Alla fine ho regolato un po 'il mio modello per ridurre il problema, anche se il problema fondamentale di abbinare molti a molti modelli esiste ancora. Ho finito per importare i dati come record separati (duplicati). Penso che quello che ho ottenuto dalle tue risposte e Justin è che non c'è una soluzione semplice a questo. Questo mi porta a credere che questa azione sia rara e quindi mi chiedo se non sia possibile progettare i modelli in modo diverso per evitare del tutto questo problema. – djs

1

Utilizzare un modello e poi attraverso .get() contro quello.

http://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships

Una volta che hai un bel passaggio modello, è possibile .get() o .filter() o .exists() per determinare l'esistenza di un oggetto che si potrebbero altrimenti voler creare. Nota che .get() è realmente inteso per le colonne in cui il DB viene applicato univoco - potresti avere prestazioni migliori con .exists() per i tuoi scopi.

Se questo è troppo radicale o scomodo una soluzione, si può anche solo afferrare il ManyRelatedManager e iterazioni per determinare se l'oggetto esiste:

object_sizes = obj.sizes.all() 
exists = object_sizes.filter(id__in = some_bunch_of_size_object_ids_you_are_curious_about).exists() 
if not exists: 
    (your creation code here) 
+0

Stavo guardando questo, ma non sono sicuro di seguirlo. In questo esempio creerei un modello 'ObjSize' con chiavi esterne a' Obj' e 'Size'? Non sono ancora bloccato con lo stesso problema? – djs

+1

Bene, ora è possibile eseguire ObjSize.objects.get (obj = some_obj, size = sizemodel1) e così via. – jMyles

+0

Mi viene fornito un Obj che contiene una dimensione che desidero, ma ho bisogno dell'Obj che contiene l'insieme di tutti gli oggetti Size specificati. – djs

Problemi correlati