Skip to content

Commit 8b8a6b1

Browse files
committed
initial commit
0 parents  commit 8b8a6b1

File tree

6 files changed

+208
-0
lines changed

6 files changed

+208
-0
lines changed

README.rst

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
===============================
2+
Ember and Django Rest Framework
3+
===============================
4+
5+
EmberJS is extremely opinionated on how JSON REST requests and responses
6+
should look. Going against the grain can lead to frustrated Javascript
7+
developers.
8+
9+
By default, Django REST Framework will produce a response like::
10+
11+
{
12+
"id": 1,
13+
"username": "john",
14+
"full_name": "John Coltrane"
15+
}
16+
17+
18+
However, if this is an ``identity`` model in EmberJS, Ember expects a
19+
response to look like the following::
20+
21+
{
22+
"identity": {
23+
"id": 1,
24+
"username": "john",
25+
"full_name": "John Coltrane"
26+
}
27+
}
28+
29+
30+
------------
31+
Requirements
32+
------------
33+
34+
1. Django
35+
2. Django REST Framework
36+
37+
------------
38+
Installation
39+
------------
40+
41+
::
42+
43+
pip install rest_framework_ember
44+
45+
46+
-----
47+
Usage
48+
-----
49+
50+
51+
``rest_framework_ember`` assumes you are using class-based views in Django
52+
Rest Framework.
53+
54+
55+
Settings
56+
^^^^^^^^
57+
58+
One can either add ``rest_framework_ember.parsers.EmberJSONParser`` and
59+
``rest_framework_ember.renderers.JSONRenderer`` to each ``ViewSet`` class, or
60+
override ``settings.REST_FRAMEWORK``::
61+
62+
63+
REST_FRAMEWORK = {
64+
'DEFAULT_PARSER_CLASSES': (
65+
'rest_framework_ember.parsers.EmberJSONParser',
66+
'rest_framework.parsers.FormParser',
67+
'rest_framework.parsers.MultiPartParser'
68+
),
69+
'DEFAULT_RENDERER_CLASSES': (
70+
'rest_framework_ember.renderers.JSONRenderer',
71+
'rest_framework.renderers.BrowsableAPIRenderer',
72+
),
73+
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',
74+
}
75+
76+
77+
resource_name property
78+
^^^^^^^^^^^^^^^^^^^^^^
79+
80+
On resources that do not subclass ``rest_framework.viewsets.ModelViewSet``,
81+
the ``resource_name`` property is required on the class.::
82+
83+
class Me(generics.GenericAPIView):
84+
"""
85+
Current user's identity endpoint.
86+
87+
GET /me
88+
"""
89+
resource_name = 'data'
90+
serializer_class = identity_serializers.IdentitySerializer
91+
allowed_methods = ['GET']
92+
permission_classes = (permissions.IsAuthenticated, )
93+
94+
95+
96+

rest_framework_ember/__init__.py

Whitespace-only changes.

rest_framework_ember/parsers.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Parsers
3+
"""
4+
from rest_framework.parsers import JSONParser
5+
6+
from rest_framework_ember.utils import get_resource_name
7+
8+
9+
class EmberJSONParser(JSONParser):
10+
"""
11+
By default, EmberJS sends a payload that looks like the following::
12+
13+
{
14+
"identity": {
15+
"id": 1,
16+
"first_name": "John",
17+
"last_name": "Coltrane"
18+
}
19+
}
20+
21+
So we can work with the grain on both Ember and RestFramework,
22+
Do some tweaks to the payload so DRF gets what it expects.
23+
"""
24+
25+
def parse(self, stream, media_type=None, parser_context=None):
26+
"""
27+
Parses the incoming bytestream as JSON and returns the resulting data
28+
"""
29+
result = super(EmberJSONParser, self).parse(
30+
stream, media_type=None, parser_context=None)
31+
32+
resource_name = get_resource_name(parser_context.get('view', None))
33+
return result.get(resource_name)
34+

rest_framework_ember/renderers.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import copy
2+
from rest_framework import renderers
3+
4+
from rest_framework_ember.utils import get_resource_name
5+
6+
7+
class JSONRenderer(renderers.JSONRenderer):
8+
"""
9+
Render a JSON response the way Ember Data wants it. Such as:
10+
{
11+
"company": {
12+
"id": 1,
13+
"name": "nGen Works",
14+
"slug": "ngen-works",
15+
"date_created": "2014-03-13 16:33:37"
16+
}
17+
}
18+
"""
19+
def render(self, data, accepted_media_type=None, renderer_context=None):
20+
view = renderer_context.get('view')
21+
22+
resource_name = get_resource_name(view)
23+
24+
try:
25+
data_copy = copy.copy(data)
26+
content = data_copy.pop('results')
27+
data = {resource_name : content, "meta" : data_copy}
28+
except (TypeError, KeyError, AttributeError) as e:
29+
data = {resource_name : data}
30+
return super(JSONRenderer, self).render(
31+
data, accepted_media_type, renderer_context)
32+

rest_framework_ember/utils.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
Resource name utilities.
3+
"""
4+
5+
def get_resource_name(view):
6+
"""
7+
Return the name of a resource
8+
"""
9+
try:
10+
# is the resource name set directly on the view?
11+
resource_name = getattr(view, 'resource_name')
12+
except AttributeError:
13+
try:
14+
# was it set in the serializer Meta class?
15+
resource_name = getattr(view, 'serializer_class')\
16+
.Meta.resource_name
17+
except AttributeError:
18+
# camelCase the name of the model if it hasn't been set
19+
# in either of the other places
20+
try:
21+
name = resource_name = getattr(view, 'serializer_class')\
22+
.Meta.model.__name__
23+
except AttributeError:
24+
try:
25+
name = view.model.__name__
26+
except AttributeError:
27+
name = view.__class__.__name__
28+
29+
resource_name = name[:1].lower() + name[1:]
30+
31+
return resource_name
32+

setup.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env python
2+
from setuptools import setup, find_packages
3+
4+
setup(
5+
name='rest_framework_ember',
6+
version='1.0',
7+
description="Make EmberJS and Django Rest Framework play nice together.",
8+
author="nGen Works",
9+
author_email='[email protected]',
10+
url='http://www.ngenworks.com',
11+
packages=find_packages(),
12+
package_data={'rest_framework_ember': []},
13+
)
14+

0 commit comments

Comments
 (0)