Skip to content

Commit 6334ceb

Browse files
committed
Issue django-json-api#155 Defer to lookup_field/custom id fields
TODO Test coverage There are so many places where PK is assumed in djangorestframework-jsonapi and so many places/ways to change IDs/URLs in DRF itself, that getting coverage on all the edge cases will be quite a lot of work. Some of that work may be better spent re-factoring things to make the code easier to understand and reduce the number of edge cases in the first place.
1 parent 10f818e commit 6334ceb

File tree

1 file changed

+37
-12
lines changed

1 file changed

+37
-12
lines changed

rest_framework_json_api/renderers.py

+37-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from collections import OrderedDict
66

77
import inflection
8-
from django.db.models import Manager, QuerySet
8+
from django.db.models import Manager
99
from django.utils import six, encoding
1010
from rest_framework import relations
1111
from rest_framework import renderers
@@ -111,8 +111,10 @@ def get_pk(obj):
111111
if relation_instance is not None else list()
112112

113113
for related_object in relation_queryset:
114-
relation_data.append(
115-
OrderedDict([('type', relation_type), ('id', encoding.force_text(get_pk(related_object)))])
114+
relation_data.append(OrderedDict([
115+
('type', relation_type),
116+
('id', encoding.force_text(getattr(
117+
related_object, field.lookup_field)))])
116118
)
117119

118120
data.update({field_name: {
@@ -144,12 +146,18 @@ def get_pk(obj):
144146
continue
145147

146148
if isinstance(field, (relations.PrimaryKeyRelatedField, relations.HyperlinkedRelatedField)):
147-
resolved, relation = utils.get_relation_instance(resource_instance, '%s_id' % source, field.parent)
148-
if not resolved:
149-
continue
150-
relation_id = get_pk(
151-
getattr(resource_instance, source)
152-
) if resource.get(field_name) else None
149+
lookup_field = getattr(field, 'lookup_field', None)
150+
if lookup_field:
151+
relation_id = getattr(
152+
getattr(resource_instance, source), lookup_field)
153+
else:
154+
resolved, relation = utils.get_relation_instance(
155+
resource_instance, source, field.parent)
156+
if not resolved:
157+
continue
158+
relation_id = get_pk(
159+
getattr(resource_instance, source)
160+
) if resource.get(field_name) else None
153161
relation_data = {
154162
'data': (
155163
OrderedDict([('type', relation_type), ('id', encoding.force_text(relation_id))])
@@ -198,7 +206,9 @@ def get_pk(obj):
198206

199207
relation_data.append(OrderedDict([
200208
('type', nested_resource_instance_type),
201-
('id', encoding.force_text(get_pk(nested_resource_instance)))
209+
('id', encoding.force_text(getattr(
210+
nested_resource_instance,
211+
field.child_relation.lookup_field)))
202212
]))
203213
data.update({
204214
field_name: {
@@ -228,7 +238,9 @@ def get_pk(obj):
228238
utils.get_resource_type_from_instance(nested_resource_instance)
229239
)
230240

231-
instance_pk_name = nested_resource_instance._meta.pk.name
241+
instance_pk_name = (
242+
getattr(field, 'lookup_field', None) or
243+
nested_resource_instance._meta.pk.name)
232244
if instance_pk_name in nested_resource_data:
233245
pk = nested_resource_data[instance_pk_name]
234246
else:
@@ -247,12 +259,19 @@ def get_pk(obj):
247259
if not resolved:
248260
continue
249261

262+
url_field_name = getattr(field, 'url_field_name', 'url')
263+
if url_field_name in field.fields:
264+
id_ = getattr(
265+
relation_instance,
266+
field[url_field_name].lookup_field)
267+
else:
268+
id_ = get_pk(relation_instance)
250269
data.update({
251270
field_name: {
252271
'data': (
253272
OrderedDict([
254273
('type', relation_type),
255-
('id', get_pk(relation_instance))
274+
('id', encoding.force_text(id_))
256275
]) if resource.get(field_name) else None)
257276
}
258277
})
@@ -402,6 +421,12 @@ def extract_root_meta(cls, serializer, resource):
402421
def build_json_resource_obj(cls, fields, resource, resource_instance, resource_name):
403422
if resource_instance is None:
404423
pk = None
424+
elif 'url' in fields:
425+
pk = getattr(resource_instance, fields['url'].lookup_field)
426+
pk = encoding.force_text(pk)
427+
elif 'id' in fields:
428+
pk = fields['id'].get_attribute(resource_instance)
429+
pk = encoding.force_text(pk)
405430
else:
406431
# Check if the primary key exists in the resource by getting the primary keys attribute name.
407432
pk_attr = resource_instance._meta.pk.name

0 commit comments

Comments
 (0)