@@ -76,26 +76,41 @@ def _is_reverse_relation(field):
7676 return isinstance (field , ForeignObjectRel )
7777
7878
79- def _cache_related_instance (inst , field_name , model_cache ):
79+ def _get_callable_field_value_with_cache (inst , field_name , model_cache , field_type ):
80+ cache_relation = True
81+
8082 try :
8183 field_meta = inst ._meta .get_field (field_name )
84+ if not _is_relation (field_meta ) or _is_reverse_relation (field_meta ):
85+ # no `attname` in reverse relations
86+ cache_relation = False
8287 except (AttributeError , FieldDoesNotExist ):
83- return
84- if not _is_relation (field_meta ) or _is_reverse_relation (field_meta ):
85- return # no `attname` in reverse relations
86- val_pk = getattr (inst , field_meta .attname )
87- val_cls = field_meta .related_model
88- cache_key = _make_model_cache_key (val_cls , val_pk )
88+ # inst doesn't look like a django model
89+ cache_relation = False
90+
91+ if cache_relation :
92+ # we're caching a foreign key field on a django model. Cache it by (model, fk_pk) so that if
93+ # other objects reference this same instance, we'll get a cache hit
94+ fk_pk = getattr (inst , field_meta .attname )
95+ val_cls = field_meta .related_model
96+ cache_key = _make_model_cache_key (val_cls , fk_pk )
97+ else :
98+ # not a foreign key. Cache it by (inst, field_name) - it won't be a cache hit on another instance, but
99+ # will be cached if this same inst is returned later in the response
100+ cache_key = (inst , field_name )
101+
89102 if cache_key in model_cache :
90103 logger .debug ("ev=model_cache, status=hit, key=%s" , cache_key )
91- related_instance = model_cache [cache_key ]
104+ result = model_cache [cache_key ]
92105 else :
93106 logger .debug ("ev=model_cache, status=miss, key=%s" , cache_key )
94- related_instance = getattr (inst , field_name )
95- if not isinstance (related_instance , val_cls ):
96- return
97- model_cache [cache_key ] = related_instance
98- setattr (inst , field_name , related_instance )
107+ result = field_type (inst )
108+
109+ if isinstance (result , models .Manager ):
110+ # need to get an iterable to proceed
111+ result = result .all ()
112+ model_cache [cache_key ] = result
113+ return result
99114
100115
101116def _get_filtered_field_value ( # noqa: C901
@@ -114,8 +129,11 @@ def _get_filtered_field_value( # noqa: C901
114129
115130 if isinstance (field_type , types .FunctionType ):
116131 if is_caching_enabled ():
117- _cache_related_instance (inst , field_name , model_cache )
118- val = field_type (inst )
132+ val = _get_callable_field_value_with_cache (
133+ inst , field_name , model_cache , field_type
134+ )
135+ else :
136+ val = field_type (inst )
119137 elif isinstance (field_type , _ExpandableForeignKey ):
120138 if expand_this :
121139 inst_field_name = field_type .inst_field_name or field_name
0 commit comments