Range fields + EXCLUDE constraint

Django ORM PostgreSQL May 22, 2026 python

Range fields store an interval (e.g.

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# ======================================================================
# Range fields store an interval (e.g. a booking's start/end) in a single
# column. `__contains` tests membership. The `ExclusionConstraint` uses the `&&`
# overlap operator to forbid two reservations for the same room from overlapping
# — enforced by the database.
# ======================================================================

# ----------------------------------------------------------------------
# reservations covering 2026-06-03
# ----------------------------------------------------------------------

# Django:
probe = datetime(2026, 6, 3, tzinfo=timezone.utc)
Reservation.objects.filter(during__contains=probe)

# SQL:
#   SELECT "examples_reservation"."id", "examples_reservation"."room", "examples_reservation"."during", "examples_reservation"."price_band", "examples_reservation"."date_span", "examples_reservation"."amount", "examples_reservation"."seq"
#   FROM "examples_reservation"
#   WHERE "examples_reservation"."during" @> '2026-06-03 00:00:00+00:00'::timestamptz::timestamp with time zone

# Result:
#   room 101 [2026-06-01 00:00:00+00:00, 2026-06-05 00:00:00+00:00)
#   room 202 [2026-06-02 00:00:00+00:00, 2026-06-06 00:00:00+00:00)

# ----------------------------------------------------------------------
# insert OVERLAPPING reservation for room 101
# ----------------------------------------------------------------------

# Django:
# blocked by the EXCLUDE constraint (room = ... && during overlaps)
Reservation.objects.create(
    room=101,
    during=Range(datetime(2026, 6, 3, tzinfo=timezone.utc),
                 datetime(2026, 6, 7, tzinfo=timezone.utc)),
)

# SQL:
#   BEGIN
#
#   INSERT INTO "examples_reservation" ("room", "during", "price_band", "date_span", "amount", "seq")
#   VALUES (101, '["2026-06-03 00:00:00+00:00","2026-06-07 00:00:00+00:00")'::tstzrange::tstzrange, NULL::int4range, NULL::daterange, NULL::numrange, NULL::int8range)
#   RETURNING "examples_reservation"."id"
#
#   ROLLBACK

# Result:
#   correctly rejected: conflicting key value violates exclusion constraint "exclude_overlapping_reservations"