This is my models to store availability of particular time when a new booking there
class TimeSlot(models.Model):
day = models.ForeignKey(
Day,
on_delete=models.CASCADE,
related_name="time"
)
booking = models.ForeignKey(
Booking,
on_delete=models.CASCADE,
related_name="time"
)
start_hour = models.TimeField()
end_hour = models.TimeField()
class Meta:
unique_together = [('end_hour', 'start_hour',)]
def clean(self):
pass
Currently it's allowing booking even those are considered as duplicate in terms of end_hour and start_hour. I want to prevent the slot, so that no new booking shouln't placed between a range that already booked.
Can anyone know how to do it with the range?
I assume the problem is that start_hour
and end_hour
that fall within an already existing time range are allowed to be added. Of course the unique_together
constraint cannot handle this as it only deals with uniqueness not uniqueness in a range. Instead you can override your models clean
method and perform this validation there:
from django.db.models import Q
from django.core.exceptions import ValidationError
class TimeSlot(models.Model):
day = models.ForeignKey(
Day,
on_delete=models.CASCADE,
related_name="time"
)
booking = models.ForeignKey(
Booking,
on_delete=models.CASCADE,
related_name="time"
)
start_hour = models.TimeField()
end_hour = models.TimeField()
class Meta:
unique_together = [('end_hour', 'start_hour',)]
def clean(self):
start_hour_in_range = Q(start_hour__lte=self.start_hour, end_hour__gte=self.start_hour)
end_hour_in_range = Q(start_hour__lte=self.end_hour, end_hour__gte=self.end_hour)
# Queryset that finds all clashing timeslots with the same day
queryset = self._meta.default_manager.filter(start_hour_in_range | end_hour_in_range, day=self.day)
if self.pk:
queryset = queryset.exclude(pk=self.pk) # Exclude this object if it is already saved to the database
if queryset.exists():
raise ValidationError('An existing timeslot clashes with the given one!')
Next if you are using a ModelForm
this method would be called automatically or if you are not you can call instance.full_clean()
which will call this method and all other cleaning methods on the model (clean_fields
and validate_unique
).