2011-09-09 20 views
28

Ho un modello Django con un intervallo di date di inizio e fine. Voglio applicare la convalida in modo che nessun record abbia intervalli di date sovrapposti. Qual è il modo più semplice per implementare questo in modo che non debba ripetermi a scrivere questa logica?Aggiunta della validazione del modello Django personalizzato

ad es. Non voglio di re-implementare questa logica in un modulo e un ModelForme una forma di amministrazione e del modello ignorato save().

Per quanto ne so, Django non rende facile applicare globalmente questo tipo di criteri.

Googling non è stato molto utile, poiché la "validazione del modello" si riferisce in genere alla convalida di specifici campi del modello e non dell'intero contenuto del modello o delle relazioni tra i campi.

risposta

27

Il modello di base che ho trovato utile è quello di mettere tutta la mia convalida personalizzata in clean() e poi semplicemente chiamare full_clean() (che chiama clean() e pochi altri metodi) dall'interno save(), ad esempio:

class BaseModel(models.Model): 

    def clean(self, *args, **kwargs): 
     # add custom validation here 
     super(BaseModel, self).clean(*args, **kwargs) 

    def save(self, *args, **kwargs): 
     self.full_clean() 
     super(BaseModel, self).save(*args, **kwargs) 

Questo non è fatto in modo predefinito, come spiegato here, perché interferisce con alcune funzionalità, ma quelle non rappresentano un problema per la mia applicazione.

8

Credo che si dovrebbe usare questo: https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects

Basta definire il metodo clean() nel modello come questo: (ad esempio dal collegamento docs)

def clean(self): 
    from django.core.exceptions import ValidationError 
    # Don't allow draft entries to have a pub_date. 
    if self.status == 'draft' and self.pub_date is not None: 
     raise ValidationError('Draft entries may not have a publication date.') 
    # Set the pub_date for published items if it hasn't been set already. 
    if self.status == 'published' and self.pub_date is None: 
     self.pub_date = datetime.datetime.now() 
+0

Questo è vicino. Dovevo anche scavalcare il mio modello save() e chiamare clean() da lì. – Cerin

+1

Ma per cosa? AdminSite (ModelForm) chiama clean() automaticamente. Ma chiamare clean() dal metodo save() potrebbe produrre ValidationError in un momento inaspettato e non verrà risolto come previsto. – alTus

+6

Non tutto chiama pulito. Questo deve essere convalidato indipendentemente da dove è stato salvato. Una pagina del sito spezzata è preferibile ai dati corrotti. – Cerin

16

vorrei ignorare il metodo validate_unique su il modello. Per essere sicuri di ignorare l'oggetto corrente durante la convalida, è possibile utilizzare il seguente:

from django.db.models import Model, DateTimeField 
from django.core.validators import NON_FIELD_ERRORS, ValidationError 

class MyModel(Model): 
    start_date = DateTimeField() 
    end_date = DateTimeField() 

    def validate_unique(self, *args, **kwargs): 
     super(MyModel, self).validate_unique(*args, **kwargs) 

     qs = self.__class__._default_manager.filter(
      start_date__lt=self.end_date, 
      end_date__gt=self.start_date 
     ) 

     if not self._state.adding and self.pk is not None: 
      qs = qs.exclude(pk=self.pk) 

     if qs.exists(): 
      raise ValidationError({ 
       NON_FIELD_ERRORS: ['overlapping date range',], 
      }) 

ModelForm chiamerà automaticamente questo per voi attraverso un full_clean(), che si può usare manualmente troppo.

PPR ha una bella discussione di un semplice, corretto range overlap condition.

+0

su django 1.3.1 Sto incontrando un problema nel generare l'errore ValidationError nel modo in cui lo descrivi.Sono stato in grado di risolverlo passando un {field: (error_msg,)} dict invece di una stringa error_msg quando si genera l'eccezione. – adam

+5

'da django.core.exceptions importare ValidationError, NON_FIELD_ERRORS' ' sollevare ValidationError ({NON_FIELD_ERRORS: ('intervallo di date sovrapposizione',)}) ' – adam

+2

C'è qualche interesse ad estendere' validate_unique' anziché definire soltanto 'Clean' ? Solo una questione di organizzazione del codice? – lajarre

Problemi correlati