Changes between Version 1 and Version 2 of Ticket #35442, comment 6


Ignore:
Timestamp:
May 10, 2024, 3:24:45 PM (6 months ago)
Author:
Simon Charette

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #35442, comment 6

    v1 v2  
    11Accessing objects though a related manager should still allow for `only` to work as expected. The fact that the ORM doesn't even warn you when it silently issues queries on field deferral leaks (#22492) makes this behavior really insidious and prevents safe usage of related managers as Guillaume brought up.
    22
    3 I think the the ORM should either have `RelatedManager.only` include the reverse field implicitly (which we've kind of rules out against in #33835) or we should find a way to more safely assign known related objects in the face of deferred fields.
     3I think the the ORM should either have `RelatedManager.only` include the reverse field implicitly (which we've kind of rules out against in #33835) or we should find a way to more safely assign known related objects in the face of deferred fields (e.g. make sure required `attrs` are in `obj.__dict__`).
     4
     5Potentially naive patch
     6
     7{{{#!diff
     8diff --git a/django/db/models/query.py b/django/db/models/query.py
     9index cb5c63c0d1..883a7a71e4 100644
     10--- a/django/db/models/query.py
     11+++ b/django/db/models/query.py
     12@@ -107,18 +107,18 @@ def __iter__(self):
     13             (
     14                 field,
     15                 related_objs,
     16-                operator.attrgetter(
     17-                    *[
     18-                        (
     19-                            field.attname
     20-                            if from_field == "self"
     21-                            else queryset.model._meta.get_field(from_field).attname
     22-                        )
     23-                        for from_field in field.from_fields
     24-                    ]
     25-                ),
     26+                attnames := [
     27+                    (
     28+                        field.attname
     29+                        if from_field == "self"
     30+                        else queryset.model._meta.get_field(from_field).attname
     31+                    )
     32+                    for from_field in field.from_fields
     33+                ],
     34+                operator.attrgetter(*attnames),
     35             )
     36             for field, related_objs in queryset._known_related_objects.items()
     37+            # if not is_deferred(field)
     38         ]
     39         for row in compiler.results_iter(results):
     40             obj = model_cls.from_db(
     41@@ -131,10 +131,14 @@ def __iter__(self):
     42                     setattr(obj, attr_name, row[col_pos])
     43
     44             # Add the known related objects to the model.
     45-            for field, rel_objs, rel_getter in known_related_objects:
     46+            for field, rel_objs, attnames, rel_getter in known_related_objects:
     47                 # Avoid overwriting objects loaded by, e.g., select_related().
     48                 if field.is_cached(obj):
     49                     continue
     50+                # Avoid fetching potentially deferred attributes that would
     51+                # result in unexpected queries.
     52+                if any(attname not in obj.__dict__ for attname in attnames):
     53+                    continue
     54                 rel_obj_id = rel_getter(obj)
     55                 try:
     56                     rel_obj = rel_objs[rel_obj_id]
     57}}}
Back to Top