mirror of
https://github.com/django/django.git
synced 2025-08-03 18:38:50 +00:00
Fixed #30581 -- Added support for Meta.constraints validation.
Thanks Simon Charette, Keryn Knight, and Mariusz Felisiak for reviews.
This commit is contained in:
parent
441103a04d
commit
667105877e
17 changed files with 852 additions and 88 deletions
|
@ -2,6 +2,7 @@ import datetime
|
|||
from unittest import mock
|
||||
|
||||
from django.contrib.postgres.indexes import OpClass
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError, NotSupportedError, connection, transaction
|
||||
from django.db.models import (
|
||||
CheckConstraint,
|
||||
|
@ -612,18 +613,26 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
|
|||
timezone.datetime(2018, 6, 28),
|
||||
timezone.datetime(2018, 6, 29),
|
||||
]
|
||||
HotelReservation.objects.create(
|
||||
reservation = HotelReservation.objects.create(
|
||||
datespan=DateRange(datetimes[0].date(), datetimes[1].date()),
|
||||
start=datetimes[0],
|
||||
end=datetimes[1],
|
||||
room=room102,
|
||||
)
|
||||
constraint.validate(HotelReservation, reservation)
|
||||
HotelReservation.objects.create(
|
||||
datespan=DateRange(datetimes[1].date(), datetimes[3].date()),
|
||||
start=datetimes[1],
|
||||
end=datetimes[3],
|
||||
room=room102,
|
||||
)
|
||||
HotelReservation.objects.create(
|
||||
datespan=DateRange(datetimes[3].date(), datetimes[4].date()),
|
||||
start=datetimes[3],
|
||||
end=datetimes[4],
|
||||
room=room102,
|
||||
cancelled=True,
|
||||
)
|
||||
# Overlap dates.
|
||||
with self.assertRaises(IntegrityError), transaction.atomic():
|
||||
reservation = HotelReservation(
|
||||
|
@ -632,33 +641,58 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
|
|||
end=datetimes[2],
|
||||
room=room102,
|
||||
)
|
||||
msg = f"Constraint “{constraint.name}” is violated."
|
||||
with self.assertRaisesMessage(ValidationError, msg):
|
||||
constraint.validate(HotelReservation, reservation)
|
||||
reservation.save()
|
||||
# Valid range.
|
||||
HotelReservation.objects.bulk_create(
|
||||
[
|
||||
# Other room.
|
||||
HotelReservation(
|
||||
datespan=(datetimes[1].date(), datetimes[2].date()),
|
||||
start=datetimes[1],
|
||||
end=datetimes[2],
|
||||
room=room101,
|
||||
),
|
||||
# Cancelled reservation.
|
||||
HotelReservation(
|
||||
datespan=(datetimes[1].date(), datetimes[1].date()),
|
||||
start=datetimes[1],
|
||||
end=datetimes[2],
|
||||
room=room102,
|
||||
cancelled=True,
|
||||
),
|
||||
# Other adjacent dates.
|
||||
HotelReservation(
|
||||
datespan=(datetimes[3].date(), datetimes[4].date()),
|
||||
start=datetimes[3],
|
||||
end=datetimes[4],
|
||||
room=room102,
|
||||
),
|
||||
]
|
||||
other_valid_reservations = [
|
||||
# Other room.
|
||||
HotelReservation(
|
||||
datespan=(datetimes[1].date(), datetimes[2].date()),
|
||||
start=datetimes[1],
|
||||
end=datetimes[2],
|
||||
room=room101,
|
||||
),
|
||||
# Cancelled reservation.
|
||||
HotelReservation(
|
||||
datespan=(datetimes[1].date(), datetimes[1].date()),
|
||||
start=datetimes[1],
|
||||
end=datetimes[2],
|
||||
room=room102,
|
||||
cancelled=True,
|
||||
),
|
||||
# Other adjacent dates.
|
||||
HotelReservation(
|
||||
datespan=(datetimes[3].date(), datetimes[4].date()),
|
||||
start=datetimes[3],
|
||||
end=datetimes[4],
|
||||
room=room102,
|
||||
),
|
||||
]
|
||||
for reservation in other_valid_reservations:
|
||||
constraint.validate(HotelReservation, reservation)
|
||||
HotelReservation.objects.bulk_create(other_valid_reservations)
|
||||
# Excluded fields.
|
||||
constraint.validate(
|
||||
HotelReservation,
|
||||
HotelReservation(
|
||||
datespan=(datetimes[1].date(), datetimes[2].date()),
|
||||
start=datetimes[1],
|
||||
end=datetimes[2],
|
||||
room=room102,
|
||||
),
|
||||
exclude={"room"},
|
||||
)
|
||||
constraint.validate(
|
||||
HotelReservation,
|
||||
HotelReservation(
|
||||
datespan=(datetimes[1].date(), datetimes[2].date()),
|
||||
start=datetimes[1],
|
||||
end=datetimes[2],
|
||||
room=room102,
|
||||
),
|
||||
exclude={"datespan", "start", "end", "room"},
|
||||
)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
|
@ -731,6 +765,21 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
|
|||
constraint_name, self.get_constraints(RangesModel._meta.db_table)
|
||||
)
|
||||
|
||||
def test_validate_range_adjacent(self):
|
||||
constraint = ExclusionConstraint(
|
||||
name="ints_adjacent",
|
||||
expressions=[("ints", RangeOperators.ADJACENT_TO)],
|
||||
violation_error_message="Custom error message.",
|
||||
)
|
||||
range_obj = RangesModel.objects.create(ints=(20, 50))
|
||||
constraint.validate(RangesModel, range_obj)
|
||||
msg = "Custom error message."
|
||||
with self.assertRaisesMessage(ValidationError, msg):
|
||||
constraint.validate(RangesModel, RangesModel(ints=(10, 20)))
|
||||
constraint.validate(RangesModel, RangesModel(ints=(10, 19)))
|
||||
constraint.validate(RangesModel, RangesModel(ints=(51, 60)))
|
||||
constraint.validate(RangesModel, RangesModel(ints=(10, 20)), exclude={"ints"})
|
||||
|
||||
def test_expressions_with_params(self):
|
||||
constraint_name = "scene_left_equal"
|
||||
self.assertNotIn(constraint_name, self.get_constraints(Scene._meta.db_table))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue