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

Issue:
Don't render empty objects on relationships
## Description of the Change

REGRESION (version 2.6)

In previous versions, OneToOneFields without Hyperlink settings in its Serializer doesn't render anything.

In version 2.6, an empty JSON object is rendered.

## Checklist

- [X] PR only contains one change (considered splitting up PR)
- [ ] unit-test added
- [ ] documentation updated
- [ ] changelog entry added to `CHANGELOG.md`
- [ ] author name in `AUTHORS`



Discussion:
Thanks for your contribution.

The specification actually states to return `null for empty to-one relationships.`
See http://jsonapi.org/format/#document-resource-object-linkage

So I think this means this is actually a bug rather than a regression - the bug is just different in previous versions where relationship is not rendered at all and now where it is rendered as empty dict.

So I would prefer when the PR could be adjusted to return null instead of skipping it. A test would also need to be added so we can make sure that this bug doesn't occur again in the future.
IMHO I'd pass this fix (be consistent with the previous behaviour) and do another PR to fix the rendering according to the spec.
I do not think we should make Django JSON API releases bugwards-compatible especially as this is not documented anywhere that relationships should be skipped when not available but is actually a bug according to our first DJA goal to be compliant to the JSON API spec.

Kind of seems to be an overkill to me to introduce the old still buggy way again and fix it properly in another PR.



Resolving patch:
diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py
--- a/rest_framework_json_api/renderers.py
+++ b/rest_framework_json_api/renderers.py
@@ -150,13 +150,6 @@ def extract_relationships(cls, fields, resource, resource_instance):
                 data.update({field_name: relation_data})
 
             if isinstance(field, (ResourceRelatedField, )):
-                relation_instance_id = getattr(resource_instance, source + "_id", None)
-                if not relation_instance_id:
-                    resolved, relation_instance = utils.get_relation_instance(resource_instance,
-                                                                              source, field.parent)
-                    if not resolved:
-                        continue
-
                 if not isinstance(field, SkipDataMixin):
                     relation_data.update({'data': resource.get(field_name)})
 


Test patch:
diff --git a/example/tests/unit/test_renderers.py b/example/tests/unit/test_renderers.py
--- a/example/tests/unit/test_renderers.py
+++ b/example/tests/unit/test_renderers.py
@@ -3,7 +3,7 @@
 from rest_framework_json_api import serializers, views
 from rest_framework_json_api.renderers import JSONRenderer
 
-from example.models import Comment, Entry
+from example.models import Author, Comment, Entry
 
 
 # serializers
@@ -45,8 +45,8 @@ class ReadOnlyDummyTestViewSet(views.ReadOnlyModelViewSet):
     serializer_class = DummyTestSerializer
 
 
-def render_dummy_test_serialized_view(view_class):
-    serializer = view_class.serializer_class(instance=Entry())
+def render_dummy_test_serialized_view(view_class, instance):
+    serializer = view_class.serializer_class(instance=instance)
     renderer = JSONRenderer()
     return renderer.render(
         serializer.data,
@@ -58,14 +58,14 @@ def test_simple_reverse_relation_included_renderer():
     Test renderer when a single reverse fk relation is passed.
     '''
     rendered = render_dummy_test_serialized_view(
-        DummyTestViewSet)
+        DummyTestViewSet, Entry())
 
     assert rendered
 
 
 def test_simple_reverse_relation_included_read_only_viewset():
     rendered = render_dummy_test_serialized_view(
-        ReadOnlyDummyTestViewSet)
+        ReadOnlyDummyTestViewSet, Entry())
 
     assert rendered
 
@@ -73,7 +73,7 @@ def test_simple_reverse_relation_included_read_only_viewset():
 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)
+    rendered = render_dummy_test_serialized_view(DummyTestViewSet, Entry())
 
     result = json.loads(rendered.decode())
     assert result['data']['attributes']['json-field'] == {'JsonKey': 'JsonValue'}
@@ -83,17 +83,15 @@ 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)
+    rendered = render_dummy_test_serialized_view(DummyTestViewSet, Entry())
 
     result = json.loads(rendered.decode())
     assert result['data']['attributes']['json-field'] == {'json-key': 'JsonValue'}
 
 
-def test_writeonly_not_in_response(settings):
+def test_writeonly_not_in_response():
     """Test that writeonly fields are not shown in list response"""
 
-    settings.JSON_API_FORMAT_FIELD_NAMES = 'dasherize'
-
     class WriteonlyTestSerializer(serializers.ModelSerializer):
         '''Serializer for testing the absence of write_only fields'''
         comments = serializers.ResourceRelatedField(
@@ -112,8 +110,27 @@ class WriteOnlyDummyTestViewSet(views.ReadOnlyModelViewSet):
         queryset = Entry.objects.all()
         serializer_class = WriteonlyTestSerializer
 
-    rendered = render_dummy_test_serialized_view(WriteOnlyDummyTestViewSet)
+    rendered = render_dummy_test_serialized_view(WriteOnlyDummyTestViewSet, Entry())
     result = json.loads(rendered.decode())
 
     assert 'rating' not in result['data']['attributes']
     assert 'relationships' not in result['data']
+
+
+def test_render_empty_relationship_reverse_lookup():
+    """Test that empty relationships are rendered as None."""
+
+    class EmptyRelationshipSerializer(serializers.ModelSerializer):
+        class Meta:
+            model = Author
+            fields = ('bio', )
+
+    class EmptyRelationshipViewSet(views.ReadOnlyModelViewSet):
+        queryset = Author.objects.all()
+        serializer_class = EmptyRelationshipSerializer
+
+    rendered = render_dummy_test_serialized_view(EmptyRelationshipViewSet, Author())
+    result = json.loads(rendered.decode())
+    assert 'relationships' in result['data']
+    assert 'bio' in result['data']['relationships']
+    assert result['data']['relationships']['bio'] == {'data': None}