From a662a2a6326ca87ac2cded5e5cb2f87b35fa4789 Mon Sep 17 00:00:00 2001 From: Greg Aker Date: Mon, 8 Sep 2014 13:15:59 -0500 Subject: [PATCH 1/4] Sideloading resource documentation --- README.rst | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index be84bcce..833be221 100644 --- a/README.rst +++ b/README.rst @@ -117,12 +117,12 @@ override ``settings.REST_FRAMEWORK``:: } + If ``PAGINATE_BY`` is set the renderer will return a ``meta`` object with record count and the next and previous links. Django Rest Framework looks for the ``page`` GET parameter by default allowing you to make requests for subsets of the data with ``this.store.find('identity', {page: 2});``. - resource_name property ^^^^^^^^^^^^^^^^^^^^^^ @@ -216,16 +216,35 @@ To display a specific error inline use the following:: {{/each}} {{input name="title" value=title}} -====== + +--------------------- +Sideloading Resources +--------------------- + +If you are using the JSON Renderer globally, this can lead to issues +when hitting endpoints that are intended to sideload other objects. + +For example:: + + { + "users": [], + "cars": [] + } + + +Set the ``resource_name`` property on the object to ``False``, and the data +will be returned as it is above. + + +------ Mixins -====== +------ The following mixin classes are available to use with Rest Framework resources. -------------------------------------------- rest_framework_ember.mixins.MultipleIDMixin -------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Overrides ``get_queryset`` to filter by ``ids[]`` in URL query params. From d28f3a9b6049f22ac971a6aa87e47a121592457c Mon Sep 17 00:00:00 2001 From: Greg Aker Date: Mon, 8 Sep 2014 13:21:00 -0500 Subject: [PATCH 2/4] Failing sideload resources test. --- example/api/api.py | 37 ++++++++++++++++++++++++ example/api/urls.py | 12 ++++++-- example/tests/test_sideload_resources.py | 25 ++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 example/tests/test_sideload_resources.py diff --git a/example/api/api.py b/example/api/api.py index c356d328..b9d50a00 100644 --- a/example/api/api.py +++ b/example/api/api.py @@ -19,6 +19,21 @@ class Meta: 'id', 'first_name', 'last_name', 'email', ) +class CarSerializer(serializers.Serializer): + """ + Cars serializer + """ + name = serializers.CharField(max_length=50) + + +class UserCarSerializer(serializers.Serializer): + """ + Serializer that returns a list of users & cars. + """ + users = IdentitySerializer(many=True) + cars = CarSerializer(many=True) + + class User(generics.GenericAPIView): """ Current user's identity endpoint. @@ -63,3 +78,25 @@ class EmberDataMixinUserModelViewSet(mixins.MultipleIDMixin, queryset = auth_models.User.objects.all() + +class UserCarResource(UserEmber): + """ + Resource that returns a list of users and cars. + """ + resource_name = False + + cars = [ + {'id': 1, 'name': 'BMW'}, + {'id': 2, 'name': 'Mercedes'}, + {'id': 3, 'name': 'Mini'}, + {'id': 4, 'name': 'Ford'} + ] + + def get(self, request, *args, **kwargs): + data = { + 'users': self.get_queryset(), + 'cars': self.cars + } + serializer = UserCarSerializer(data) + return Response(serializer.data) + diff --git a/example/api/urls.py b/example/api/urls.py index 60fc8049..5ab986bd 100644 --- a/example/api/urls.py +++ b/example/api/urls.py @@ -2,19 +2,25 @@ Example app URLs """ from django.conf.urls import patterns, include, url -from .api import User, UserEmber, EmberUserModelViewSet, EmberDataMixinUserModelViewSet +from .api import ( + User, UserEmber, EmberUserModelViewSet, EmberDataMixinUserModelViewSet, + UserCarResource) from rest_framework import routers urlpatterns = patterns('', url(r'^user-default/(?P\d+)/$', User.as_view(), name='user-default'), url(r'^user-ember/(?P\d+)/$', UserEmber.as_view(), name='user-ember'), - url(r'^user-mixin-viewset/$', EmberDataMixinUserModelViewSet.as_view({'get': 'list'}), + url(r'^user-mixin-viewset/$', + EmberDataMixinUserModelViewSet.as_view({'get': 'list'}), name='mixin-user-list'), url(r'^user-viewset/$', EmberUserModelViewSet.as_view({'get': 'list'}), name='user-list'), url(r'^user-viewset/(?P\d+)/$', EmberUserModelViewSet.as_view( - {'get': 'retrieve', 'post': 'create', 'put': 'update'}), name='user-detail'), + {'get': 'retrieve', 'post': 'create', 'put': 'update'}), + name='user-detail'), + url(r'users-cars/$', UserCarResource.as_view(), + name='users-cars'), ) diff --git a/example/tests/test_sideload_resources.py b/example/tests/test_sideload_resources.py new file mode 100644 index 00000000..8b67e2c7 --- /dev/null +++ b/example/tests/test_sideload_resources.py @@ -0,0 +1,25 @@ +""" +Test sideloading resources +""" +import json +from example.tests import TestBase +from django.core.urlresolvers import reverse_lazy +from django.conf import settings + + +class SideloadResourceTest(TestBase): + """ + Test that sideloading resources returns expected output. + """ + url = reverse_lazy('users-cars') + + def test_get_sideloaded_data(self): + """ + Ensure resources that are meant for sideloaded data + do not return a single root key. + """ + response = self.client.get(self.url) + content = json.loads(response.content) + + self.assertEqual(content.keys(), ['cars', 'users']) + From 14a6dc445b544febffa3f4eb3f67cf670d2b4c83 Mon Sep 17 00:00:00 2001 From: Greg Aker Date: Mon, 8 Sep 2014 13:23:53 -0500 Subject: [PATCH 3/4] Tabbing fix --- rest_framework_ember/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework_ember/utils.py b/rest_framework_ember/utils.py index 3cac0dd5..74da41f9 100644 --- a/rest_framework_ember/utils.py +++ b/rest_framework_ember/utils.py @@ -4,8 +4,8 @@ def get_resource_name(view): """ -Return the name of a resource -""" + Return the name of a resource + """ try: # is the resource name set directly on the view? resource_name = getattr(view, 'resource_name') From 621ae7f7ff7d4b81af192ded1beec193748cfd90 Mon Sep 17 00:00:00 2001 From: Greg Aker Date: Mon, 8 Sep 2014 13:24:00 -0500 Subject: [PATCH 4/4] Return data when ``resource_name`` == False --- rest_framework_ember/renderers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rest_framework_ember/renderers.py b/rest_framework_ember/renderers.py index 97e86dd1..8bc22db3 100644 --- a/rest_framework_ember/renderers.py +++ b/rest_framework_ember/renderers.py @@ -21,6 +21,10 @@ def render(self, data, accepted_media_type=None, renderer_context=None): resource_name = get_resource_name(view) + if resource_name == False: + return super(JSONRenderer, self).render( + data, accepted_media_type, renderer_context) + try: data_copy = copy.copy(data) content = data_copy.pop('results')