Is there a way to check whether a related object is already fetched?
Since Django 2.0 you can easily check for all fetched relation by:
obj._state.fields_cache
ModelStateFieldsCacheDescriptor
is responsible for storing your cached relations.
>>> Person.objects.first()._state.fields_cache
{}
>>> Person.objects.select_related('address').first()._state.fields_cache
{'address': <Address: Your Address>}
Per this comment on the ticket linked in the comment by @jaap3 above, the recommended way to do this for Django 3+ (perhaps 2+?) is to use the undocumented is_cached
method on the model's field, which comes from this internal mixin:
>>> person1 = Person.objects.first()
>>> Person.address.is_cached(person1)
False
>>> person2 = Person.objects.select_related('address').last()
>>> Person.address.is_cached(person2)
True
If the address
relation has been fetched, then the Person object will have a populated attribute called _address_cache
; you can check this.
def is_fetched(obj, relation_name):
cache_name = '_{}_cache'.format(relation_name)
return getattr(obj, cache_name, False)
Note you'd need to call this with the object and the name of the relation:
is_fetched(person, 'address')
since doing person.address
would trigger the fetch immediately.
Edit reverse or many-to-many relations can only be fetched by prefetch_related
; that populates a single attribute, _prefetched_objects_cache
, which is a dict of lists where the key is the name of the related model. Eg if you do:
addresses = Address.objects.prefetch_related('person_set')
then each item in addresses
will have a _prefetched_objects_cache
dict containing a "person'
key.
Note, both of these are single-underscore attributes which means they are part of the private API; you're free to use them, but Django is also free to change them in future releases.