Skip to content

Commit f02c7c4

Browse files
committed
Merge remote-tracking branch 'upstream/master' into related_disable_pk_only
2 parents 5dc8e45 + bda8f63 commit f02c7c4

File tree

11 files changed

+253
-26
lines changed

11 files changed

+253
-26
lines changed

.travis.yml

+3-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ cache: pip
44
# Favor explicit over implicit and use an explicit build matrix.
55
matrix:
66
include:
7+
- python: 3.6
8+
env: TOXENV=flake8
9+
710
- python: 2.7
811
env: TOXENV=py27-df11-django111-drf36
912
- python: 2.7
@@ -43,11 +46,6 @@ matrix:
4346
env: TOXENV=py36-df20-django20-drf37
4447
- python: 3.6
4548
env: TOXENV=py36-df20-django20-drf38
46-
47-
- python: 3.6
48-
env: TOXENV=flake8
49-
- python: 3.6
50-
env: TOXENV=isort
5149
install:
5250
- pip install tox
5351
script:

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ Roberto Barreda <roberto.barreda@gmail.com>
1818
santiavenda <santiavenda2@gmail.com>
1919
Tim Selman <timcbaoth@gmail.com>
2020
Yaniv Peer <yanivpeer@gmail.com>
21+
Mohammed Ali Zubair <mazg1493@gmail.com>

README.rst

+3-4
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,18 @@ Running the example app
126126
Browse to http://localhost:8000
127127

128128

129-
Running Tests
130-
^^^^^^^^^^^^^
129+
Running Tests and linting
130+
^^^^^^^^^^^^^^^^^^^^^^^^^
131131

132132
It is recommended to create a virtualenv for testing. Assuming it is already
133133
installed and activated:
134134

135135
::
136136

137-
$ pip install -e .
138137
$ pip install -r requirements-development.txt
138+
$ flake8
139139
$ py.test
140140

141-
142141
-----
143142
Usage
144143
-----

example/serializers.py

+33
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from datetime import datetime
22

3+
from rest_framework import serializers as drf_serilazers
4+
35
from rest_framework_json_api import relations, serializers
46

57
from example.models import (
@@ -26,6 +28,15 @@ class Meta:
2628
fields = ('tag',)
2729

2830

31+
class TaggedItemDRFSerializer(drf_serilazers.ModelSerializer):
32+
"""
33+
DRF default serializer to test default DRF functionalities
34+
"""
35+
class Meta:
36+
model = TaggedItem
37+
fields = ('tag',)
38+
39+
2940
class BlogSerializer(serializers.ModelSerializer):
3041
copyright = serializers.SerializerMethodField()
3142
tags = TaggedItemSerializer(many=True, read_only=True)
@@ -49,6 +60,28 @@ class Meta:
4960
meta_fields = ('copyright',)
5061

5162

63+
class BlogDRFSerializer(drf_serilazers.ModelSerializer):
64+
"""
65+
DRF default serializer to test default DRF functionalities
66+
"""
67+
copyright = serializers.SerializerMethodField()
68+
tags = TaggedItemSerializer(many=True, read_only=True)
69+
70+
def get_copyright(self, resource):
71+
return datetime.now().year
72+
73+
def get_root_meta(self, resource, many):
74+
return {
75+
'api_docs': '/docs/api/blogs'
76+
}
77+
78+
class Meta:
79+
model = Blog
80+
fields = ('name', 'url', 'tags', 'copyright')
81+
read_only_fields = ('tags',)
82+
meta_fields = ('copyright',)
83+
84+
5285
class EntrySerializer(serializers.ModelSerializer):
5386
def __init__(self, *args, **kwargs):
5487
super(EntrySerializer, self).__init__(*args, **kwargs)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import json
2+
3+
import pytest
4+
from django.urls import reverse
5+
from rest_framework import viewsets
6+
from rest_framework.serializers import ModelSerializer, SerializerMethodField
7+
8+
from rest_framework_json_api.renderers import JSONRenderer
9+
10+
from example.models import Blog, Comment, Entry
11+
12+
13+
# serializers
14+
class RelatedModelSerializer(ModelSerializer):
15+
class Meta:
16+
model = Comment
17+
fields = ('id',)
18+
19+
20+
class DummyTestSerializer(ModelSerializer):
21+
"""
22+
This serializer is a simple compound document serializer which includes only
23+
a single embedded relation
24+
"""
25+
related_models = RelatedModelSerializer(source='comments', many=True, read_only=True)
26+
27+
json_field = SerializerMethodField()
28+
29+
def get_json_field(self, entry):
30+
return {'JsonKey': 'JsonValue'}
31+
32+
class Meta:
33+
model = Entry
34+
fields = ('related_models', 'json_field')
35+
36+
37+
# views
38+
class DummyTestViewSet(viewsets.ModelViewSet):
39+
queryset = Entry.objects.all()
40+
serializer_class = DummyTestSerializer
41+
42+
43+
def render_dummy_test_serialized_view(view_class):
44+
serializer = DummyTestSerializer(instance=Entry())
45+
renderer = JSONRenderer()
46+
return renderer.render(
47+
serializer.data,
48+
renderer_context={'view': view_class()})
49+
50+
51+
# tests
52+
def test_simple_reverse_relation_included_renderer():
53+
"""
54+
Test renderer when a single reverse fk relation is passed.
55+
"""
56+
rendered = render_dummy_test_serialized_view(
57+
DummyTestViewSet)
58+
59+
assert rendered
60+
61+
62+
def test_render_format_field_names(settings):
63+
"""Test that json field is kept untouched."""
64+
settings.JSON_API_FORMAT_FIELD_NAMES = 'dasherize'
65+
rendered = render_dummy_test_serialized_view(DummyTestViewSet)
66+
67+
result = json.loads(rendered.decode())
68+
assert result['data']['attributes']['json-field'] == {'JsonKey': 'JsonValue'}
69+
70+
71+
def test_render_format_keys(settings):
72+
"""Test that json field value keys are formated."""
73+
delattr(settings, 'JSON_API_FORMAT_FILED_NAMES')
74+
settings.JSON_API_FORMAT_KEYS = 'dasherize'
75+
rendered = render_dummy_test_serialized_view(DummyTestViewSet)
76+
77+
result = json.loads(rendered.decode())
78+
assert result['data']['attributes']['json-field'] == {'json-key': 'JsonValue'}
79+
80+
81+
@pytest.mark.django_db
82+
def test_blog_create(client):
83+
84+
url = reverse('drf-entry-blog-list')
85+
name = "Dummy Name"
86+
87+
request_data = {
88+
'data': {
89+
'attributes': {'name': name},
90+
'type': 'blogs'
91+
},
92+
}
93+
94+
resp = client.post(url, request_data)
95+
96+
# look for created blog in database
97+
blog = Blog.objects.filter(name=name)
98+
99+
# check if blog exists in database
100+
assert blog.count() == 1
101+
102+
# get created blog from database
103+
blog = blog.first()
104+
105+
expected = {
106+
'data': {
107+
'attributes': {'name': blog.name},
108+
'id': '{}'.format(blog.id),
109+
'links': {'self': 'http://testserver/blogs/{}'.format(blog.id)},
110+
'meta': {'copyright': 2018},
111+
'relationships': {'tags': {'data': []}},
112+
'type': 'blogs'
113+
},
114+
'meta': {'apiDocs': '/docs/api/blogs'}
115+
}
116+
117+
assert resp.status_code == 201
118+
assert resp.json() == expected
119+
120+
121+
@pytest.mark.django_db
122+
def test_get_object_gives_correct_blog(client, blog, entry):
123+
124+
url = reverse('drf-entry-blog-detail', kwargs={'entry_pk': entry.id})
125+
resp = client.get(url)
126+
expected = {
127+
'data': {
128+
'attributes': {'name': blog.name},
129+
'id': '{}'.format(blog.id),
130+
'links': {'self': 'http://testserver/blogs/{}'.format(blog.id)},
131+
'meta': {'copyright': 2018},
132+
'relationships': {'tags': {'data': []}},
133+
'type': 'blogs'
134+
},
135+
'meta': {'apiDocs': '/docs/api/blogs'}
136+
}
137+
got = resp.json()
138+
assert got == expected
139+
140+
141+
@pytest.mark.django_db
142+
def test_get_object_patches_correct_blog(client, blog, entry):
143+
144+
url = reverse('drf-entry-blog-detail', kwargs={'entry_pk': entry.id})
145+
new_name = blog.name + " update"
146+
assert not new_name == blog.name
147+
148+
request_data = {
149+
'data': {
150+
'attributes': {'name': new_name},
151+
'id': '{}'.format(blog.id),
152+
'links': {'self': 'http://testserver/blogs/{}'.format(blog.id)},
153+
'meta': {'copyright': 2018},
154+
'relationships': {'tags': {'data': []}},
155+
'type': 'blogs'
156+
},
157+
'meta': {'apiDocs': '/docs/api/blogs'}
158+
}
159+
160+
resp = client.patch(url, data=request_data)
161+
162+
assert resp.status_code == 200
163+
164+
expected = {
165+
'data': {
166+
'attributes': {'name': new_name},
167+
'id': '{}'.format(blog.id),
168+
'links': {'self': 'http://testserver/blogs/{}'.format(blog.id)},
169+
'meta': {'copyright': 2018},
170+
'relationships': {'tags': {'data': []}},
171+
'type': 'blogs'
172+
},
173+
'meta': {'apiDocs': '/docs/api/blogs'}
174+
}
175+
got = resp.json()
176+
assert got == expected
177+
178+
179+
@pytest.mark.django_db
180+
def test_get_object_deletes_correct_blog(client, entry):
181+
182+
url = reverse('drf-entry-blog-detail', kwargs={'entry_pk': entry.id})
183+
184+
resp = client.delete(url)
185+
186+
assert resp.status_code == 204

example/urls_test.py

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
CompanyViewset,
1313
CourseRelationshipView,
1414
CourseViewSet,
15+
DRFBlogViewSet,
1516
EntryRelationshipView,
1617
EntryViewSet,
1718
FiltersetEntryViewSet,
@@ -26,6 +27,8 @@
2627
router = routers.DefaultRouter(trailing_slash=False)
2728

2829
router.register(r'blogs', BlogViewSet)
30+
# router to test default DRF functionalities
31+
router.register(r'drf-blogs', DRFBlogViewSet, 'drf-entry-blog')
2932
router.register(r'entries', EntryViewSet)
3033
# these "flavors" of entries are used for various tests:
3134
router.register(r'nopage-entries', NonPaginatedEntryViewSet, 'nopage-entry')

example/views.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import rest_framework.exceptions as exceptions
22
import rest_framework.parsers
33
import rest_framework.renderers
4+
from django_filters import rest_framework as filters
5+
from rest_framework import viewsets
46
from rest_framework.filters import SearchFilter
57

68
import rest_framework_json_api.metadata
79
import rest_framework_json_api.parsers
810
import rest_framework_json_api.renderers
9-
from django_filters import rest_framework as filters
1011
from rest_framework_json_api.django_filters import DjangoFilterBackend
1112
from rest_framework_json_api.filters import OrderingFilter, QueryParameterValidationFilter
1213
from rest_framework_json_api.pagination import PageNumberPagination
@@ -16,6 +17,7 @@
1617
from example.models import Author, Blog, Comment, Company, Course, Entry, Project, ProjectType, Term
1718
from example.serializers import (
1819
AuthorSerializer,
20+
BlogDRFSerializer,
1921
BlogSerializer,
2022
CommentSerializer,
2123
CompanySerializer,
@@ -41,6 +43,19 @@ def get_object(self):
4143
return super(BlogViewSet, self).get_object()
4244

4345

46+
class DRFBlogViewSet(viewsets.ModelViewSet):
47+
queryset = Blog.objects.all()
48+
serializer_class = BlogDRFSerializer
49+
lookup_url_kwarg = 'entry_pk'
50+
51+
def get_object(self):
52+
entry_pk = self.kwargs.get(self.lookup_url_kwarg, None)
53+
if entry_pk is not None:
54+
return Entry.objects.get(id=entry_pk).blog
55+
56+
return super(DRFBlogViewSet, self).get_object()
57+
58+
4459
class JsonApiViewSet(ModelViewSet):
4560
"""
4661
This is an example on how to configure DRF-jsonapi from

requirements-development.txt

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
-e .
22
django-debug-toolbar
3+
django-filter>=2.0
34
django-polymorphic>=2.0
45
factory-boy
56
Faker
7+
flake8
8+
flake8-isort
69
isort
710
mock
811
pytest
@@ -11,6 +14,4 @@ pytest-factoryboy
1114
recommonmark
1215
Sphinx
1316
sphinx_rtd_theme
14-
tox
1517
twine
16-
django-filter>=2.0

rest_framework_json_api/django_filters/backends.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import re
22

3+
from django_filters import VERSION
4+
from django_filters.rest_framework import DjangoFilterBackend
35
from rest_framework.exceptions import ValidationError
46
from rest_framework.settings import api_settings
57

6-
from django_filters import VERSION
7-
from django_filters.rest_framework import DjangoFilterBackend
88
from rest_framework_json_api.utils import format_value
99

1010

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ known_localfolder = example
2121
known_standard_library = mock
2222
line_length = 100
2323
multi_line_output = 3
24-
skip_glob=*migrations*
24+
skip=migrations,.tox,docs/conf.py
2525

2626
[coverage:report]
2727
omit=

0 commit comments

Comments
 (0)