Adding Django models constraints?

Viewed 447

I have the model Period as below:

class Period(models.Model):
      number = models.PositiveIntegerField(primary_key=True)
      start_time = models.TimeField()
      end_time = models.TimeField()

How can I add constraints to make Period.start_time < Period.end_time and Period models have the order like this?

Period 1: 7:00-7:50; Period 2: 7:50-8:40; ...

The field number express the order of the period instance. My approach is adding methods to check if it is valid . However, I want to know that there is any database way to do this.

2 Answers

You can do it in several methods

Method -1: Override the save() method of Period model as

from django.core.exceptions import ValidationError


class Period(models.Model):
    number = models.PositiveIntegerField(primary_key=True)
    start_time = models.TimeField()
    end_time = models.TimeField()

    def save(self, *args, **kwargs):
        if self.end_time < self.start_time:
            raise ValidationError("some message")
        super().save(*args, **kwargs)



Method -2: Override the clean() method of model

from django.core.exceptions import ValidationError


class Period(models.Model):
    number = models.PositiveIntegerField(primary_key=True)
    start_time = models.TimeField()
    end_time = models.TimeField()

    def clean(self):
        super().clean()  # calling default cleaning
        if self.end_time < self.start_time:
            raise ValidationError("some message")


Method - 3: Override the full_clean() method

from django.core.exceptions import ValidationError


class Period(models.Model):
    number = models.PositiveIntegerField(primary_key=True)
    start_time = models.TimeField()
    end_time = models.TimeField()

    def full_clean(self, exclude=None, validate_unique=True):
        super().full_clean(exclude=None, validate_unique=True)  # calling default full_clean
        if self.end_time < self.start_time:
            raise ValidationError("some message")



Reference
1. Model.clean() - Django doc
2. Model.save() - Django doc
3. Model.full_clean() - Django doc

Instead of having a "start" and "end" you could have a "start" and "duration" using the DurationField, then the "end" property is calculated from that.

class Period(models.Model):
      number = models.PositiveIntegerField(primary_key=True)
      start_time = models.TimeField()
      duration = models.DurationField()

      @property
      def end_time(self):
          return self.start_time + self.duration

You should also be able to use the end time in queries by adding it with the annotate method on a queryset, i.e.

query = Period.objects.all().annotate(end_time=F('start_time')+F('duration')) 
Related