From 1506f498fe42b65730ece809300c2c0963e38c5a Mon Sep 17 00:00:00 2001 From: Francesco Panico Date: Thu, 10 Aug 2023 14:08:11 +0000 Subject: [PATCH] Fixed #34743 -- Fixed Meta.constraints validation crash when using pk. Thanks Nwawel A Iroume for the report. --- django/db/models/base.py | 5 ++++- django/db/models/sql/query.py | 2 +- tests/constraints/tests.py | 13 +++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index 3e9b847b37ad..5c0a9c430f34 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1235,11 +1235,14 @@ def _get_field_value_map(self, meta, exclude=None): if exclude is None: exclude = set() meta = meta or self._meta - return { + field_map = { field.name: Value(getattr(self, field.attname), field) for field in meta.local_concrete_fields if field.name not in exclude } + if "pk" not in exclude: + field_map["pk"] = Value(self.pk, meta.pk) + return field_map def prepare_database_save(self, field): if self.pk is None: diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 9853919482e9..6a9348af665d 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1670,7 +1670,7 @@ def names_to_path(self, names, opts, allow_many=True, fail_on_missing=False): path, names_with_path = [], [] for pos, name in enumerate(names): cur_names_with_path = (name, []) - if name == "pk": + if name == "pk" and opts is not None: name = opts.pk.name field = None diff --git a/tests/constraints/tests.py b/tests/constraints/tests.py index f6571084b050..55397449d9aa 100644 --- a/tests/constraints/tests.py +++ b/tests/constraints/tests.py @@ -352,6 +352,19 @@ def test_validate_nullable_jsonfield(self): is_not_null_constraint.validate(JSONFieldModel, JSONFieldModel(data=None)) is_not_null_constraint.validate(JSONFieldModel, JSONFieldModel(data={})) + def test_validate_pk_field(self): + constraint_with_pk = models.CheckConstraint( + check=~models.Q(pk=models.F("age")), + name="pk_not_age_check", + ) + constraint_with_pk.validate(ChildModel, ChildModel(pk=1, age=2)) + msg = f"Constraint “{constraint_with_pk.name}” is violated." + with self.assertRaisesMessage(ValidationError, msg): + constraint_with_pk.validate(ChildModel, ChildModel(pk=1, age=1)) + with self.assertRaisesMessage(ValidationError, msg): + constraint_with_pk.validate(ChildModel, ChildModel(id=1, age=1)) + constraint_with_pk.validate(ChildModel, ChildModel(pk=1, age=1), exclude={"pk"}) + class UniqueConstraintTests(TestCase): @classmethod