How to restrict overlapping datetimes in django model at DB level using CheckConstraint

Viewed 258

I have the following fields in my Model:

class Event(models.Model):
    starts = models.DateTimeField()
    ends = models.DateTimeField()
    

I want to restrict overlapping dates (starts, ends). I have managed to do this in model validation, but now I want this enforced at database level such that an IntegrityError exception is thrown if an insert happens outside the model's save method.

My Validation was as follows:

...
def clean(self):
    if self.starts and self.ends:
        if self.__class__.objects.filter(
            models.Q(ends__gte=self.starts, starts__lt=self.starts) | models.Q(ends__gte=self.ends, starts__lt=self.ends) | models.Q(starts__gt=self.starts, ends__lt=self.ends)
        ).exists():
            raise ValidationError('Event times overlap with existing record!')

This works. Say an event starting 2020 Oct 11 @ 19:00, and ending 2020 Oct 11 @ 20:00, the following values will prompt an overlap:

  1. same date, starting @ 18:00, ending @ 21:00
  2. same date, starting @ 19:30, ending @ 19:50
  3. same date, starting @ 19:30, ending @ 20:50

But there are situations where the model's .clean() method will not be invoked, which may result in invalid data to be inserted.

My question is, how can I enforce a constraint on the model, which will apply on the database itself, like unique_together does.

I have used postgres specific fields like DateRangeField but in this case, their functionality is limited as they can contain empty upper values to mention one.

I have also come accross this question here on S/O which implements the new (from django 2.2) CheckConstraint, which I have tried to implement, but it doesn't work.

0 Answers
Related