@@ -76,26 +76,41 @@ def _is_reverse_relation(field):
76
76
return isinstance (field , ForeignObjectRel )
77
77
78
78
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
+
80
82
try :
81
83
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
82
87
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
+
89
102
if cache_key in model_cache :
90
103
logger .debug ("ev=model_cache, status=hit, key=%s" , cache_key )
91
- related_instance = model_cache [cache_key ]
104
+ result = model_cache [cache_key ]
92
105
else :
93
106
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
99
114
100
115
101
116
def _get_filtered_field_value ( # noqa: C901
@@ -114,8 +129,11 @@ def _get_filtered_field_value( # noqa: C901
114
129
115
130
if isinstance (field_type , types .FunctionType ):
116
131
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 )
119
137
elif isinstance (field_type , _ExpandableForeignKey ):
120
138
if expand_this :
121
139
inst_field_name = field_type .inst_field_name or field_name
0 commit comments