diff options
| author | Simon Charette <charette.s@gmail.com> | 2024-04-05 23:08:49 -0400 |
|---|---|---|
| committer | nessita <124304+nessita@users.noreply.github.com> | 2024-04-23 13:17:17 -0300 |
| commit | 83f5478225588f31e7cbbfed63a4a2b936abc03f (patch) | |
| tree | 8024a1a6871b67d16f5ba8df3ecd435f5e48afce /django/db/models/sql/query.py | |
| parent | bcad5ad92b1dad2874453dee7a480e9b9f29aad5 (diff) | |
Fixed #35356 -- Deferred self-referential foreign key fields adequately.
While refs #34612 surfaced issues with reverse one-to-one fields
deferrals, it missed that switching to storing remote fields would break
self-referential relationships.
This change switches to storing related objects in the select mask
instead of remote fields to prevent collisions when dealing with
self-referential relationships that might have a different directional
mask.
Despite fixing #21204 introduced a crash under some self-referential
deferral conditions, it was simply not working even before that as it
aggregated the sets of deferred fields by model.
Thanks Joshua van Besouw for the report and Mariusz Felisiak for the
review.
Diffstat (limited to 'django/db/models/sql/query.py')
| -rw-r--r-- | django/db/models/sql/query.py | 18 |
1 files changed, 5 insertions, 13 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index b3f130c0b4..c5c58b1788 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -815,19 +815,17 @@ class Query(BaseExpression): if filtered_relation := self._filtered_relations.get(field_name): relation = opts.get_field(filtered_relation.relation_name) field_select_mask = select_mask.setdefault((field_name, relation), {}) - field = relation.field else: - reverse_rel = opts.get_field(field_name) + relation = opts.get_field(field_name) # While virtual fields such as many-to-many and generic foreign # keys cannot be effectively deferred we've historically # allowed them to be passed to QuerySet.defer(). Ignore such # field references until a layer of validation at mask # alteration time will be implemented eventually. - if not hasattr(reverse_rel, "field"): + if not hasattr(relation, "field"): continue - field = reverse_rel.field - field_select_mask = select_mask.setdefault(field, {}) - related_model = field.model._meta.concrete_model + field_select_mask = select_mask.setdefault(relation, {}) + related_model = relation.related_model._meta.concrete_model self._get_defer_select_mask( related_model._meta, field_mask, field_select_mask ) @@ -840,13 +838,7 @@ class Query(BaseExpression): # Only include fields mentioned in the mask. for field_name, field_mask in mask.items(): field = opts.get_field(field_name) - # Retrieve the actual field associated with reverse relationships - # as that's what is expected in the select mask. - if field in opts.related_objects: - field_key = field.field - else: - field_key = field - field_select_mask = select_mask.setdefault(field_key, {}) + field_select_mask = select_mask.setdefault(field, {}) if field_mask: if not field.is_relation: raise FieldError(next(iter(field_mask))) |
