diff --git a/django/db/models/base.py b/django/db/models/base.py index 0f07d235a9..4c7b71e848 100644 --- a/django/db/models/base.py +++ b/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 diff --git a/django/db/models/options.py b/django/db/models/options.py index 28b93ca155..11b2742f7d 100644 --- a/django/db/models/options.py +++ b/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) diff --git a/tests/composite_pk/test_update.py b/tests/composite_pk/test_update.py index 4d45e906cf..ec770230fc 100644 --- a/tests/composite_pk/test_update.py +++ b/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"]) diff --git a/tests/update_only_fields/tests.py b/tests/update_only_fields/tests.py index a6a5b7cb8e..43f3e1fd16 100644 --- a/tests/update_only_fields/tests.py +++ b/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"])