Browse Source

Fixed #34743 -- Fixed Meta.constraints validation crash when using pk.

Thanks Nwawel A Iroume for the report.
Francesco Panico 1 year ago
parent
commit
1506f498fe
3 changed files with 18 additions and 2 deletions
  1. 4 1
      django/db/models/base.py
  2. 1 1
      django/db/models/sql/query.py
  3. 13 0
      tests/constraints/tests.py

+ 4 - 1
django/db/models/base.py

@@ -1235,11 +1235,14 @@ class Model(AltersData, metaclass=ModelBase):
         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:

+ 1 - 1
django/db/models/sql/query.py

@@ -1670,7 +1670,7 @@ class Query(BaseExpression):
         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

+ 13 - 0
tests/constraints/tests.py

@@ -352,6 +352,19 @@ class CheckConstraintTests(TestCase):
             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