2010-02-17 26 views
20

Si bien las subclases db.models.Model, a veces es esencial agregar controles/restricciones adicionales.Agregando restricciones adicionales a los campos en Django

E.g. Tengo un modelo Event con start_date y end_date.

Quiero agregar la validación en los campos o el modelo para que end_date > start_date.

¿Cuántas formas posibles de hacer esto?

Al menos sé que esto se puede hacer fuera de models.Model dentro de la validación ModelForm.

¿Pero cómo conectarse a los campos y al models.Model?

+0

lo que usted sugiere como restricción no se puede definir como una declaración SQL, por lo que solo se esperan cambios de dicha verificación en forma de administrador. Puede hacerlo anulando la función de guardado de forma administrativa para esa clase. La respuesta de umnik700 muestra cómo puedes hacerlo. – Numenor

+3

En realidad, hay una restricción "CHECK" en SQL. PostgreSQL admite esto: http://www.postgresql.org/docs/8.1/static/ddl-constraints.html Sin embargo, MySQL no es compatible con esto: la cláusula CHECK es analizada pero ignorada por todos los motores de almacenamiento (vea http : //dev.mysql.com/doc/refman/5.5/en/create-table.html) –

+0

@ slack3r: Gracias. Sé que hay un cheque, pero solo lo quiero en un nivel superior, en el nivel de declaración de metadatos de Django. Evito los cambios de esquema. – Viet

Respuesta

41

No voy a poner restricciones como éstas en el método de guardar, ya es demasiado tarde. Plantear una excepción allí, no ayuda al usuario que ingresó los datos de manera incorrecta, porque terminará como 500 y el usuario no recibirá el formulario con errores, etc.

Realmente debería verificar para esto en el método de limpieza Forms/ModelForms y generar un ValidationError, entonces form.is_valid() devuelve falso y puede enviar los errores en el formulario de vuelta al usuario para su corrección.

También tenga en cuenta que desde la versión 1.2, Django ha tenido Model Validation.

Se vería algo como esto:

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

Gracias por la segunda/mejor respuesta (?). Es justo lo que necesitaba. – KobeJohn

+0

Esto, por supuesto, solo funciona si está usando 'ModelForm' o llama' is_valid' manualmente. De lo contrario, si solo llama a 'save', [does * nothing *] (https://stackoverflow.com/questions/4441539/why-doesnt-djangos-model-save-call-full-clean). – mlissner

10

Hágalo dentro de su Guardar método de su modelo:

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

en django 1.2 o posterior recuerde agregar * args, ** kwargs tanto a la definición del método save() modificado como a cualquier lugar al que se llame – michuk

+2

En mi humilde opinión, tener un cheque en el modelo de guardado es excelente, pero insuficiente. Siempre debe imponer restricciones al nivel más bajo posible: en este caso, le gustaría que la base de datos evite almacenar cualquier valor que rompa la restricción. –

9

Como @stefanw dice, es mejor experiencia de usuario para comprobar en el método de limpieza de la forma.

Esto es suficiente si está seguro de que no hay, y nunca existirá, otra forma de cambiar el valor. Pero ya que rara vez se puede estar seguro de que, si la consistencia de base de datos es importante, se puede añadir otro cheque (además de la forma), uno de:

  • La manera más fácil y base de datos independiente es en el modelo de guardar método como @ umnik700 dijo. Tenga en cuenta que esto aún no impide que otros usuarios de la base de datos (otra aplicación o la interfaz de administración) creen un estado incoherente.
  • Para estar 'completamente' seguro de que la base de datos es coherente, puede agregar una restricción de nivel de base de datos. P.ej. puede crear una migración con RunSQL y SQL, algo similar (no probado):

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

    (No se ha probado). Esto puede depender de la base de datos, lo cual es una desventaja, por supuesto.

En su ejemplo, no es probablemente vale la pena (tiempos de inicio incorrecto/final sólo se ven un poco raro, pero sólo afectan el único evento inconsistente), y que no quieren cambios en el esquema manuales. Pero es útil en casos donde la consistencia es crítica.

EDIT: También puede simplemente guardar la hora de inicio y la duración, en lugar de las horas de inicio y fin.

+0

¡Esta es la respuesta correcta! – emanuelcds

+0

Tenga en cuenta que las restricciones CHECK directamente [no funcionan en MySQL] (https://stackoverflow.com/questions/2115497/check-constraint-in-mysql-is-not-working) – mlissner

Cuestiones relacionadas