2010-02-17 16 views
20

Durante la sottoclasse db.models.Model, a volte è essenziale aggiungere controlli/vincoli aggiuntivi.Aggiunta di vincoli aggiuntivi nei campi in Django

E.g. Ho un modello Event con start_date e end_date.

Desidero aggiungere la convalida nei campi o nel modello in modo che end_date > start_date.

Quanti modi possibili per fare ciò?

Almeno so che questo può essere fatto al di fuori del models.Model all'interno della convalida ModelForm.

Ma come allegare ai campi e allo models.Model?

+0

ciò che suggerisci come vincolo non può essere definito come un'istruzione sql, quindi solo le modifiche che ti aspetteresti da tale controllo sono nel modulo di amministrazione. Puoi farlo ignorando la funzione di salvataggio di adminform per quella classe. La risposta di umnik700 mostra come puoi farlo. – Numenor

+3

In realtà, esiste un vincolo "CHECK" in SQL. PostgreSQL supporta questo: http://www.postgresql.org/docs/8.1/static/ddl-constraints.html Tuttavia, MySQL non supporta questo: la clausola CHECK viene analizzata ma ignorata da tutti i motori di archiviazione (vedere http : //dev.mysql.com/doc/refman/5.5/en/create-table.html) –

+0

@ slack3r: Grazie. So che c'è un assegno, ma lo voglio solo a un livello più alto, al livello di dichiarazione dei metadati di Django. Evito i cambiamenti dello schema. – Viet

risposta

41

Non vorrei mettere vincoli come questi nel metodo di salvataggio, è troppo tardi. Alzare un'eccezione lì, non aiuta l'utente che ha inserito i dati nel modo sbagliato, perché finirà come un 500 e l'utente non otterrà il modulo con errori, ecc.

Si dovrebbe davvero controllare per questo nel metodo Forms/ModelForms clean e genera un errore Validation, quindi form.is_valid() restituisce false e puoi inviare gli errori nel modulo all'utente per la correzione.

Si noti inoltre che dalla versione 1.2, Django ha avuto Model Validation.

Sarebbe simile a questa:

class Foo(models.Model): 
    # ... model stuff... 
    def clean(self): 
     if self.start_date > self.end_date: 
      raise ValidationError('Start date is after end date') 
+2

Grazie per la seconda/migliore (?) Risposta. È proprio quello di cui avevo bisogno. – KobeJohn

+0

Questo, ovviamente, funziona solo se si utilizza un 'ModelForm' o si chiama' is_valid' manualmente. Altrimenti, se chiami semplicemente 'save', [fa * nothing *] (https://stackoverflow.com/questions/4441539/why-doesnt-djangos-model-save-call-full-clean). – mlissner

10

farlo all'interno vostra salvataggio metodo del modello:

def save(self, *args, **kwargs): 
    if(self.end_date > self.start_date): 
     super(Foo, self).save(*args, **kwargs) 
    else: 
     raise Exception, "end_date should be greater than start_date" 
+4

in django 1.2 o successivo ricordati di aggiungere * args, ** kwargs sia alla definizione del metodo overriden save() che ovunque sia chiamato – michuk

+2

IMHO, avere un controllo nel modello di salvataggio è ottimo, ma insufficiente. Dovresti sempre imporre restrizioni al livello più basso possibile: in questo caso, vorrai che il database impedisca la memorizzazione di valori che infrangono il vincolo. –

9

Come @stefanw dice, è meglio l'esperienza degli utenti di controllare nel metodo pulito del form.

Questo è sufficiente se si è certi che non ci sia, e non lo sarà mai, un altro modo per modificare il valore. Ma dal momento che raramente si può essere sicuri di questo, se la consistenza del database è importante, è possibile aggiungere un altro controllo (in aggiunta al modulo), uno dei:

  • Il modo più facile e indipendente dal database è nel modello di salvataggio metodo come ha detto @ umnik700. Si noti che ciò non impedisce agli altri utenti del database (un'altra app o all'interfaccia di amministrazione) di creare uno stato incoerente.
  • Per essere "completamente" sicuro che il database sia coerente, è possibile aggiungere un vincolo a livello di database. Per esempio. è possibile creare una migrazione con RunSQL e SQL, qualcosa di simile (non testato):

    migrations.RunSQL('ALTER TABLE app_event ADD CONSTRAINT chronology CHECK (start_date > end_date);') 
    

    (non testato). Questo può dipendere dal database, che ovviamente è uno svantaggio.

Nel tuo esempio, probabilmente non vale la pena (i tempi di avvio errato/fine basta guardare un po 'strano, ma riguardano solo l'unico evento incoerente), e non si vuole modifiche dello schema manuali. Ma è utile nei casi in cui la coerenza è fondamentale.

MODIFICA: È anche possibile salvare solo l'ora di inizio e la durata, anziché le ore di inizio e di fine.

+0

Questa è la risposta giusta! – emanuelcds

+0

Nota che i vincoli CHECK sono diretti [non funzionano in MySQL] (https://stackoverflow.com/questions/2115497/check-constraint-in-mysql-is-not-working) – mlissner

Problemi correlati