2011-09-09 11 views
28

Tengo un modelo de Django con un rango de fecha de inicio y finalización. Quiero aplicar la validación para que no haya dos registros con rangos de fechas superpuestos. ¿Cuál es la forma más sencilla de implementar esto para que no tenga que repetirme escribiendo esta lógica?Cómo agregar una validación de modelo de Django personalizada

p. Ej. No quiero volver a implementar esta lógica en un formulario y un ModelForm y un formulario de administración y del modelo anulado save().

Por lo que sé, Django no hace que sea fácil hacer cumplir globalmente este tipo de criterios.

Google no ha sido muy útil, ya que la "validación del modelo" generalmente se refiere a la validación de campos de modelos específicos, y no a los contenidos del modelo completo, o las relaciones entre los campos.

Respuesta

27

El patrón básico que he encontrado útil es poner toda mi validación personalizada en clean() y luego simplemente llamar full_clean() (que llama a clean() y algunos otros métodos) desde el interior save(), por ejemplo:

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) 

Este no se realiza de manera predeterminada, como se explica en here, porque interfiere con ciertas funciones, pero eso no es un problema para mi aplicación.

8

creo que puedes usar esto: https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects

Simplemente defina el método clean() en su modelo como este: (ejemplo desde el enlace 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

Esto está cerca. También tuve que anular el guardado de mi modelo() y llamar a clean() desde allí. – Cerin

+1

¿Pero para qué? AdminSite (ModelForm) llama a clean() automáticamente. Pero llamar a clean() desde el método save() podría producir ValidationError en un momento inesperado y no se considerará como se esperaba. – alTus

+6

No todo se llama limpio. Esto debe validarse independientemente de dónde se guarde. Una página de sitio rota es preferible a datos corruptos. – Cerin

16

que sería reemplazar el método validate_unique en el modelo. Para asegurarse de que se ignora el objeto actual cuando se valide, puede utilizar el siguiente:

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 llamará automáticamente por usted a través de un full_clean(), que se puede utilizar de forma manual también.

PPR tiene una buena discusión de un simple, correcto range overlap condition.

+0

En django 1.3.1 me encuentro con un problema que plantea ValidationError tal como lo describes.Pude solucionarlo pasando un {field: (error_msg,)} dict en lugar de una cadena error_msg al generar la excepción. ' – adam

+5

de django.core.exceptions importar ValidationError, NON_FIELD_ERRORS' ' aumentar ValidationError ({NON_FIELD_ERRORS: ('rango de fechas superposición',)}) ' – adam

+2

¿Hay algún interés en ampliar' validate_unique' lugar de definir sólo 'clean' ? ¿Solo una cuestión de organizar el código? – lajarre

Cuestiones relacionadas