From 7d5fd088553583931345422beee2f8c309ca5bbf Mon Sep 17 00:00:00 2001 From: Adam Goldsmith Date: Mon, 9 Sep 2024 19:15:01 -0400 Subject: [PATCH] reservations: Add generated Reservation.timespan field and use it for filtering --- .../migrations/0003_reservation_timespan.py | 24 +++++++++++++++++++ reservations/models.py | 17 +++++++------ 2 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 reservations/migrations/0003_reservation_timespan.py diff --git a/reservations/migrations/0003_reservation_timespan.py b/reservations/migrations/0003_reservation_timespan.py new file mode 100644 index 0000000..19fcb1e --- /dev/null +++ b/reservations/migrations/0003_reservation_timespan.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.1 on 2024-09-09 21:32 + +import django.contrib.postgres.fields.ranges +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("reservations", "0002_externalreservation"), + ] + + operations = [ + migrations.AddField( + model_name="reservation", + name="timespan", + field=models.GeneratedField( + db_persist=True, + expression=models.Func( + models.F("start"), models.F("end"), function="tstzrange" + ), + output_field=django.contrib.postgres.fields.ranges.DateTimeRangeField(), + ), + ), + ] diff --git a/reservations/models.py b/reservations/models.py index 98afd3c..e3ffc95 100644 --- a/reservations/models.py +++ b/reservations/models.py @@ -3,11 +3,13 @@ from __future__ import annotations from typing import TYPE_CHECKING from django.contrib.auth import get_user_model +from django.contrib.postgres.fields import DateTimeRangeField from django.db import models from django.db.models import F, Q, QuerySet from django.utils import timezone from model_utils.managers import InheritanceQuerySetMixin +from psycopg.types.range import Range if TYPE_CHECKING: from collections.abc import Iterable @@ -55,31 +57,32 @@ class ReservationQuerySet(InheritanceQuerySetMixin, QuerySet): Selects events that are after the specified datetime, including overlaps but excluding exactly matching endpoints. """ - return self.filter(Q(start__gt=start) | Q(end__gt=start)) + return self.filter(timespan__overlap=Range(start, None)) def filter_before(self, end: datetime) -> ReservationQuerySet: """ Selects events that are before the specified datetime, including overlaps but excluding exactly matching endpoints. """ - return self.filter(Q(start__lt=end) | Q(end__lt=end)) + return self.filter(timespan__overlap=Range(None, end)) def filter_between(self, start: datetime, end: datetime) -> ReservationQuerySet: """ Selects events that are between the specified datetime, including overlaps but excluding exactly matching endpoints. """ - return self.filter( - Q(start__gt=start, start__lt=end) - | Q(end__gt=start, end__lt=end) - | (Q(start__lt=start) & Q(end__gt=end)) - ) + return self.filter(timespan__overlap=Range(start, end)) class Reservation(models.Model): resources = models.ManyToManyField(Resource, blank=True) start = models.DateTimeField() end = models.DateTimeField() + timespan = models.GeneratedField( + expression=models.Func(F("start"), F("end"), function="tstzrange"), + output_field=DateTimeRangeField(), + db_persist=True, + ) google_calendar_event_id = models.CharField( max_length=1024, null=True, blank=True, unique=True )