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

Issue:
Support for HyperlinkedIdentityField
```
Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/snippets/

Django Version: 1.10.3
Python Version: 2.7.12
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'rest_framework',
 'rest_framework_jwt',
 'rest_framework_json_api',
 'snippets.apps.SnippetsConfig']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
  39.             response = get_response(request)

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  217.                 response = self.process_exception_by_middleware(e, request)

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  215.                 response = response.render()

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/django/template/response.py" in render
  109.             self.content = self.rendered_content

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/rest_framework/response.py" in rendered_content
  72.         ret = renderer.render(self.data, accepted_media_type, context)

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/rest_framework/renderers.py" in render
  701.         context = self.get_context(data, accepted_media_type, renderer_context)

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/rest_framework/renderers.py" in get_context
  659.             'content': self.get_content(renderer, data, accepted_media_type, renderer_context),

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/rest_framework/renderers.py" in get_content
  409.         content = renderer.render(data, accepted_media_type, renderer_context)

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/rest_framework_json_api/renderers.py" in render
  469.                     json_resource_obj = self.build_json_resource_obj(fields, resource, resource_instance, resource_name)

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/rest_framework_json_api/renderers.py" in build_json_resource_obj
  390.         relationships = JSONRenderer.extract_relationships(fields, resource, resource_instance)

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/rest_framework_json_api/renderers.py" in extract_relationships
  91.             relation_type = utils.get_related_resource_type(field)

File "/home/user/.pyenv/versions/djangorest/lib/python2.7/site-packages/rest_framework_json_api/utils.py" in get_related_resource_type
  202.                     parent_model_relation = getattr(parent_model, relation.field_name)

Exception Type: AttributeError at /snippets/
Exception Value: type object 'Snippet' has no attribute 'highlight'
```
Trying to integrate djangorestframework-jsonapi into the [official djangorestframework tutorial](https://github.com/tomchristie/rest-framework-tutorial), but I keep getting errors. I followed the [docs configuration instructions](https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#configuration), and changed my `serializers.py` import to `from rest_framework_json_api import serializers`.  What am I missing here? Does djangorestframework-jsonapi not support the `HyperlinkedIdentityField`? Or should I be using the `ResourceRelatedField` for relationships? Any help is greatly appreciated!

**Side note:** _If there's anything I would improve upon it's the documentation. A tutorial, possibly forked from the official drf one, would be nice. Thank you for creating this project!_ :)


Discussion:
Is there any progress on supporting `HyperlinkedIdentityField`, what are the alternatives?
`rest_framework_json_api.relations.HyperlinkedIdentityField` is properly what you are looking for?
Hey @sliverc 👋 

Did you mean to say:

> `HyperlinkedIdentityField` is properly *supported* what *are you* looking for?

And if so, please kindly see the traceback in my original comment. 🤔 

Please let me know what additional info you need. Thanks in advance for your time!
Thanks for your feedback.

Back in June when I browsing though all open tickets trying to address them I think I was bit quick with this comment. :wink: Sorry about that.

Yes indeed a tutorial would be a good idea for DJA but best trying of explain the difference between DJA and DRF. Help in this regards would be very much appreciated.

Technically DRF `HyperlinkedIdentityField` should also be supported but there is actually no test :cry: .

In general DRF serializers fields should technically work but as there are some specialties in JSON API specification it is recommended to use the DJA specific serializer relations. which are explained at https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html#related-fields
Hope this answers your question how to move forward using DJA.

Marking this still as bug though as at least there should be a test to use `HyperlinkedIdentityField` and potential fix of the implementation if it can be reproduced in newest version.
@sliverc Any suggestions on how I could get started? I see serializers using `HyperlinkedRelatedField`, should I be testing those endpoints by making DRF equivalent of those serializers?
@Alig1493 I would extend the DRF tests you already have written and add a DRFEntrySerializer using HyperlinkedIdentityField for blog relation.
@sliverc The default DRF HyperlinkedIdentityField produces errors.
Using the DRF HyperlinkedModelSerializer defaults the relationship to HyperlinkedRelatedField.
@Alig1493 if the error is similar to what is above then that's great because this means the error can be reproduced. Best create a PR with the reproducing tests. If you find a fix for the error that would be great or otherwise someone else might have a look (I might get to it at some point).
This is the response for HyperlinkRelatedField for Blogs in Entry Serializer for default DRF:
https://dpaste.de/nrr5
This is the reponse for default HyperlinkedIdentityField for blogs:
https://dpaste.de/r7Ld
Alright I'll set up a PR soon.

Resolving patch:
diff --git a/example/serializers.py b/example/serializers.py
--- a/example/serializers.py
+++ b/example/serializers.py
@@ -63,7 +63,7 @@ class BlogDRFSerializer(drf_serilazers.ModelSerializer):
     DRF default serializer to test default DRF functionalities
     """
     copyright = serializers.SerializerMethodField()
-    tags = TaggedItemSerializer(many=True, read_only=True)
+    tags = TaggedItemDRFSerializer(many=True, read_only=True)
 
     def get_copyright(self, resource):
         return datetime.now().year
@@ -173,6 +173,21 @@ class JSONAPIMeta:
         included_resources = ['comments']
 
 
+class EntryDRFSerializers(drf_serilazers.ModelSerializer):
+
+    tags = TaggedItemDRFSerializer(many=True, read_only=True)
+    url = drf_serilazers.HyperlinkedIdentityField(
+        view_name='drf-entry-blog-detail',
+        lookup_url_kwarg='entry_pk',
+        read_only=True,
+    )
+
+    class Meta:
+        model = Entry
+        fields = ('tags', 'url',)
+        read_only_fields = ('tags',)
+
+
 class AuthorTypeSerializer(serializers.ModelSerializer):
     class Meta:
         model = AuthorType
diff --git a/example/views.py b/example/views.py
--- a/example/views.py
+++ b/example/views.py
@@ -21,6 +21,7 @@
     BlogSerializer,
     CommentSerializer,
     CompanySerializer,
+    EntryDRFSerializers,
     EntrySerializer,
     ProjectSerializer,
     ProjectTypeSerializer
@@ -104,6 +105,20 @@ def get_object(self):
         return super(EntryViewSet, self).get_object()
 
 
+class DRFEntryViewSet(viewsets.ModelViewSet):
+    queryset = Entry.objects.all()
+    serializer_class = EntryDRFSerializers
+    lookup_url_kwarg = 'entry_pk'
+
+    def get_object(self):
+        # Handle featured
+        entry_pk = self.kwargs.get(self.lookup_url_kwarg, None)
+        if entry_pk is not None:
+            return Entry.objects.exclude(pk=entry_pk).first()
+
+        return super(DRFEntryViewSet, self).get_object()
+
+
 class NoPagination(PageNumberPagination):
     page_size = None
 


Test patch:
diff --git a/example/tests/unit/test_default_drf_serializers.py b/example/tests/unit/test_default_drf_serializers.py
--- a/example/tests/unit/test_default_drf_serializers.py
+++ b/example/tests/unit/test_default_drf_serializers.py
@@ -184,3 +184,45 @@ def test_get_object_deletes_correct_blog(client, entry):
     resp = client.delete(url)
 
     assert resp.status_code == 204
+
+
+@pytest.mark.django_db
+def test_get_entry_list_with_blogs(client, entry):
+    url = reverse('drf-entry-suggested', kwargs={'entry_pk': entry.id})
+    resp = client.get(url)
+
+    got = resp.json()
+
+    expected = {
+        'links': {
+            'first': 'http://testserver/drf-entries/1/suggested/?page=1',
+            'last': 'http://testserver/drf-entries/1/suggested/?page=1',
+            'next': None,
+            'prev': None
+        },
+        'data': [
+            {
+                'type': 'entries',
+                'id': '1',
+                'attributes': {},
+                'relationships': {
+                    'tags': {
+                        'data': []
+                    }
+                },
+                'links': {
+                    'self': 'http://testserver/drf-blogs/1'
+                }
+            }
+        ],
+        'meta': {
+            'pagination': {
+                'page': 1,
+                'pages': 1,
+                'count': 1
+            }
+        }
+    }
+
+    assert resp.status_code == 200
+    assert got == expected
diff --git a/example/urls_test.py b/example/urls_test.py
--- a/example/urls_test.py
+++ b/example/urls_test.py
@@ -11,6 +11,7 @@
     CommentViewSet,
     CompanyViewset,
     DRFBlogViewSet,
+    DRFEntryViewSet,
     EntryRelationshipView,
     EntryViewSet,
     FiltersetEntryViewSet,
@@ -23,7 +24,7 @@
 router = routers.DefaultRouter(trailing_slash=False)
 
 router.register(r'blogs', BlogViewSet)
-# router to test default DRF functionalities
+# router to test default DRF blog functionalities
 router.register(r'drf-blogs', DRFBlogViewSet, 'drf-entry-blog')
 router.register(r'entries', EntryViewSet)
 # these "flavors" of entries are used for various tests:
@@ -59,6 +60,10 @@
         EntryViewSet.as_view({'get': 'list'}),
         name='entry-suggested'
         ),
+    url(r'^drf-entries/(?P<entry_pk>[^/.]+)/suggested/',
+        DRFEntryViewSet.as_view({'get': 'list'}),
+        name='drf-entry-suggested'
+        ),
     url(r'entries/(?P<entry_pk>[^/.]+)/authors',
         AuthorViewSet.as_view({'get': 'list'}),
         name='entry-authors'),