summaryrefslogtreecommitdiff
path: root/django/db/models/query_utils.py
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2024-04-05 23:20:41 -0400
committernessita <124304+nessita@users.noreply.github.com>2024-04-23 13:17:17 -0300
commit195d885ca01b14e3ce9a1881c3b8f7074f953736 (patch)
tree559f8ababd0bb79626a0fa20c00a4d1bf6827a48 /django/db/models/query_utils.py
parent83f5478225588f31e7cbbfed63a4a2b936abc03f (diff)
Refs #35356 -- Clarified select related with masked field logic.
By always including related objects in the select mask via adjusting the defer logic (_get_defer_select_mask()), it becomes possible for select_related_descend() to treat forward and reverse relationships indistinctively. This work also simplifies and adds comments to select_related_descend() to make it easier to understand.
Diffstat (limited to 'django/db/models/query_utils.py')
-rw-r--r--django/db/models/query_utils.py47
1 files changed, 23 insertions, 24 deletions
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index 7162c4fea9..9be17a4a84 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -340,38 +340,37 @@ class RegisterLookupMixin:
_unregister_class_lookup = classmethod(_unregister_class_lookup)
-def select_related_descend(field, restricted, requested, select_mask, reverse=False):
+def select_related_descend(field, restricted, requested, select_mask):
"""
- Return True if this field should be used to descend deeper for
- select_related() purposes. Used by both the query construction code
- (compiler.get_related_selections()) and the model instance creation code
- (compiler.klass_info).
+ Return whether `field` should be used to descend deeper for
+ `select_related()` purposes.
Arguments:
- * field - the field to be checked
- * restricted - a boolean field, indicating if the field list has been
- manually restricted using a requested clause)
- * requested - The select_related() dictionary.
- * select_mask - the dictionary of selected fields.
- * reverse - boolean, True if we are checking a reverse select related
+ * `field` - the field to be checked. Can be either a `Field` or
+ `ForeignObjectRel` instance.
+ * `restricted` - a boolean field, indicating if the field list has been
+ manually restricted using a select_related() clause.
+ * `requested` - the select_related() dictionary.
+ * `select_mask` - the dictionary of selected fields.
"""
+ # Only relationships can be descended.
if not field.remote_field:
return False
- if field.remote_field.parent_link and not reverse:
+ # Forward MTI parent links should not be explicitly descended as they are
+ # always JOIN'ed against (unless excluded by `select_mask`).
+ if getattr(field.remote_field, "parent_link", False):
return False
- if restricted:
- if reverse and field.related_query_name() not in requested:
- return False
- if not reverse and field.name not in requested:
- return False
- if not restricted and field.null:
+ # When `select_related()` is used without a `*requested` mask all
+ # relationships are descended unless they are nullable.
+ if not restricted:
+ return not field.null
+ # When `select_related(*requested)` is used only fields that are part of
+ # `requested` should be descended.
+ if field.name not in requested:
return False
- if (
- restricted
- and select_mask
- and field.name in requested
- and field not in select_mask
- ):
+ # Prevent invalid usages of `select_related()` and `only()`/`defer()` such
+ # as `select_related("a").only("b")` and `select_related("a").defer("a")`.
+ if select_mask and field not in select_mask:
raise FieldError(
f"Field {field.model._meta.object_name}.{field.name} cannot be both "
"deferred and traversed using select_related at the same time."