mirror of
https://github.com/django/django.git
synced 2025-10-29 16:46:11 +00:00
Fixed #19547 -- Caching of related instances.
When &'ing or |'ing querysets, wrong values could be cached, and crashes could happen. Thanks Marc Tamlyn for figuring out the problem and writing the patch.
This commit is contained in:
@@ -44,7 +44,7 @@ class QuerySet(object):
|
||||
self._for_write = False
|
||||
self._prefetch_related_lookups = []
|
||||
self._prefetch_done = False
|
||||
self._known_related_object = None # (attname, rel_obj)
|
||||
self._known_related_objects = {} # {rel_field, {pk: rel_obj}}
|
||||
|
||||
########################
|
||||
# PYTHON MAGIC METHODS #
|
||||
@@ -221,6 +221,7 @@ class QuerySet(object):
|
||||
if isinstance(other, EmptyQuerySet):
|
||||
return other._clone()
|
||||
combined = self._clone()
|
||||
combined._merge_known_related_objects(other)
|
||||
combined.query.combine(other.query, sql.AND)
|
||||
return combined
|
||||
|
||||
@@ -229,6 +230,7 @@ class QuerySet(object):
|
||||
combined = self._clone()
|
||||
if isinstance(other, EmptyQuerySet):
|
||||
return combined
|
||||
combined._merge_known_related_objects(other)
|
||||
combined.query.combine(other.query, sql.OR)
|
||||
return combined
|
||||
|
||||
@@ -289,10 +291,9 @@ class QuerySet(object):
|
||||
init_list.append(field.attname)
|
||||
model_cls = deferred_class_factory(self.model, skip)
|
||||
|
||||
# Cache db, model and known_related_object outside the loop
|
||||
# Cache db and model outside the loop
|
||||
db = self.db
|
||||
model = self.model
|
||||
kro_attname, kro_instance = self._known_related_object or (None, None)
|
||||
compiler = self.query.get_compiler(using=db)
|
||||
if fill_cache:
|
||||
klass_info = get_klass_info(model, max_depth=max_depth,
|
||||
@@ -323,9 +324,16 @@ class QuerySet(object):
|
||||
for i, aggregate in enumerate(aggregate_select):
|
||||
setattr(obj, aggregate, row[i + aggregate_start])
|
||||
|
||||
# Add the known related object to the model, if there is one
|
||||
if kro_instance:
|
||||
setattr(obj, kro_attname, kro_instance)
|
||||
# Add the known related objects to the model, if there are any
|
||||
if self._known_related_objects:
|
||||
for field, rel_objs in self._known_related_objects.items():
|
||||
pk = getattr(obj, field.get_attname())
|
||||
try:
|
||||
rel_obj = rel_objs[pk]
|
||||
except KeyError:
|
||||
pass # may happen in qs1 | qs2 scenarios
|
||||
else:
|
||||
setattr(obj, field.name, rel_obj)
|
||||
|
||||
yield obj
|
||||
|
||||
@@ -902,7 +910,7 @@ class QuerySet(object):
|
||||
c = klass(model=self.model, query=query, using=self._db)
|
||||
c._for_write = self._for_write
|
||||
c._prefetch_related_lookups = self._prefetch_related_lookups[:]
|
||||
c._known_related_object = self._known_related_object
|
||||
c._known_related_objects = self._known_related_objects
|
||||
c.__dict__.update(kwargs)
|
||||
if setup and hasattr(c, '_setup_query'):
|
||||
c._setup_query()
|
||||
@@ -942,6 +950,13 @@ class QuerySet(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def _merge_known_related_objects(self, other):
|
||||
"""
|
||||
Keep track of all known related objects from either QuerySet instance.
|
||||
"""
|
||||
for field, objects in other._known_related_objects.items():
|
||||
self._known_related_objects.setdefault(field, {}).update(objects)
|
||||
|
||||
def _setup_aggregate_query(self, aggregates):
|
||||
"""
|
||||
Prepare the query for computing a result that contains aggregate annotations.
|
||||
|
||||
Reference in New Issue
Block a user