Repository: django-json-api/django-rest-framework-json-api

Issue:
Test Default DRF Serializer Usage
Currently we do not have test coverage for usage of `from rest_framework import serializers` only `from rest_framework_json_api import serializers`.

Basic functionality has broken in the past when using `from rest_framework import serializers` so we should explicitly test that case if we would like to officially support that usage.



Discussion:
Is it possible to make a contribution to this? If it is then some guidance would be helpful.
@Alig1493 Best have a look how the test are written for [DJA model vieweset](https://github.com/django-json-api/django-rest-framework-json-api/blob/master/example/tests/test_model_viewsets.py#L11). Create a new python module e.g. test_restframework_serializer.py (maybe there is a better name) and rewrite the test that it uses views and serializer which are directly from package `rest_framework` instead of `rest_framework_json_api`.
@sliverc the rest framework has a different sort of output in comparison to yours. Do I write tests that matches the request responses to that of the default rest framework serializers?
@Alig1493 renderer and parsers need to be rest_framework_json_api which are set in settings.py already in the example app. This means the output needs to be json api spec compatible but the serializer and view classes are from rest framework directly.

Hope this clarifies.
I've taken a look at the example project and I found some serializer test references in test_renderer_class_methods and test_renderers. Will it be similar to those except the format I'll be using will be from the drf default serializers?
@Alig1493 I guess `test_renderers.py` might be the better example to look at. Best give it a try and open a PR. Important is just that it is expected that the result using DRF Serializers will still be a renderdered json api spec.
@sliverc I have made an initial attempt mostly taking inspiration from test_renderers.py. I'll be putting up a PR shortly.

Resolving patch:
diff --git a/example/serializers.py b/example/serializers.py
--- 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/views.py b/example/views.py
--- 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


Test patch:
diff --git a/example/tests/unit/test_default_drf_serializers.py b/example/tests/unit/test_default_drf_serializers.py
new file mode 100644
--- /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
--- 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')