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:
- same date, starting @
18:00
, ending @21:00
- same date, starting @
19:30
, ending @19:50
- 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.