summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2025-01-13 22:04:33 -0500
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2025-01-14 16:21:19 +0100
commitf07360e8087d3b403d1d12ff696da3138116055a (patch)
tree2317665d4cecd2be7e35da8a1756ef51aa710dd2
parent161e79d277ffe8b79b15ad51cb0d23de54270202 (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.py8
-rw-r--r--django/db/models/options.py5
-rw-r--r--tests/composite_pk/test_update.py2
-rw-r--r--tests/update_only_fields/tests.py14
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"])