Python django.utils.functional.cached_property() Examples
The following are 27
code examples of django.utils.functional.cached_property().
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example.
You may also want to check out all available functions/classes of the module
django.utils.functional
, or try the search function
.
Example #1
Source File: __init__.py From bioforum with MIT License | 6 votes |
def __reduce__(self): """ Pickling should return the model._meta.fields instance of the field, not a new copy of that field. So, use the app registry to load the model and then the field back. """ if not hasattr(self, 'model'): # Fields are sometimes used without attaching them to models (for # example in aggregation). In this case give back a plain field # instance. The code below will create a new empty instance of # class self.__class__, then update its dict with self.__dict__ # values - so, this is very close to normal pickle. state = self.__dict__.copy() # The _get_default cached_property can't be pickled due to lambda # usage. state.pop('_get_default', None) return _empty, (self.__class__,), state return _load_field, (self.model._meta.app_label, self.model._meta.object_name, self.name)
Example #2
Source File: __init__.py From Hands-On-Application-Development-with-PyCharm with MIT License | 6 votes |
def __reduce__(self): """ Pickling should return the model._meta.fields instance of the field, not a new copy of that field. So, use the app registry to load the model and then the field back. """ if not hasattr(self, 'model'): # Fields are sometimes used without attaching them to models (for # example in aggregation). In this case give back a plain field # instance. The code below will create a new empty instance of # class self.__class__, then update its dict with self.__dict__ # values - so, this is very close to normal pickle. state = self.__dict__.copy() # The _get_default cached_property can't be pickled due to lambda # usage. state.pop('_get_default', None) return _empty, (self.__class__,), state return _load_field, (self.model._meta.app_label, self.model._meta.object_name, self.name)
Example #3
Source File: test_functional.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_cached_property_reuse_different_names(self): """Disallow this case because the decorated function wouldn't be cached.""" with self.assertRaises(RuntimeError) as ctx: class ReusedCachedProperty: @cached_property def a(self): pass b = a self.assertEqual( str(ctx.exception.__context__), str(TypeError( "Cannot assign the same cached_property to two different " "names ('a' and 'b')." )) )
Example #4
Source File: test_functional.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_cached_property(self): """cached_property caches its value and behaves like a property.""" class Class: @cached_property def value(self): """Here is the docstring...""" return 1, object() @cached_property def __foo__(self): """Here is the docstring...""" return 1, object() def other_value(self): """Here is the docstring...""" return 1, object() other = cached_property(other_value, name='other') attrs = ['value', 'other', '__foo__'] for attr in attrs: self.assertCachedPropertyWorks(attr, Class)
Example #5
Source File: __init__.py From python with Apache License 2.0 | 6 votes |
def __reduce__(self): """ Pickling should return the model._meta.fields instance of the field, not a new copy of that field. So, we use the app registry to load the model and then the field back. """ if not hasattr(self, 'model'): # Fields are sometimes used without attaching them to models (for # example in aggregation). In this case give back a plain field # instance. The code below will create a new empty instance of # class self.__class__, then update its dict with self.__dict__ # values - so, this is very close to normal pickle. state = self.__dict__.copy() # The _get_default cached_property can't be pickled due to lambda # usage. state.pop('_get_default', None) return _empty, (self.__class__,), state return _load_field, (self.model._meta.app_label, self.model._meta.object_name, self.name)
Example #6
Source File: utils.py From django-lifecycle with MIT License | 6 votes |
def _get_model_property_names(instance) -> List[str]: """ Gather up properties and cached_properties which may be methods that were decorated. Need to inspect class versions b/c doing getattr on them could cause unwanted side effects. """ property_names = [] for name in dir(instance): try: attr = getattr(type(instance), name) if isinstance(attr, property) or isinstance(attr, cached_property): property_names.append(name) except AttributeError: pass return property_names
Example #7
Source File: tests_extensions.py From django-ca with GNU General Public License v3.0 | 6 votes |
def test_config(self): self.assertTrue(issubclass(self.ext_class, Extension)) self.assertEqual(self.ext_class.key, self.ext_class_key) self.assertEqual(self.ext_class.name, self.ext_class_name) # Test some basic properties (just to be sure) self.assertIsInstance(self.ext_class.oid, ObjectIdentifier) self.assertIsInstance(self.ext_class.key, str) self.assertGreater(len(self.ext_class.key), 0) self.assertIsInstance(self.ext_class.name, str) self.assertGreater(len(self.ext_class.name), 0) # Test mapping dicts self.assertEqual(KEY_TO_EXTENSION[self.ext_class.key], self.ext_class) self.assertEqual(OID_TO_EXTENSION[self.ext_class.oid], self.ext_class) # test that the model matches self.assertTrue(hasattr(X509CertMixin, self.ext_class.key)) self.assertIsInstance(getattr(X509CertMixin, self.ext_class.key), cached_property)
Example #8
Source File: __init__.py From python2017 with MIT License | 6 votes |
def __reduce__(self): """ Pickling should return the model._meta.fields instance of the field, not a new copy of that field. So, we use the app registry to load the model and then the field back. """ if not hasattr(self, 'model'): # Fields are sometimes used without attaching them to models (for # example in aggregation). In this case give back a plain field # instance. The code below will create a new empty instance of # class self.__class__, then update its dict with self.__dict__ # values - so, this is very close to normal pickle. state = self.__dict__.copy() # The _get_default cached_property can't be pickled due to lambda # usage. state.pop('_get_default', None) return _empty, (self.__class__,), state return _load_field, (self.model._meta.app_label, self.model._meta.object_name, self.name)
Example #9
Source File: core.py From django-controlcenter with BSD 3-Clause "New" or "Revised" License | 5 votes |
def __new__(mcs, name, bases, attrs): # We can't use it on __init__ because # cached_property fires on property's __get__ for attr in mcs.CACHED_ATTRS: if attr in attrs: attrs[attr] = cached_property(attrs[attr]) return super(WidgetMeta, mcs).__new__(mcs, name, bases, attrs)
Example #10
Source File: options.py From GTDWeb with GNU General Public License v2.0 | 5 votes |
def _populate_directed_relation_graph(self): """ This method is used by each model to find its reverse objects. As this method is very expensive and is accessed frequently (it looks up every field in a model, in every app), it is computed on first access and then is set as a property on every model. """ related_objects_graph = defaultdict(list) all_models = self.apps.get_models(include_auto_created=True) for model in all_models: # Abstract model's fields are copied to child models, hence we will # see the fields from the child models. if model._meta.abstract: continue fields_with_relations = ( f for f in model._meta._get_fields(reverse=False, include_parents=False) if f.is_relation and f.related_model is not None ) for f in fields_with_relations: if not isinstance(f.rel.to, six.string_types): related_objects_graph[f.rel.to._meta].append(f) for model in all_models: # Set the relation_tree using the internal __dict__. In this way # we avoid calling the cached property. In attribute lookup, # __dict__ takes precedence over a data descriptor (such as # @cached_property). This means that the _meta._relation_tree is # only called if related_objects is not in __dict__. related_objects = related_objects_graph[model._meta] model._meta.__dict__['_relation_tree'] = related_objects # It seems it is possible that self is not in all_models, so guard # against that with default for get(). return self.__dict__.get('_relation_tree', EMPTY_RELATION_TREE)
Example #11
Source File: test_functional.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_cached_property_name_validation(self): msg = "%s can't be used as the name of a cached_property." with self.assertRaisesMessage(ValueError, msg % "'<lambda>'"): cached_property(lambda x: None) with self.assertRaisesMessage(ValueError, msg % 42): cached_property(str, name=42)
Example #12
Source File: test_functional.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_cached_property_mangled_error(self): msg = ( 'cached_property does not work with mangled methods on ' 'Python < 3.6 without the appropriate `name` argument.' ) with self.assertRaisesMessage(ValueError, msg): @cached_property def __value(self): pass with self.assertRaisesMessage(ValueError, msg): def func(self): pass cached_property(func, name='__value')
Example #13
Source File: test_functional.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_cached_property_set_name_not_called(self): cp = cached_property(lambda s: None) class Foo: pass Foo.cp = cp msg = 'Cannot use cached_property instance without calling __set_name__() on it.' with self.assertRaisesMessage(TypeError, msg): Foo().cp
Example #14
Source File: test_functional.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_cached_property_auto_name(self): """ cached_property caches its value and behaves like a property on mangled methods or when the name kwarg isn't set. """ class Class: @cached_property def __value(self): """Here is the docstring...""" return 1, object() def other_value(self): """Here is the docstring...""" return 1, object() other = cached_property(other_value) other2 = cached_property(other_value, name='different_name') attrs = ['_Class__value', 'other'] for attr in attrs: self.assertCachedPropertyWorks(attr, Class) # An explicit name is ignored. obj = Class() obj.other2 self.assertFalse(hasattr(obj, 'different_name'))
Example #15
Source File: test_functional.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def assertCachedPropertyWorks(self, attr, Class): with self.subTest(attr=attr): def get(source): return getattr(source, attr) obj = Class() class SubClass(Class): pass subobj = SubClass() # Docstring is preserved. self.assertEqual(get(Class).__doc__, 'Here is the docstring...') self.assertEqual(get(SubClass).__doc__, 'Here is the docstring...') # It's cached. self.assertEqual(get(obj), get(obj)) self.assertEqual(get(subobj), get(subobj)) # The correct value is returned. self.assertEqual(get(obj)[0], 1) self.assertEqual(get(subobj)[0], 1) # State isn't shared between instances. obj2 = Class() subobj2 = SubClass() self.assertNotEqual(get(obj), get(obj2)) self.assertNotEqual(get(subobj), get(subobj2)) # It behaves like a property when there's no instance. self.assertIsInstance(get(Class), cached_property) self.assertIsInstance(get(SubClass), cached_property) # 'other_value' doesn't become a property. self.assertTrue(callable(obj.other_value)) self.assertTrue(callable(subobj.other_value))
Example #16
Source File: test_functional.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_cached_property(self): """ cached_property caches its value and that it behaves like a property """ class A: @cached_property def value(self): """Here is the docstring...""" return 1, object() def other_value(self): return 1 other = cached_property(other_value, name='other') # docstring should be preserved self.assertEqual(A.value.__doc__, "Here is the docstring...") a = A() # check that it is cached self.assertEqual(a.value, a.value) # check that it returns the right thing self.assertEqual(a.value[0], 1) # check that state isn't shared between instances a2 = A() self.assertNotEqual(a.value, a2.value) # check that it behaves like a property when there's no instance self.assertIsInstance(A.value, cached_property) # check that overriding name works self.assertEqual(a.other, 1) self.assertTrue(callable(a.other_value))
Example #17
Source File: options.py From python2017 with MIT License | 5 votes |
def _populate_directed_relation_graph(self): """ This method is used by each model to find its reverse objects. As this method is very expensive and is accessed frequently (it looks up every field in a model, in every app), it is computed on first access and then is set as a property on every model. """ related_objects_graph = defaultdict(list) all_models = self.apps.get_models(include_auto_created=True) for model in all_models: opts = model._meta # Abstract model's fields are copied to child models, hence we will # see the fields from the child models. if opts.abstract: continue fields_with_relations = ( f for f in opts._get_fields(reverse=False, include_parents=False) if f.is_relation and f.related_model is not None ) for f in fields_with_relations: if not isinstance(f.remote_field.model, six.string_types): related_objects_graph[f.remote_field.model._meta.concrete_model._meta].append(f) for model in all_models: # Set the relation_tree using the internal __dict__. In this way # we avoid calling the cached property. In attribute lookup, # __dict__ takes precedence over a data descriptor (such as # @cached_property). This means that the _meta._relation_tree is # only called if related_objects is not in __dict__. related_objects = related_objects_graph[model._meta.concrete_model._meta] model._meta.__dict__['_relation_tree'] = related_objects # It seems it is possible that self is not in all_models, so guard # against that with default for get(). return self.__dict__.get('_relation_tree', EMPTY_RELATION_TREE)
Example #18
Source File: tests_extensions.py From django-ca with GNU General Public License v3.0 | 5 votes |
def test_config(self): self.assertTrue(issubclass(self.ext_class, Extension)) self.assertEqual(self.ext_class.key, self.ext_class_key) self.assertEqual(self.ext_class.name, self.ext_class_name) # Test mapping dicts self.assertEqual(KEY_TO_EXTENSION[self.ext_class.key], self.ext_class) self.assertEqual(OID_TO_EXTENSION[self.ext_class.oid], self.ext_class) # test that the model matches self.assertTrue(hasattr(X509CertMixin, self.ext_class.key)) self.assertIsInstance(getattr(X509CertMixin, self.ext_class.key), cached_property)
Example #19
Source File: options.py From openhgsenti with Apache License 2.0 | 5 votes |
def _populate_directed_relation_graph(self): """ This method is used by each model to find its reverse objects. As this method is very expensive and is accessed frequently (it looks up every field in a model, in every app), it is computed on first access and then is set as a property on every model. """ related_objects_graph = defaultdict(list) all_models = self.apps.get_models(include_auto_created=True) for model in all_models: # Abstract model's fields are copied to child models, hence we will # see the fields from the child models. if model._meta.abstract: continue fields_with_relations = ( f for f in model._meta._get_fields(reverse=False, include_parents=False) if f.is_relation and f.related_model is not None ) for f in fields_with_relations: if not isinstance(f.remote_field.model, six.string_types): related_objects_graph[f.remote_field.model._meta].append(f) for model in all_models: # Set the relation_tree using the internal __dict__. In this way # we avoid calling the cached property. In attribute lookup, # __dict__ takes precedence over a data descriptor (such as # @cached_property). This means that the _meta._relation_tree is # only called if related_objects is not in __dict__. related_objects = related_objects_graph[model._meta] model._meta.__dict__['_relation_tree'] = related_objects # It seems it is possible that self is not in all_models, so guard # against that with default for get(). return self.__dict__.get('_relation_tree', EMPTY_RELATION_TREE)
Example #20
Source File: options.py From python with Apache License 2.0 | 5 votes |
def _populate_directed_relation_graph(self): """ This method is used by each model to find its reverse objects. As this method is very expensive and is accessed frequently (it looks up every field in a model, in every app), it is computed on first access and then is set as a property on every model. """ related_objects_graph = defaultdict(list) all_models = self.apps.get_models(include_auto_created=True) for model in all_models: opts = model._meta # Abstract model's fields are copied to child models, hence we will # see the fields from the child models. if opts.abstract: continue fields_with_relations = ( f for f in opts._get_fields(reverse=False, include_parents=False) if f.is_relation and f.related_model is not None ) for f in fields_with_relations: if not isinstance(f.remote_field.model, six.string_types): related_objects_graph[f.remote_field.model._meta.concrete_model._meta].append(f) for model in all_models: # Set the relation_tree using the internal __dict__. In this way # we avoid calling the cached property. In attribute lookup, # __dict__ takes precedence over a data descriptor (such as # @cached_property). This means that the _meta._relation_tree is # only called if related_objects is not in __dict__. related_objects = related_objects_graph[model._meta.concrete_model._meta] model._meta.__dict__['_relation_tree'] = related_objects # It seems it is possible that self is not in all_models, so guard # against that with default for get(). return self.__dict__.get('_relation_tree', EMPTY_RELATION_TREE)
Example #21
Source File: models.py From course-discovery with GNU Affero General Public License v3.0 | 5 votes |
def access_token(self): """ Returns the access token for this service. We don't use a cached_property decorator because we aren't sure how to set custom expiration dates for those. Returns: str: JWT access token """ key = 'oauth2_access_token' access_token = cache.get(key) if not access_token: url = '{root}/access_token'.format(root=self.oauth2_provider_url) access_token, expiration_datetime = EdxRestApiClient.get_oauth_access_token( url, self.oauth2_client_id, self.oauth2_client_secret, token_type='jwt' ) expires = (expiration_datetime - datetime.datetime.utcnow()).seconds cache.set(key, access_token, expires) return access_token
Example #22
Source File: options.py From Hands-On-Application-Development-with-PyCharm with MIT License | 5 votes |
def _populate_directed_relation_graph(self): """ This method is used by each model to find its reverse objects. As this method is very expensive and is accessed frequently (it looks up every field in a model, in every app), it is computed on first access and then is set as a property on every model. """ related_objects_graph = defaultdict(list) all_models = self.apps.get_models(include_auto_created=True) for model in all_models: opts = model._meta # Abstract model's fields are copied to child models, hence we will # see the fields from the child models. if opts.abstract: continue fields_with_relations = ( f for f in opts._get_fields(reverse=False, include_parents=False) if f.is_relation and f.related_model is not None ) for f in fields_with_relations: if not isinstance(f.remote_field.model, str): related_objects_graph[f.remote_field.model._meta.concrete_model._meta].append(f) for model in all_models: # Set the relation_tree using the internal __dict__. In this way # we avoid calling the cached property. In attribute lookup, # __dict__ takes precedence over a data descriptor (such as # @cached_property). This means that the _meta._relation_tree is # only called if related_objects is not in __dict__. related_objects = related_objects_graph[model._meta.concrete_model._meta] model._meta.__dict__['_relation_tree'] = related_objects # It seems it is possible that self is not in all_models, so guard # against that with default for get(). return self.__dict__.get('_relation_tree', EMPTY_RELATION_TREE)
Example #23
Source File: options.py From bioforum with MIT License | 5 votes |
def _populate_directed_relation_graph(self): """ This method is used by each model to find its reverse objects. As this method is very expensive and is accessed frequently (it looks up every field in a model, in every app), it is computed on first access and then is set as a property on every model. """ related_objects_graph = defaultdict(list) all_models = self.apps.get_models(include_auto_created=True) for model in all_models: opts = model._meta # Abstract model's fields are copied to child models, hence we will # see the fields from the child models. if opts.abstract: continue fields_with_relations = ( f for f in opts._get_fields(reverse=False, include_parents=False) if f.is_relation and f.related_model is not None ) for f in fields_with_relations: if not isinstance(f.remote_field.model, str): related_objects_graph[f.remote_field.model._meta.concrete_model._meta].append(f) for model in all_models: # Set the relation_tree using the internal __dict__. In this way # we avoid calling the cached property. In attribute lookup, # __dict__ takes precedence over a data descriptor (such as # @cached_property). This means that the _meta._relation_tree is # only called if related_objects is not in __dict__. related_objects = related_objects_graph[model._meta.concrete_model._meta] model._meta.__dict__['_relation_tree'] = related_objects # It seems it is possible that self is not in all_models, so guard # against that with default for get(). return self.__dict__.get('_relation_tree', EMPTY_RELATION_TREE)
Example #24
Source File: query.py From python2017 with MIT License | 4 votes |
def get_prefetcher(instance, through_attr, to_attr): """ For the attribute 'through_attr' on the given instance, finds an object that has a get_prefetch_queryset(). Returns a 4 tuple containing: (the object with get_prefetch_queryset (or None), the descriptor object representing this relationship (or None), a boolean that is False if the attribute was not found at all, a boolean that is True if the attribute has already been fetched) """ prefetcher = None is_fetched = False # For singly related objects, we have to avoid getting the attribute # from the object, as this will trigger the query. So we first try # on the class, in order to get the descriptor object. rel_obj_descriptor = getattr(instance.__class__, through_attr, None) if rel_obj_descriptor is None: attr_found = hasattr(instance, through_attr) else: attr_found = True if rel_obj_descriptor: # singly related object, descriptor object has the # get_prefetch_queryset() method. if hasattr(rel_obj_descriptor, 'get_prefetch_queryset'): prefetcher = rel_obj_descriptor if rel_obj_descriptor.is_cached(instance): is_fetched = True else: # descriptor doesn't support prefetching, so we go ahead and get # the attribute on the instance rather than the class to # support many related managers 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): is_fetched = to_attr in instance.__dict__ else: is_fetched = hasattr(instance, to_attr) else: is_fetched = through_attr in instance._prefetched_objects_cache return prefetcher, rel_obj_descriptor, attr_found, is_fetched
Example #25
Source File: query.py From python with Apache License 2.0 | 4 votes |
def get_prefetcher(instance, through_attr, to_attr): """ For the attribute 'through_attr' on the given instance, finds an object that has a get_prefetch_queryset(). Returns a 4 tuple containing: (the object with get_prefetch_queryset (or None), the descriptor object representing this relationship (or None), a boolean that is False if the attribute was not found at all, a boolean that is True if the attribute has already been fetched) """ prefetcher = None is_fetched = False # For singly related objects, we have to avoid getting the attribute # from the object, as this will trigger the query. So we first try # on the class, in order to get the descriptor object. rel_obj_descriptor = getattr(instance.__class__, through_attr, None) if rel_obj_descriptor is None: attr_found = hasattr(instance, through_attr) else: attr_found = True if rel_obj_descriptor: # singly related object, descriptor object has the # get_prefetch_queryset() method. if hasattr(rel_obj_descriptor, 'get_prefetch_queryset'): prefetcher = rel_obj_descriptor if rel_obj_descriptor.is_cached(instance): is_fetched = True else: # descriptor doesn't support prefetching, so we go ahead and get # the attribute on the instance rather than the class to # support many related managers 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): is_fetched = to_attr in instance.__dict__ else: is_fetched = hasattr(instance, to_attr) else: is_fetched = through_attr in instance._prefetched_objects_cache return prefetcher, rel_obj_descriptor, attr_found, is_fetched
Example #26
Source File: query.py From Hands-On-Application-Development-with-PyCharm with MIT License | 4 votes |
def get_prefetcher(instance, through_attr, to_attr): """ For the attribute 'through_attr' on the given instance, find an object that has a get_prefetch_queryset(). Return a 4 tuple containing: (the object with get_prefetch_queryset (or None), the descriptor object representing this relationship (or None), a boolean that is False if the attribute was not found at all, a boolean that is True if the attribute has already been fetched) """ prefetcher = None is_fetched = False # For singly related objects, we have to avoid getting the attribute # from the object, as this will trigger the query. So we first try # on the class, in order to get the descriptor object. rel_obj_descriptor = getattr(instance.__class__, through_attr, None) if rel_obj_descriptor is None: attr_found = hasattr(instance, through_attr) else: attr_found = True if rel_obj_descriptor: # singly related object, descriptor object has the # get_prefetch_queryset() method. if hasattr(rel_obj_descriptor, 'get_prefetch_queryset'): prefetcher = rel_obj_descriptor if rel_obj_descriptor.is_cached(instance): is_fetched = True else: # descriptor doesn't support prefetching, so we go ahead and get # the attribute on the instance rather than the class to # support many related managers 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): is_fetched = to_attr in instance.__dict__ else: is_fetched = hasattr(instance, to_attr) else: is_fetched = through_attr in instance._prefetched_objects_cache return prefetcher, rel_obj_descriptor, attr_found, is_fetched
Example #27
Source File: query.py From bioforum with MIT License | 4 votes |
def get_prefetcher(instance, through_attr, to_attr): """ For the attribute 'through_attr' on the given instance, find an object that has a get_prefetch_queryset(). Return a 4 tuple containing: (the object with get_prefetch_queryset (or None), the descriptor object representing this relationship (or None), a boolean that is False if the attribute was not found at all, a boolean that is True if the attribute has already been fetched) """ prefetcher = None is_fetched = False # For singly related objects, we have to avoid getting the attribute # from the object, as this will trigger the query. So we first try # on the class, in order to get the descriptor object. rel_obj_descriptor = getattr(instance.__class__, through_attr, None) if rel_obj_descriptor is None: attr_found = hasattr(instance, through_attr) else: attr_found = True if rel_obj_descriptor: # singly related object, descriptor object has the # get_prefetch_queryset() method. if hasattr(rel_obj_descriptor, 'get_prefetch_queryset'): prefetcher = rel_obj_descriptor if rel_obj_descriptor.is_cached(instance): is_fetched = True else: # descriptor doesn't support prefetching, so we go ahead and get # the attribute on the instance rather than the class to # support many related managers 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): is_fetched = to_attr in instance.__dict__ else: is_fetched = hasattr(instance, to_attr) else: is_fetched = through_attr in instance._prefetched_objects_cache return prefetcher, rel_obj_descriptor, attr_found, is_fetched