Opened 99 minutes ago
#36627 new Uncategorized
Support PostgreSQL 18+ temporal constraints
Reported by: | Adam Johnson | Owned by: | |
---|---|---|---|
Component: | contrib.postgres | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
- Release note
- `UNIQUE CONSTRAINT` docs for `WITHOUT OVERLAPS`
- `PRIMARY KEY` docs for `WITHOUT OVERLAPS`
- `FOREIGN KEY` docs for `PERIOD`
PostgreSQL 18 comes with support for "temporal constraints". These allow adding a special modifier to the final column in a unique constraint (WITHOUT OVERLAPS
), primary key (WITHOUT OVERLAPS
), or foreign key (PERIOD
) which turns that column into a kind of "period specifier". This allows enforcing the given constraint is valid only for the range of that period. While the feature is not time-specific, the natural use case is to enforce uniqueness / validness over time, hence the name.
I propose that we add support for this feature in Django's UniqueConstraint
, through some a new expression WithoutOverlaps
in django.contrib.postgres
, which would be used like this:
from django.db import models from django.contrib.postgres.constraints import WithoutOverlaps from django.contrib.postgres.fields import DateTimeRangeField class Booking(models.Model): title = models.TextField() room = models.ForeignKey(Room, on_delete=models.CASCADE) span = DateTimeRangeField() class Meta: constraints = [ models.UniqueConstraint( "room", WithoutOverlaps("span"), name="%(app_label)s_%(class)s_room_span_no_overlaps" ), ]
This could compile down to SQL like UNIQUE (room_id, span WITHOUT OVERLAPS)
.
It may also be possible, but more tricky, to support the other two ways to specify temporal constraints:
- Primary key support would need to modify or subclass
CompositePrimaryKey
to supportWithoutOverlaps
for the final column. - Foreign key support would need to modify or subclass
ForeignObject
, which is still a private API.