Refs #29908 -- Optimized known related objects assignment.
Since CPython implements a C level attrgetter(*attrs) it even outperforms the most common case of a single known related object since the resulting attribute values tuple is built in C.
This commit is contained in:
parent
f436c82637
commit
0cf85e6b07
|
@ -63,6 +63,7 @@ and two directions (forward and reverse) for a total of six combinations.
|
||||||
``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead.
|
``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from django.core.exceptions import FieldError
|
||||||
from django.db import connections, router, transaction
|
from django.db import connections, router, transaction
|
||||||
from django.db.models import Q, signals
|
from django.db.models import Q, signals
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
@ -581,10 +582,17 @@ def create_reverse_many_to_one_manager(superclass, rel):
|
||||||
# that abuse create_reverse_many_to_one_manager() with reverse
|
# that abuse create_reverse_many_to_one_manager() with reverse
|
||||||
# one-to-many relationships instead and break known related
|
# one-to-many relationships instead and break known related
|
||||||
# objects assignment.
|
# objects assignment.
|
||||||
rel_obj_id = tuple([
|
try:
|
||||||
getattr(self.instance, target_field.attname)
|
target_field = self.field.target_field
|
||||||
for target_field in self.field.get_path_info()[-1].target_fields
|
except FieldError:
|
||||||
])
|
# The relationship has multiple target fields. Use a tuple
|
||||||
|
# for related object id.
|
||||||
|
rel_obj_id = tuple([
|
||||||
|
getattr(self.instance, target_field.attname)
|
||||||
|
for target_field in self.field.get_path_info()[-1].target_fields
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
rel_obj_id = getattr(self.instance, target_field.attname)
|
||||||
queryset._known_related_objects = {self.field: {rel_obj_id: self.instance}}
|
queryset._known_related_objects = {self.field: {rel_obj_id: self.instance}}
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
|
@ -62,14 +62,12 @@ class ModelIterable(BaseIterable):
|
||||||
for f in select[model_fields_start:model_fields_end]]
|
for f in select[model_fields_start:model_fields_end]]
|
||||||
related_populators = get_related_populators(klass_info, select, db)
|
related_populators = get_related_populators(klass_info, select, db)
|
||||||
known_related_objects = [
|
known_related_objects = [
|
||||||
(field, related_objs, [
|
(field, related_objs, operator.attrgetter(*[
|
||||||
operator.attrgetter(
|
field.attname
|
||||||
field.attname
|
if from_field == 'self' else
|
||||||
if from_field == 'self' else
|
queryset.model._meta.get_field(from_field).attname
|
||||||
queryset.model._meta.get_field(from_field).attname
|
|
||||||
)
|
|
||||||
for from_field in field.from_fields
|
for from_field in field.from_fields
|
||||||
]) for field, related_objs in queryset._known_related_objects.items()
|
])) for field, related_objs in queryset._known_related_objects.items()
|
||||||
]
|
]
|
||||||
for row in compiler.results_iter(results):
|
for row in compiler.results_iter(results):
|
||||||
obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end])
|
obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end])
|
||||||
|
@ -80,11 +78,11 @@ class ModelIterable(BaseIterable):
|
||||||
setattr(obj, attr_name, row[col_pos])
|
setattr(obj, attr_name, row[col_pos])
|
||||||
|
|
||||||
# Add the known related objects to the model.
|
# Add the known related objects to the model.
|
||||||
for field, rel_objs, rel_getters in known_related_objects:
|
for field, rel_objs, rel_getter in known_related_objects:
|
||||||
# Avoid overwriting objects loaded by, e.g., select_related().
|
# Avoid overwriting objects loaded by, e.g., select_related().
|
||||||
if field.is_cached(obj):
|
if field.is_cached(obj):
|
||||||
continue
|
continue
|
||||||
rel_obj_id = tuple([rel_getter(obj) for rel_getter in rel_getters])
|
rel_obj_id = rel_getter(obj)
|
||||||
try:
|
try:
|
||||||
rel_obj = rel_objs[rel_obj_id]
|
rel_obj = rel_objs[rel_obj_id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
Loading…
Reference in New Issue