2013-05-24 13 views
6

ho i seguenti modelli:Django molti-a-molti di controllo inserimento relazione

class Item(models.Model): 
    # fields 
    # ... 

class Collection(models.Model): 
    items = models.ManyToManyField(Item, related_name="collections") 
    # other fields 
    # ... 

Ora voglio due cose:

  1. Voglio controllare se un Item può essere aggiunto ad un Collection .
  2. Desidero che lo Collection aggiorni alcuni dei suoi campi se è stato aggiunto o rimosso uno Item.

Per il secondo problema, so che esiste lo django.db.models.signals.m2m_changed che posso utilizzare per agganciare le modifiche della relazione. È permesso/ok cambiare il Collection all'interno del richiamo del segnale? Posso usare il segnale anche per "interrompere" l'inserimento per il problema 1?

+0

Per il rilascio 1, probabilmente si dovrebbe utilizzare ciclo di pulizia del form per convalidare i dati (che facilita la messaggistica di validazione), che viene poi inviano al [save_m2m] (https: //docs.djangoproject.it/it/dev/topics/forms/modelforms/# the-save-method) –

+1

@Hedde: preferirei una soluzione vicina ai modelli, perché i miei dati probabilmente non verranno modificati da un modulo. (Più probabile attraverso gli strumenti CLI e un'API esposta). – Constantinius

+0

È possibile sovrascrivere i metodi di salvataggio del modello, almeno per parte della logica, ma se si sta implementando un'API sembra che tale logica appartenga al livello di autorizzazione dell'API. Tastypie è una grande API ricca che funziona bene con Django. –

risposta

8

Penso che il modo migliore per affrontare entrambi i vostri comportamenti desiderati non è con i segnali, ma piuttosto con un override save() e delete() metodo sul tavolo through quale si desidera definire in modo esplicito utilizzando l'argomento through vedi: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ManyToManyField.through. e questo: https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods

Qualcosa di simile a questo:

# -*- coding: utf-8 -*- 

from django.db import models 


class Item(models.Model): 
    # fields 
    # ... 

class Collection(models.Model): 
    items = models.ManyToManyField(Item, related_name="collections", through="CollectionItem") 
    # other fields 
    # ... 

class CollectionItem(models.Model): 
    collection = models.ForeignKey(Collection) 
    item = models.ForeignKey(Item) 

    def save(self, *args, **kwargs): 
     # Only allow this relationship to be created on some_condition 
     # Part 1 of your question. 
     if some_condition: 
      super(CollectionItem, self).save(*args, **kwargs) 

      # Update some fields on Collection when this 
      # relationship is created 
      # Part 2 of your question (1/2) 
      self.Collection.updateSomeFields() 

    def delete(self, *args, **kwargs): 
     collection = self.collection 
     super(CollectionItem, self).delete(*args, **kwargs) 

     # Update some fields on Collection when this relationship 
     # is destroyed. 
     # Part 2 of your question (2/2) 
     collection.updateSomeFields() 

Per inciso, vi accorgerete che l'aggiunta di un rapporto sarà causare un segnale risparmiare sulle questo attraverso il modello.

E, per quanto riguarda i segnali, una volta che il tavolo passante è in posizione, si sarà in grado di ascoltare i segnali pre_save e/o post_save, ma nessuno di questi vi consentirà di porre direttamente il veto alla creazione della relazione.

Se uno o entrambi i modelli sono forniti da un terzo partito e davvero non si può creare la tabella attraverso, allora sì, il percorso del segnale può essere l'unico modo per andare.

https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed

In questo caso, si poteva ascoltare per l'evento m2m_changed e innescare aggiornamenti tuoi oggetti da collezione (parte 2 della tua domanda) e retroattivamente eliminare relazioni impropriamente creati (parte 1 della tua domanda). Tuttavia, poiché quest'ultimo è un po 'fugly kludgy, mi piacerebbe restare con l'esplicita attraverso tavolo se potete.

+1

Mi piace molto questo approccio. Non ho mai pensato di usare il modello 'trhough' come il posto in cui gestirlo, ma ha senso. L'unico inconveniente che vedo è che ora non posso usare il metodo 'add' di' ManyToManyField', ma ho bisogno di creare il 'CollectionItem' stesso. Questo non è un problema, solo qualcosa su cui abituarsi. Grazie per la tua risposta. – Constantinius

+1

anche questa è una buona alternativa e offre una maggiore flessibilità – Alp

+1

Ciò è particolarmente utile, visto che il segnale m2m_changed non viene attivato per le modifiche apportate nell'amministratore [https://code.djangoproject.com/ticket/16073] – trubliphone

3
  1. Il segnale pre_save viene chiamato prima di salvare un'istanza. Ma non sei in grado di interrompere l'operazione di salvataggio da lì. Una soluzione migliore sarebbe quella di aggiungere un nuovo metodo per il vostro modello Collection, che è responsabile per la verifica se un Item può essere aggiunto:

    class Collection(models.Model): 
        items = models.ManyToManyField(Item, related_name="collections") 
    
        ... 
    
        def add_item(self, item): 
         if check_if_item_can_be_added(item): 
          items.add(item) 
          self.save() 
    
    def check_if_item_can_be_added(self, item): 
        # do your checks here 
    
  2. Quando si aggiunge un esempio per un campo m2m, il metodo di salvataggio non ottiene chiamato. Hai ragione, il segnale m2m_changed è la strada da percorrere. Puoi tranquillamente aggiornare l'istanza di raccolta lì.

Problemi correlati