Browse Source

Refs #36075 -- Adjusted MTI handling of _non_pk_concrete_field_names.

Regression in bf7b17d16d3978b2e1cee4a0f7ce8840bd1a8dc4.

Thanks Sage Abdullah for the report.
Simon Charette 2 months ago
parent
commit
f07360e808

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

@@ -874,13 +874,13 @@ class Model(AltersData, metaclass=ModelBase):
 
             update_fields = frozenset(update_fields)
             field_names = self._meta._non_pk_concrete_field_names
-            non_model_fields = update_fields.difference(field_names)
+            not_updatable_fields = update_fields.difference(field_names)
 
-            if non_model_fields:
+            if not_updatable_fields:
                 raise ValueError(
                     "The following fields do not exist in this model, are m2m "
-                    "fields, or are non-concrete fields: %s"
-                    % ", ".join(non_model_fields)
+                    "fields, primary keys, or are non-concrete fields: %s"
+                    % ", ".join(not_updatable_fields)
                 )
 
         # If saving to the same database, and this model is deferred, then

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

@@ -1008,8 +1008,11 @@ class Options:
         Return a set of the non-pk concrete field names defined on the model.
         """
         names = []
+        all_pk_fields = set(self.pk_fields)
+        for parent in self.all_parents:
+            all_pk_fields.update(parent._meta.pk_fields)
         for field in self.concrete_fields:
-            if field not in self.pk_fields:
+            if field not in all_pk_fields:
                 names.append(field.name)
                 if field.name != field.attname:
                     names.append(field.attname)

+ 1 - 1
tests/composite_pk/test_update.py

@@ -74,7 +74,7 @@ class CompositePKUpdateTests(TestCase):
     def test_update_fields_pk_field(self):
         msg = (
             "The following fields do not exist in this model, are m2m fields, "
-            "or are non-concrete fields: id"
+            "primary keys, or are non-concrete fields: id"
         )
         with self.assertRaisesMessage(ValueError, msg):
             self.user_1.save(update_fields=["id"])

+ 12 - 2
tests/update_only_fields/tests.py

@@ -7,8 +7,8 @@ from .models import Account, Employee, Person, Profile, ProxyEmployee
 
 class UpdateOnlyFieldsTests(TestCase):
     msg = (
-        "The following fields do not exist in this model, are m2m fields, or "
-        "are non-concrete fields: %s"
+        "The following fields do not exist in this model, are m2m "
+        "fields, primary keys, or are non-concrete fields: %s"
     )
 
     def test_update_fields_basic(self):
@@ -308,3 +308,13 @@ class UpdateOnlyFieldsTests(TestCase):
         profile_boss = Profile.objects.create(name="Boss", salary=3000)
         with self.assertRaisesMessage(ValueError, self.msg % "non_concrete"):
             profile_boss.save(update_fields=["non_concrete"])
+
+    def test_update_pk_field(self):
+        person_boss = Person.objects.create(name="Boss", gender="F")
+        with self.assertRaisesMessage(ValueError, self.msg % "id"):
+            person_boss.save(update_fields=["id"])
+
+    def test_update_inherited_pk_field(self):
+        employee_boss = Employee.objects.create(name="Boss", gender="F")
+        with self.assertRaisesMessage(ValueError, self.msg % "id"):
+            employee_boss.save(update_fields=["id"])