Fixed #36730 -- Fixed constraint validation crash for excluded FK attnames.

Regression in e44e8327d3.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
Adam Johnson 2025-11-12 19:47:16 +00:00 committed by Mariusz Felisiak
parent 5401b125ab
commit abfa4619fb
3 changed files with 39 additions and 2 deletions

View file

@ -51,9 +51,12 @@ class BaseConstraint:
def _expression_refs_exclude(cls, model, expression, exclude):
get_field = model._meta.get_field
for field_name, *__ in model._get_expr_references(expression):
if field_name in exclude:
if field_name == "pk":
field = model._meta.pk
else:
field = get_field(field_name)
if field_name in exclude or field.name in exclude:
return True
field = get_field(field_name)
if field.generated and cls._expression_refs_exclude(
model, field.expression, exclude
):

View file

@ -543,3 +543,22 @@ class ConstraintsModel(models.Model):
violation_error_message="Price must be greater than zero.",
),
]
class AttnameConstraintsModel(models.Model):
left = models.ForeignKey(
"self", related_name="+", null=True, on_delete=models.SET_NULL
)
right = models.ForeignKey(
"self", related_name="+", null=True, on_delete=models.SET_NULL
)
class Meta:
required_db_features = {"supports_table_check_constraints"}
constraints = [
models.CheckConstraint(
name="%(app_label)s_%(class)s_left_not_right",
# right_id here is the ForeignKey's attname, not name.
condition=~models.Q(left=models.F("right_id")),
),
]

View file

@ -30,6 +30,7 @@ from django.utils.version import PY314, PYPY
from .models import (
Article,
ArticleStatus,
AttnameConstraintsModel,
Author,
Author1,
Award,
@ -3766,3 +3767,17 @@ class ConstraintValidationTests(TestCase):
self.assertEqual(
full_form.errors, {"__all__": ["Price must be greater than zero."]}
)
def test_check_constraint_refs_excluded_field_attname(self):
left = AttnameConstraintsModel.objects.create()
instance = AttnameConstraintsModel.objects.create(left=left)
data = {
"left": str(left.id),
"right": "",
}
AttnameConstraintsModelForm = modelform_factory(
AttnameConstraintsModel, fields="__all__"
)
full_form = AttnameConstraintsModelForm(data, instance=instance)
self.assertFalse(full_form.is_valid())
self.assertEqual(full_form.errors, {"right": ["This field is required."]})