From 19b3062e7ec2e5a2bdcfaaa33af254bac569ff66 Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Wed, 12 Oct 2016 17:29:34 +0300 Subject: [PATCH 1/3] Allow SerializerMethodResourceRelatedField to be list --- rest_framework_json_api/relations.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rest_framework_json_api/relations.py b/rest_framework_json_api/relations.py index 9762bc74..6c79a6e5 100644 --- a/rest_framework_json_api/relations.py +++ b/rest_framework_json_api/relations.py @@ -171,7 +171,6 @@ def get_choices(self, cutoff=None): ]) - class SerializerMethodResourceRelatedField(ResourceRelatedField): """ Allows us to use serializer method RelatedFields @@ -211,7 +210,7 @@ def get_attribute(self, instance): return super(SerializerMethodResourceRelatedField, self).get_attribute(instance) def to_representation(self, value): - if isinstance(value, QuerySet): + if isinstance(value, list): base = super(SerializerMethodResourceRelatedField, self) return [base.to_representation(x) for x in value] return super(SerializerMethodResourceRelatedField, self).to_representation(value) From 9fd42be861d48b67580edbfd76c0e67d2004e78a Mon Sep 17 00:00:00 2001 From: Anton Shutik Date: Wed, 19 Oct 2016 16:16:41 -0700 Subject: [PATCH 2/3] Make links working on serializermethod field --- rest_framework_json_api/relations.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/rest_framework_json_api/relations.py b/rest_framework_json_api/relations.py index 6c79a6e5..b4eefc45 100644 --- a/rest_framework_json_api/relations.py +++ b/rest_framework_json_api/relations.py @@ -1,6 +1,7 @@ +import collections import json -from rest_framework.fields import MISSING_ERROR_MESSAGE +from rest_framework.fields import MISSING_ERROR_MESSAGE, SerializerMethodField from rest_framework.relations import * from django.utils.translation import ugettext_lazy as _ from django.db.models.query import QuerySet @@ -10,6 +11,8 @@ get_resource_type_from_queryset, get_resource_type_from_instance, \ get_included_serializers, get_resource_type_from_serializer +LINKS_PARAMS = ['self_link_view_name', 'related_link_view_name', 'related_link_lookup_field', 'related_link_url_kwarg'] + class ResourceRelatedField(PrimaryKeyRelatedField): self_link_view_name = None @@ -189,17 +192,20 @@ def __init__(self, child_relation=None, *args, **kwargs): # DRF 3.1 doesn't expect the `many` kwarg kwargs.pop('many', None) model = kwargs.pop('model', None) + if child_relation is not None: + self.child_relation = child_relation if model: self.model = model - super(SerializerMethodResourceRelatedField, self).__init__(child_relation, *args, **kwargs) + super(SerializerMethodResourceRelatedField, self).__init__(*args, **kwargs) @classmethod def many_init(cls, *args, **kwargs): - list_kwargs = {'child_relation': cls(*args, **kwargs)} + list_kwargs = {k: kwargs.pop(k) for k in LINKS_PARAMS if k in kwargs} + list_kwargs['child_relation'] = cls(*args, **kwargs) for key in kwargs.keys(): if key in ('model',) + MANY_RELATION_KWARGS: list_kwargs[key] = kwargs[key] - return SerializerMethodResourceRelatedField(**list_kwargs) + return cls(**list_kwargs) def get_attribute(self, instance): # check for a source fn defined on the serializer instead of the model @@ -210,10 +216,7 @@ def get_attribute(self, instance): return super(SerializerMethodResourceRelatedField, self).get_attribute(instance) def to_representation(self, value): - if isinstance(value, list): + if isinstance(value, collections.Iterable): base = super(SerializerMethodResourceRelatedField, self) return [base.to_representation(x) for x in value] - return super(SerializerMethodResourceRelatedField, self).to_representation(value) - - def get_links(self, obj=None, lookup_field='pk'): - return OrderedDict() + return super(SerializerMethodResourceRelatedField, self).to_representation(value) \ No newline at end of file From 3539739aeee4851a8cccbd82c303e390ffbfc7bd Mon Sep 17 00:00:00 2001 From: Anton Shutik Date: Thu, 20 Oct 2016 12:57:58 -0700 Subject: [PATCH 3/3] Added test for SerializerMethodResourceRelatedField with many=True --- example/serializers.py | 6 +++++- .../integration/test_non_paginated_responses.py | 12 ++++++++++-- example/tests/integration/test_pagination.py | 6 +++++- example/urls_test.py | 4 ++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/example/serializers.py b/example/serializers.py index 7929577b..e20a7c5f 100644 --- a/example/serializers.py +++ b/example/serializers.py @@ -44,7 +44,11 @@ def __init__(self, *args, **kwargs): source='comment_set', many=True, read_only=True) # many related from serializer suggested = relations.SerializerMethodResourceRelatedField( - source='get_suggested', model=Entry, many=True, read_only=True) + source='get_suggested', model=Entry, many=True, read_only=True, + related_link_view_name='entry-suggested', + related_link_url_kwarg='entry_pk', + self_link_view_name='entry-relationships', + ) # single related from serializer featured = relations.SerializerMethodResourceRelatedField( source='get_featured', model=Entry, read_only=True) diff --git a/example/tests/integration/test_non_paginated_responses.py b/example/tests/integration/test_non_paginated_responses.py index de9e3055..d0a9adb0 100644 --- a/example/tests/integration/test_non_paginated_responses.py +++ b/example/tests/integration/test_non_paginated_responses.py @@ -43,7 +43,11 @@ def test_multiple_entries_no_pagination(multiple_entries, rf): "data": [{"type": "comments", "id": "1"}] }, "suggested": { - "data": [{"type": "entries", "id": "2"}] + "data": [{"type": "entries", "id": "2"}], + "links": { + "related": "http://testserver/entries/1/suggested/", + "self": "http://testserver/entries/1/relationships/suggested" + } } } }, @@ -74,7 +78,11 @@ def test_multiple_entries_no_pagination(multiple_entries, rf): "data": [{"type": "comments", "id": "2"}] }, "suggested": { - "data": [{"type": "entries", "id": "1"}] + "data": [{"type": "entries", "id": "1"}], + "links": { + "related": "http://testserver/entries/2/suggested/", + "self": "http://testserver/entries/2/relationships/suggested" + } } } }, diff --git a/example/tests/integration/test_pagination.py b/example/tests/integration/test_pagination.py index 742be523..482ac460 100644 --- a/example/tests/integration/test_pagination.py +++ b/example/tests/integration/test_pagination.py @@ -37,7 +37,11 @@ def test_pagination_with_single_entry(single_entry, client): "data": [{"type": "comments", "id": "1"}] }, "suggested": { - "data": [] + "data": [], + "links": { + "related": "http://testserver/entries/1/suggested/", + "self": "http://testserver/entries/1/relationships/suggested" + } } } }], diff --git a/example/urls_test.py b/example/urls_test.py index 0f8ed73b..2804863d 100644 --- a/example/urls_test.py +++ b/example/urls_test.py @@ -23,6 +23,10 @@ GenericIdentity.as_view(), name='user-default'), + url(r'^entries/(?P[^/.]+)/suggested/', + EntryViewSet.as_view({'get': 'list'}), + name='entry-suggested' + ), url(r'^entries/(?P[^/.]+)/relationships/(?P\w+)', EntryRelationshipView.as_view(), name='entry-relationships'),