-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
Refs #36481 -- Fixed QuerySet.update concrete fields check. #19595
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @rpkilby for this PR, this is an interesting find! 🌟
I think this could be simplified further, since, assuming A = field.auto_created
and B = field.concrete
, using DeMorgan Law:
direct = not (A and not B) or not B
direct = not A or not (not B) or not B
direct = not A or B or not B
direct = not A or True
direct = True
So then:
if not direct or (field.is_relation and field.many_to_many):
Becomes if not True or...
which is if False or...
which is:
if field.is_relation and field.many_to_many:
Wanna push that change?
@charettes am I missing something??? 🤯 |
@nessita There is some context in the attached ticket, but I think it would be valuable to keep the |
Thank you @rpkilby! I did read the ticket, but I still think there are two things in this PR:
|
Hi @nessita. I'd argue the two items are related and that this is more of a bugfix. imo this change reflects the intent of the original code, and As to the changed test method, composite keys are a new feature as of 5.2, and Updated the test to include both saved and unsaved instances. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo this change reflects the intent of the original code, and direct being reducable is only due to the faulty logic, which this would fix.
I agree with this conclusion. The logic in add_update_values
is there to ensure that only fields backed by actual columns can be updated against as that's a requirement of UPDATE
.
Since Field.concrete
basically means column is not None
only checking against it should be enough? That fact we need the or (field.is_relation and field.many_to_many)
part in the first place is likely due to ManyToManyField
lacking a proper get_attname_column
override.
def get_attname_column(self):
attname, column = super().get_attname_column()
return attname, None
@charettes should I commit an update with the following changes: diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index bad71a5fd6..e97ecf8a0d 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -1844,6 +1844,10 @@ class ManyToManyField(RelatedField):
)
return name, path, args, kwargs
+ def get_attname_column(self):
+ attname, column = super().get_attname_column()
+ return attname, None
+
def _get_path_info(self, direct=False, filtered_relation=None):
"""Called by both direct and indirect m2m traversal."""
int_model = self.remote_field.through
diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py
index d0e0f82cd5..86f12f95ea 100644
--- a/django/db/models/sql/subqueries.py
+++ b/django/db/models/sql/subqueries.py
@@ -91,10 +91,10 @@ class UpdateQuery(Query):
raise FieldError(
"Composite primary key fields must be updated individually."
)
- if not field.concrete or (field.is_relation and field.many_to_many):
+ if not field.concrete:
raise FieldError(
- "Cannot update model field %r (only non-relations and "
- "foreign keys permitted)." % field
+ "Cannot update model field %r (only concrete fields are permitted)."
+ % field
)
if model is not self.get_meta().concrete_model:
self.add_related_update(model, field, val)
diff --git a/tests/update/tests.py b/tests/update/tests.py
index bb83440008..e17d906c80 100644
--- a/tests/update/tests.py
+++ b/tests/update/tests.py
@@ -160,7 +160,7 @@ class AdvancedTests(TestCase):
msg = (
"Cannot update model field "
"<django.db.models.fields.related.ManyToManyField: m2m_foo> "
- "(only non-relations and foreign keys permitted)."
+ "(only concrete fields are permitted)."
)
with self.assertRaisesMessage(FieldError, msg):
Bar.objects.update(m2m_foo="whatever") |
@rpkilby no lets keep this work focused on dealing with |
Thanks - let me know if there's anything else I can to do to help wrap this up. |
I've reopened https://code.djangoproject.com/ticket/35453, which discusses this question. |
Trac ticket number
ticket-36481
Branch description
Fixed
QuerySet.update
concrete fields check.Checklist
main
branch.