Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions django/db/models/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,15 @@ def __iter__(self):
(
field,
related_objs,
operator.attrgetter(
*[
(
field.attname
if from_field == "self"
else queryset.model._meta.get_field(from_field).attname
)
for from_field in field.from_fields
]
),
attnames := [
(
field.attname
if from_field == "self"
else queryset.model._meta.get_field(from_field).attname
)
for from_field in field.from_fields
],
operator.attrgetter(*attnames),
)
for field, related_objs in queryset._known_related_objects.items()
]
Expand All @@ -145,10 +144,14 @@ def __iter__(self):
setattr(obj, attr_name, row[col_pos])

# Add the known related objects to the model.
for field, rel_objs, rel_getter in known_related_objects:
for field, rel_objs, rel_attnames, rel_getter in known_related_objects:
# Avoid overwriting objects loaded by, e.g., select_related().
if field.is_cached(obj):
continue
# Avoid fetching potentially deferred attributes that would
# result in unexpected queries.
if any(attname not in obj.__dict__ for attname in rel_attnames):
continue
rel_obj_id = rel_getter(obj)
try:
rel_obj = rel_objs[rel_obj_id]
Expand Down
15 changes: 12 additions & 3 deletions tests/defer/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ def test_only(self):
# of them except the model's primary key see #15494
self.assert_delayed(qs.only("pk")[0], 3)
# You can use 'pk' with reverse foreign key lookups.
# The related_id is always set even if it's not fetched from the DB,
# so pk and related_id are not deferred.
self.assert_delayed(self.s1.primary_set.only("pk")[0], 2)
# The related_id is not set if it's not fetched from the DB,
# so pk is not deferred, but related_id is.
self.assert_delayed(self.s1.primary_set.only("pk")[0], 3)

def test_defer_only_chaining(self):
qs = Primary.objects.all()
Expand Down Expand Up @@ -84,6 +84,15 @@ def test_only_none_raises_error(self):
with self.assertRaisesMessage(TypeError, msg):
Primary.objects.only(None)

def test_only_related_manager_optimization(self):
s = Secondary.objects.create(first="one", second="two")
Primary.objects.bulk_create(
[Primary(name="p1", value="v1", related=s) for _ in range(5)]
)
with self.assertNumQueries(1):
for p in s.primary_set.only("pk"):
_ = p.pk

def test_defer_extra(self):
qs = Primary.objects.all()
self.assert_delayed(qs.defer("name").extra(select={"a": 1})[0], 1)
Expand Down