Skip to content

Commit 0aedffb

Browse files
knaperekjerel
authored andcommitted
Support regular serializers
* initial fix for regular serializer relations * fixes to resolve correct resource_name * prioritize serializer resource_name * minor fix to included serializers
1 parent c93dea1 commit 0aedffb

File tree

3 files changed

+91
-66
lines changed

3 files changed

+91
-66
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pip-delete-this-directory.txt
2828
# Pycharm project files
2929
.idea/
3030

31+
# PyTest cache
32+
.cache/
33+
3134
# Tox
3235
.tox/
3336

rest_framework_json_api/renderers.py

+42-30
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
from collections import OrderedDict
66

77
import inflection
8+
from django.db.models import Manager, QuerySet
89
from django.utils import six, encoding
910
from rest_framework import relations
1011
from rest_framework import renderers
11-
from rest_framework.serializers import BaseSerializer, ListSerializer, ModelSerializer
12+
from rest_framework.serializers import BaseSerializer, Serializer, ListSerializer, ModelSerializer
1213
from rest_framework.settings import api_settings
1314

1415
from . import utils
@@ -88,25 +89,28 @@ def extract_relationships(fields, resource, resource_instance):
8889

8990
source = field.source
9091
try:
91-
relation_instance_or_manager = getattr(resource_instance, source)
92+
relation_instance = getattr(resource_instance, source)
9293
except AttributeError:
9394
# if the field is not defined on the model then we check the serializer
9495
# and if no value is there we skip over the field completely
9596
serializer_method = getattr(field.parent, source, None)
9697
if serializer_method and hasattr(serializer_method, '__call__'):
97-
relation_instance_or_manager = serializer_method(resource_instance)
98+
relation_instance = serializer_method(resource_instance)
9899
else:
99100
continue
100101

102+
if isinstance(relation_instance, Manager):
103+
relation_instance = relation_instance.all()
104+
101105
relation_type = utils.get_related_resource_type(field)
102106

103107
if isinstance(field, relations.HyperlinkedIdentityField):
104108
# special case for HyperlinkedIdentityField
105109
relation_data = list()
106110

107111
# Don't try to query an empty relation
108-
relation_queryset = relation_instance_or_manager.all() \
109-
if relation_instance_or_manager is not None else list()
112+
relation_queryset = relation_instance \
113+
if relation_instance is not None else list()
110114

111115
for related_object in relation_queryset:
112116
relation_data.append(
@@ -138,7 +142,7 @@ def extract_relationships(fields, resource, resource_instance):
138142
continue
139143

140144
if isinstance(field, (relations.PrimaryKeyRelatedField, relations.HyperlinkedRelatedField)):
141-
relation_id = relation_instance_or_manager.pk if resource.get(field_name) else None
145+
relation_id = relation_instance.pk if resource.get(field_name) else None
142146

143147
relation_data = {
144148
'data': (
@@ -177,11 +181,15 @@ def extract_relationships(fields, resource, resource_instance):
177181
continue
178182

179183
relation_data = list()
180-
for related_object in relation_instance_or_manager.all():
181-
related_object_type = utils.get_instance_or_manager_resource_type(related_object)
184+
for nested_resource_instance in relation_instance:
185+
nested_resource_instance_type = (
186+
relation_type or
187+
utils.get_resource_type_from_instance(nested_resource_instance)
188+
)
189+
182190
relation_data.append(OrderedDict([
183-
('type', related_object_type),
184-
('id', encoding.force_text(related_object.pk))
191+
('type', nested_resource_instance_type),
192+
('id', encoding.force_text(nested_resource_instance.pk))
185193
]))
186194
data.update({
187195
field_name: {
@@ -193,15 +201,19 @@ def extract_relationships(fields, resource, resource_instance):
193201
})
194202
continue
195203

196-
if isinstance(field, ListSerializer):
204+
if isinstance(field, ListSerializer) and relation_instance is not None:
197205
relation_data = list()
198206

199207
serializer_data = resource.get(field_name)
200-
resource_instance_queryset = list(relation_instance_or_manager.all())
208+
resource_instance_queryset = list(relation_instance)
201209
if isinstance(serializer_data, list):
202210
for position in range(len(serializer_data)):
203211
nested_resource_instance = resource_instance_queryset[position]
204-
nested_resource_instance_type = utils.get_resource_type_from_instance(nested_resource_instance)
212+
nested_resource_instance_type = (
213+
relation_type or
214+
utils.get_resource_type_from_instance(nested_resource_instance)
215+
)
216+
205217
relation_data.append(OrderedDict([
206218
('type', nested_resource_instance_type),
207219
('id', encoding.force_text(nested_resource_instance.pk))
@@ -210,16 +222,13 @@ def extract_relationships(fields, resource, resource_instance):
210222
data.update({field_name: {'data': relation_data}})
211223
continue
212224

213-
if isinstance(field, ModelSerializer):
214-
relation_model = field.Meta.model
215-
relation_type = utils.format_resource_type(relation_model.__name__)
216-
225+
if isinstance(field, Serializer):
217226
data.update({
218227
field_name: {
219228
'data': (
220229
OrderedDict([
221230
('type', relation_type),
222-
('id', encoding.force_text(relation_instance_or_manager.pk))
231+
('id', encoding.force_text(relation_instance.pk))
223232
]) if resource.get(field_name) else None)
224233
}
225234
})
@@ -258,38 +267,41 @@ def extract_included(fields, resource, resource_instance, included_resources):
258267
continue
259268

260269
try:
261-
relation_instance_or_manager = getattr(resource_instance, field_name)
270+
relation_instance = getattr(resource_instance, field_name)
262271
except AttributeError:
263272
try:
264273
# For ManyRelatedFields if `related_name` is not set we need to access `foo_set` from `source`
265-
relation_instance_or_manager = getattr(resource_instance, field.child_relation.source)
274+
relation_instance = getattr(resource_instance, field.child_relation.source)
266275
except AttributeError:
267276
if not hasattr(current_serializer, field.source):
268277
continue
269278
serializer_method = getattr(current_serializer, field.source)
270-
relation_instance_or_manager = serializer_method(resource_instance)
279+
relation_instance = serializer_method(resource_instance)
280+
281+
if isinstance(relation_instance, Manager):
282+
relation_instance = relation_instance.all()
271283

272284
new_included_resources = [key.replace('%s.' % field_name, '', 1)
273285
for key in included_resources
274286
if field_name == key.split('.')[0]]
275287
serializer_data = resource.get(field_name)
276288

277289
if isinstance(field, relations.ManyRelatedField):
278-
serializer_class = included_serializers.get(field_name)
279-
field = serializer_class(relation_instance_or_manager.all(), many=True, context=context)
290+
serializer_class = included_serializers[field_name]
291+
field = serializer_class(relation_instance, many=True, context=context)
280292
serializer_data = field.data
281293

282294
if isinstance(field, relations.RelatedField):
283-
serializer_class = included_serializers.get(field_name)
284-
if relation_instance_or_manager is None:
295+
if relation_instance is None:
285296
continue
286-
field = serializer_class(relation_instance_or_manager, context=context)
297+
serializer_class = included_serializers[field_name]
298+
field = serializer_class(relation_instance, context=context)
287299
serializer_data = field.data
288300

289301
if isinstance(field, ListSerializer):
290302
serializer = field.child
291303
relation_type = utils.get_resource_type_from_serializer(serializer)
292-
relation_queryset = list(relation_instance_or_manager.all())
304+
relation_queryset = list(relation_instance)
293305

294306
# Get the serializer fields
295307
serializer_fields = utils.get_serializer_fields(serializer)
@@ -312,7 +324,7 @@ def extract_included(fields, resource, resource_instance, included_resources):
312324
)
313325
)
314326

315-
if isinstance(field, ModelSerializer):
327+
if isinstance(field, Serializer):
316328

317329
relation_type = utils.get_resource_type_from_serializer(field)
318330

@@ -322,11 +334,11 @@ def extract_included(fields, resource, resource_instance, included_resources):
322334
included_data.append(
323335
JSONRenderer.build_json_resource_obj(
324336
serializer_fields, serializer_data,
325-
relation_instance_or_manager, relation_type)
337+
relation_instance, relation_type)
326338
)
327339
included_data.extend(
328340
JSONRenderer.extract_included(
329-
serializer_fields, serializer_data, relation_instance_or_manager, new_included_resources
341+
serializer_fields, serializer_data, relation_instance, new_included_resources
330342
)
331343
)
332344

rest_framework_json_api/utils.py

+46-36
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ def format_resource_type(value, format_type=None, pluralize=None):
162162

163163

164164
def get_related_resource_type(relation):
165+
try:
166+
return get_resource_type_from_serializer(relation)
167+
except AttributeError:
168+
pass
169+
170+
relation_model = None
165171
if hasattr(relation, '_meta'):
166172
relation_model = relation._meta.model
167173
elif hasattr(relation, 'model'):
@@ -171,41 +177,39 @@ def get_related_resource_type(relation):
171177
relation_model = relation.get_queryset().model
172178
else:
173179
parent_serializer = relation.parent
180+
parent_model = None
174181
if hasattr(parent_serializer, 'Meta'):
175-
parent_model = parent_serializer.Meta.model
176-
else:
177-
parent_model = parent_serializer.parent.Meta.model
178-
179-
if relation.source:
180-
if relation.source != '*':
181-
parent_model_relation = getattr(parent_model, relation.source)
182+
parent_model = getattr(parent_serializer.Meta, 'model', None)
183+
elif hasattr(parent_serializer, 'parent') and hasattr(parent_serializer.parent, 'Meta'):
184+
parent_model = getattr(parent_serializer.parent.Meta, 'model', None)
185+
186+
if parent_model is not None:
187+
if relation.source:
188+
if relation.source != '*':
189+
parent_model_relation = getattr(parent_model, relation.source)
190+
else:
191+
parent_model_relation = getattr(parent_model, relation.field_name)
182192
else:
183-
parent_model_relation = getattr(parent_model, relation.field_name)
184-
else:
185-
parent_model_relation = getattr(parent_model, parent_serializer.field_name)
186-
187-
if hasattr(parent_model_relation, 'related'):
188-
try:
189-
relation_model = parent_model_relation.related.related_model
190-
except AttributeError:
191-
# Django 1.7
192-
relation_model = parent_model_relation.related.model
193-
elif hasattr(parent_model_relation, 'field'):
194-
try:
195-
relation_model = parent_model_relation.field.remote_field.model
196-
except AttributeError:
197-
relation_model = parent_model_relation.field.related.model
198-
else:
199-
return get_related_resource_type(parent_model_relation)
200-
return get_resource_type_from_model(relation_model)
193+
parent_model_relation = getattr(parent_model, parent_serializer.field_name)
194+
195+
if hasattr(parent_model_relation, 'related'):
196+
try:
197+
relation_model = parent_model_relation.related.related_model
198+
except AttributeError:
199+
# Django 1.7
200+
relation_model = parent_model_relation.related.model
201+
elif hasattr(parent_model_relation, 'field'):
202+
try:
203+
relation_model = parent_model_relation.field.remote_field.model
204+
except AttributeError:
205+
relation_model = parent_model_relation.field.related.model
206+
else:
207+
return get_related_resource_type(parent_model_relation)
201208

209+
if relation_model is None:
210+
raise APIException(_('Could not resolve resource type for relation %s' % relation))
202211

203-
def get_instance_or_manager_resource_type(resource_instance_or_manager):
204-
if hasattr(resource_instance_or_manager, 'model'):
205-
return get_resource_type_from_manager(resource_instance_or_manager)
206-
if hasattr(resource_instance_or_manager, '_meta'):
207-
return get_resource_type_from_instance(resource_instance_or_manager)
208-
pass
212+
return get_resource_type_from_model(relation_model)
209213

210214

211215
def get_resource_type_from_model(model):
@@ -221,18 +225,24 @@ def get_resource_type_from_queryset(qs):
221225

222226

223227
def get_resource_type_from_instance(instance):
224-
return get_resource_type_from_model(instance._meta.model)
228+
if hasattr(instance, '_meta'):
229+
return get_resource_type_from_model(instance._meta.model)
225230

226231

227232
def get_resource_type_from_manager(manager):
228233
return get_resource_type_from_model(manager.model)
229234

230235

231236
def get_resource_type_from_serializer(serializer):
232-
if hasattr(serializer.Meta, 'resource_name'):
233-
return serializer.Meta.resource_name
234-
else:
235-
return get_resource_type_from_model(serializer.Meta.model)
237+
json_api_meta = getattr(serializer, 'JSONAPIMeta', None)
238+
meta = getattr(serializer, 'Meta', None)
239+
if hasattr(json_api_meta, 'resource_name'):
240+
return json_api_meta.resource_name
241+
elif hasattr(meta, 'resource_name'):
242+
return meta.resource_name
243+
elif hasattr(meta, 'model'):
244+
return get_resource_type_from_model(meta.model)
245+
raise AttributeError()
236246

237247

238248
def get_default_included_resources_from_serializer(serializer):

0 commit comments

Comments
 (0)