diff --git a/AUTHORS b/AUTHORS index f6feed96..f24fc6a6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -18,3 +18,4 @@ Roberto Barreda santiavenda Tim Selman Yaniv Peer +Mohammed Ali Zubair diff --git a/example/serializers.py b/example/serializers.py index 44a61796..1fee79c4 100644 --- a/example/serializers.py +++ b/example/serializers.py @@ -1,5 +1,7 @@ from datetime import datetime +from rest_framework import serializers as drf_serilazers + from rest_framework_json_api import relations, serializers from example.models import ( @@ -24,6 +26,15 @@ class Meta: fields = ('tag',) +class TaggedItemDRFSerializer(drf_serilazers.ModelSerializer): + """ + DRF default serializer to test default DRF functionalities + """ + class Meta: + model = TaggedItem + fields = ('tag',) + + class BlogSerializer(serializers.ModelSerializer): copyright = serializers.SerializerMethodField() tags = TaggedItemSerializer(many=True, read_only=True) @@ -47,6 +58,28 @@ class Meta: meta_fields = ('copyright',) +class BlogDRFSerializer(drf_serilazers.ModelSerializer): + """ + DRF default serializer to test default DRF functionalities + """ + copyright = serializers.SerializerMethodField() + tags = TaggedItemSerializer(many=True, read_only=True) + + def get_copyright(self, resource): + return datetime.now().year + + def get_root_meta(self, resource, many): + return { + 'api_docs': '/docs/api/blogs' + } + + class Meta: + model = Blog + fields = ('name', 'url', 'tags', 'copyright') + read_only_fields = ('tags',) + meta_fields = ('copyright',) + + class EntrySerializer(serializers.ModelSerializer): def __init__(self, *args, **kwargs): super(EntrySerializer, self).__init__(*args, **kwargs) diff --git a/example/tests/unit/test_default_drf_serializers.py b/example/tests/unit/test_default_drf_serializers.py new file mode 100644 index 00000000..d09d293f --- /dev/null +++ b/example/tests/unit/test_default_drf_serializers.py @@ -0,0 +1,186 @@ +import json + +import pytest +from django.urls import reverse +from rest_framework import viewsets +from rest_framework.serializers import ModelSerializer, SerializerMethodField + +from rest_framework_json_api.renderers import JSONRenderer + +from example.models import Blog, Comment, Entry + + +# serializers +class RelatedModelSerializer(ModelSerializer): + class Meta: + model = Comment + fields = ('id',) + + +class DummyTestSerializer(ModelSerializer): + """ + This serializer is a simple compound document serializer which includes only + a single embedded relation + """ + related_models = RelatedModelSerializer(source='comments', many=True, read_only=True) + + json_field = SerializerMethodField() + + def get_json_field(self, entry): + return {'JsonKey': 'JsonValue'} + + class Meta: + model = Entry + fields = ('related_models', 'json_field') + + +# views +class DummyTestViewSet(viewsets.ModelViewSet): + queryset = Entry.objects.all() + serializer_class = DummyTestSerializer + + +def render_dummy_test_serialized_view(view_class): + serializer = DummyTestSerializer(instance=Entry()) + renderer = JSONRenderer() + return renderer.render( + serializer.data, + renderer_context={'view': view_class()}) + + +# tests +def test_simple_reverse_relation_included_renderer(): + """ + Test renderer when a single reverse fk relation is passed. + """ + rendered = render_dummy_test_serialized_view( + DummyTestViewSet) + + assert rendered + + +def test_render_format_field_names(settings): + """Test that json field is kept untouched.""" + settings.JSON_API_FORMAT_FIELD_NAMES = 'dasherize' + rendered = render_dummy_test_serialized_view(DummyTestViewSet) + + result = json.loads(rendered.decode()) + assert result['data']['attributes']['json-field'] == {'JsonKey': 'JsonValue'} + + +def test_render_format_keys(settings): + """Test that json field value keys are formated.""" + delattr(settings, 'JSON_API_FORMAT_FILED_NAMES') + settings.JSON_API_FORMAT_KEYS = 'dasherize' + rendered = render_dummy_test_serialized_view(DummyTestViewSet) + + result = json.loads(rendered.decode()) + assert result['data']['attributes']['json-field'] == {'json-key': 'JsonValue'} + + +@pytest.mark.django_db +def test_blog_create(client): + + url = reverse('drf-entry-blog-list') + name = "Dummy Name" + + request_data = { + 'data': { + 'attributes': {'name': name}, + 'type': 'blogs' + }, + } + + resp = client.post(url, request_data) + + # look for created blog in database + blog = Blog.objects.filter(name=name) + + # check if blog exists in database + assert blog.count() == 1 + + # get created blog from database + blog = blog.first() + + expected = { + 'data': { + 'attributes': {'name': blog.name}, + 'id': '{}'.format(blog.id), + 'links': {'self': 'http://testserver/blogs/{}'.format(blog.id)}, + 'meta': {'copyright': 2018}, + 'relationships': {'tags': {'data': []}}, + 'type': 'blogs' + }, + 'meta': {'apiDocs': '/docs/api/blogs'} + } + + assert resp.status_code == 201 + assert resp.json() == expected + + +@pytest.mark.django_db +def test_get_object_gives_correct_blog(client, blog, entry): + + url = reverse('drf-entry-blog-detail', kwargs={'entry_pk': entry.id}) + resp = client.get(url) + expected = { + 'data': { + 'attributes': {'name': blog.name}, + 'id': '{}'.format(blog.id), + 'links': {'self': 'http://testserver/blogs/{}'.format(blog.id)}, + 'meta': {'copyright': 2018}, + 'relationships': {'tags': {'data': []}}, + 'type': 'blogs' + }, + 'meta': {'apiDocs': '/docs/api/blogs'} + } + got = resp.json() + assert got == expected + + +@pytest.mark.django_db +def test_get_object_patches_correct_blog(client, blog, entry): + + url = reverse('drf-entry-blog-detail', kwargs={'entry_pk': entry.id}) + new_name = blog.name + " update" + assert not new_name == blog.name + + request_data = { + 'data': { + 'attributes': {'name': new_name}, + 'id': '{}'.format(blog.id), + 'links': {'self': 'http://testserver/blogs/{}'.format(blog.id)}, + 'meta': {'copyright': 2018}, + 'relationships': {'tags': {'data': []}}, + 'type': 'blogs' + }, + 'meta': {'apiDocs': '/docs/api/blogs'} + } + + resp = client.patch(url, data=request_data) + + assert resp.status_code == 200 + + expected = { + 'data': { + 'attributes': {'name': new_name}, + 'id': '{}'.format(blog.id), + 'links': {'self': 'http://testserver/blogs/{}'.format(blog.id)}, + 'meta': {'copyright': 2018}, + 'relationships': {'tags': {'data': []}}, + 'type': 'blogs' + }, + 'meta': {'apiDocs': '/docs/api/blogs'} + } + got = resp.json() + assert got == expected + + +@pytest.mark.django_db +def test_get_object_deletes_correct_blog(client, entry): + + url = reverse('drf-entry-blog-detail', kwargs={'entry_pk': entry.id}) + + resp = client.delete(url) + + assert resp.status_code == 204 diff --git a/example/urls_test.py b/example/urls_test.py index 94568ce4..2e7d2d64 100644 --- a/example/urls_test.py +++ b/example/urls_test.py @@ -10,6 +10,7 @@ CommentRelationshipView, CommentViewSet, CompanyViewset, + DRFBlogViewSet, EntryRelationshipView, EntryViewSet, FiltersetEntryViewSet, @@ -22,6 +23,8 @@ router = routers.DefaultRouter(trailing_slash=False) router.register(r'blogs', BlogViewSet) +# router to test default DRF functionalities +router.register(r'drf-blogs', DRFBlogViewSet, 'drf-entry-blog') router.register(r'entries', EntryViewSet) # these "flavors" of entries are used for various tests: router.register(r'nopage-entries', NonPaginatedEntryViewSet, 'nopage-entry') diff --git a/example/views.py b/example/views.py index f2777136..78cdc6ad 100644 --- a/example/views.py +++ b/example/views.py @@ -2,6 +2,7 @@ import rest_framework.parsers import rest_framework.renderers from django_filters import rest_framework as filters +from rest_framework import viewsets from rest_framework.filters import SearchFilter import rest_framework_json_api.metadata @@ -16,6 +17,7 @@ from example.models import Author, Blog, Comment, Company, Entry, Project, ProjectType from example.serializers import ( AuthorSerializer, + BlogDRFSerializer, BlogSerializer, CommentSerializer, CompanySerializer, @@ -39,6 +41,19 @@ def get_object(self): return super(BlogViewSet, self).get_object() +class DRFBlogViewSet(viewsets.ModelViewSet): + queryset = Blog.objects.all() + serializer_class = BlogDRFSerializer + lookup_url_kwarg = 'entry_pk' + + def get_object(self): + entry_pk = self.kwargs.get(self.lookup_url_kwarg, None) + if entry_pk is not None: + return Entry.objects.get(id=entry_pk).blog + + return super(DRFBlogViewSet, self).get_object() + + class JsonApiViewSet(ModelViewSet): """ This is an example on how to configure DRF-jsonapi from