|
@@ -2434,11 +2434,23 @@ def get_prefetcher(instance, through_attr, to_attr):
|
|
|
the attribute has already been fetched for that instance)
|
|
|
"""
|
|
|
|
|
|
- def has_to_attr_attribute(instance):
|
|
|
- return hasattr(instance, to_attr)
|
|
|
+ def is_to_attr_fetched(model, to_attr):
|
|
|
+ # Special case cached_property instances because hasattr() triggers
|
|
|
+ # attribute computation and assignment.
|
|
|
+ if isinstance(getattr(model, to_attr, None), cached_property):
|
|
|
+
|
|
|
+ def has_cached_property(instance):
|
|
|
+ return to_attr in instance.__dict__
|
|
|
+
|
|
|
+ return has_cached_property
|
|
|
+
|
|
|
+ def has_to_attr_attribute(instance):
|
|
|
+ return hasattr(instance, to_attr)
|
|
|
+
|
|
|
+ return has_to_attr_attribute
|
|
|
|
|
|
prefetcher = None
|
|
|
- is_fetched = has_to_attr_attribute
|
|
|
+ is_fetched = is_to_attr_fetched(instance.__class__, to_attr)
|
|
|
|
|
|
# For singly related objects, we have to avoid getting the attribute
|
|
|
# from the object, as this will trigger the query. So we first try
|
|
@@ -2453,7 +2465,12 @@ def get_prefetcher(instance, through_attr, to_attr):
|
|
|
# get_prefetch_queryset() method.
|
|
|
if hasattr(rel_obj_descriptor, "get_prefetch_queryset"):
|
|
|
prefetcher = rel_obj_descriptor
|
|
|
- is_fetched = rel_obj_descriptor.is_cached
|
|
|
+ # If to_attr is set, check if the value has already been set,
|
|
|
+ # which is done with has_to_attr_attribute(). Do not use the
|
|
|
+ # method from the descriptor, as the cache_name it defines
|
|
|
+ # checks the field name, not the to_attr value.
|
|
|
+ if through_attr == to_attr:
|
|
|
+ is_fetched = rel_obj_descriptor.is_cached
|
|
|
else:
|
|
|
# descriptor doesn't support prefetching, so we go ahead and get
|
|
|
# the attribute on the instance rather than the class to
|
|
@@ -2461,18 +2478,7 @@ def get_prefetcher(instance, through_attr, to_attr):
|
|
|
rel_obj = getattr(instance, through_attr)
|
|
|
if hasattr(rel_obj, "get_prefetch_queryset"):
|
|
|
prefetcher = rel_obj
|
|
|
- if through_attr != to_attr:
|
|
|
- # Special case cached_property instances because hasattr
|
|
|
- # triggers attribute computation and assignment.
|
|
|
- if isinstance(
|
|
|
- getattr(instance.__class__, to_attr, None), cached_property
|
|
|
- ):
|
|
|
-
|
|
|
- def has_cached_property(instance):
|
|
|
- return to_attr in instance.__dict__
|
|
|
-
|
|
|
- is_fetched = has_cached_property
|
|
|
- else:
|
|
|
+ if through_attr == to_attr:
|
|
|
|
|
|
def in_prefetched_cache(instance):
|
|
|
return through_attr in instance._prefetched_objects_cache
|