Adding Custom Django Model Validation

前端 未结 4 1916
我寻月下人不归
我寻月下人不归 2020-12-07 14:15

I have a Django model with a start and end date range. I want to enforce validation so that no two records have overlapping date ranges. What\'s the simplest way to implemen

相关标签:
4条回答
  • 2020-12-07 14:23

    I think you should use this: https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects

    Just define clean() method in your model like this: (example from the docs link)

    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 讨论(0)
  • 2020-12-07 14:33

    The basic pattern I've found useful is to put all my custom validation in clean() and then simply call full_clean() (which calls clean() and a few other methods) from inside save(), e.g.:

    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)
    

    This isn't done by default, as explained here, because it interferes with certain features, but those aren't a problem for my application.

    0 讨论(0)
  • 2020-12-07 14:33

    I think this can help you, We can create multiple validators like this use in models.

    from django.core.exceptions import ValidationError
    from django.utils.translation import gettext_lazy as _
    from django.db import models
    
    def validate_even(value):
        if value % 2 != 0:
            raise ValidationError(
                _('%(value)s is not an even number'),
                params={'value': value},
            )
    
    class MyModel(models.Model):
        even_field = models.IntegerField(validators=[validate_even])
    
    0 讨论(0)
  • 2020-12-07 14:48

    I would override the validate_unique method on the model. To make sure you ignore the current object when validating, you can use the following:

    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 will automatically call this for you through a full_clean(), which you can use manually too.

    PPR has a nice discussion of a simple, correct range overlap condition.

    0 讨论(0)
提交回复
热议问题