diff options
| author | Simon Charette <charette.s@gmail.com> | 2025-01-13 22:04:33 -0500 |
|---|---|---|
| committer | Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> | 2025-01-14 16:21:19 +0100 |
| commit | f07360e8087d3b403d1d12ff696da3138116055a (patch) | |
| tree | 2317665d4cecd2be7e35da8a1756ef51aa710dd2 | |
| parent | 161e79d277ffe8b79b15ad51cb0d23de54270202 (diff) | |
Refs #36075 -- Adjusted MTI handling of _non_pk_concrete_field_names.
Regression in bf7b17d16d3978b2e1cee4a0f7ce8840bd1a8dc4.
Thanks Sage Abdullah for the report.
| -rw-r--r-- | django/db/models/base.py | 8 | ||||
| -rw-r--r-- | django/db/models/options.py | 5 | ||||
| -rw-r--r-- | tests/composite_pk/test_update.py | 2 | ||||
| -rw-r--r-- | tests/update_only_fields/tests.py | 14 |
4 files changed, 21 insertions, 8 deletions
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"]) |
