From 041bd7e0f476e8e3b28a9e01ab1f979704703cae Mon Sep 17 00:00:00 2001 From: Oliver Sauder Date: Fri, 16 Nov 2018 17:12:17 +0100 Subject: [PATCH] Avoid error with related urls when retrieving relationship Fixes raising of error when retrieving relationship is referenced as `ForeignKey`on parent --- CHANGELOG.md | 1 + example/serializers.py | 1 + example/tests/test_views.py | 18 ++++++++++++++++-- rest_framework_json_api/views.py | 8 +++++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c60329ef..a9d4e794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ any parts of the framework not mentioned in the documentation should generally b * Pass context from `PolymorphicModelSerializer` to child serializers to support fields which require a `request` context such as `url`. * Avoid patch on `RelationshipView` deleting relationship instance when constraint would allow null ([#242](https://github.com/django-json-api/django-rest-framework-json-api/issues/242)) +* Avoid error with related urls when retrieving relationship which is referenced as `ForeignKey` on parent ## [2.6.0] - 2018-09-20 diff --git a/example/serializers.py b/example/serializers.py index c52f575f..97dde4bb 100644 --- a/example/serializers.py +++ b/example/serializers.py @@ -225,6 +225,7 @@ class AuthorSerializer(serializers.ModelSerializer): } related_serializers = { 'bio': 'example.serializers.AuthorBioSerializer', + 'type': 'example.serializers.AuthorTypeSerializer', 'entries': 'example.serializers.EntrySerializer', 'first_entry': 'example.serializers.EntrySerializer' } diff --git a/example/tests/test_views.py b/example/tests/test_views.py index 5b778b90..198b8b00 100644 --- a/example/tests/test_views.py +++ b/example/tests/test_views.py @@ -325,11 +325,11 @@ def test_get_serializer_comes_from_included_serializers(self): view.serializer_class.related_serializers = related_serializers def test_get_serializer_class_raises_error(self): - kwargs = {'pk': self.author.id, 'related_field': 'type'} + kwargs = {'pk': self.author.id, 'related_field': 'unknown'} view = self._get_view(kwargs) self.assertRaises(NotFound, view.get_serializer_class) - def test_retrieve_related_single(self): + def test_retrieve_related_single_reverse_lookup(self): url = reverse('author-related', kwargs={'pk': self.author.pk, 'related_field': 'bio'}) resp = self.client.get(url) expected = { @@ -345,6 +345,20 @@ def test_retrieve_related_single(self): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.json(), expected) + def test_retrieve_related_single(self): + url = reverse('author-related', kwargs={'pk': self.author.type.pk, 'related_field': 'type'}) + resp = self.client.get(url) + expected = { + 'data': { + 'type': 'authorTypes', 'id': str(self.author.type.id), + 'attributes': { + 'name': str(self.author.type.name) + }, + } + } + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.json(), expected) + def test_retrieve_related_many(self): entry = EntryFactory(authors=self.author) url = reverse('author-related', kwargs={'pk': self.author.pk, 'related_field': 'entries'}) diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index 9f3178a1..35a213fc 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -14,6 +14,8 @@ from django.utils.module_loading import import_string as import_class_from_dotted_path from rest_framework import generics, viewsets from rest_framework.exceptions import MethodNotAllowed, NotFound +from rest_framework.fields import get_attribute +from rest_framework.relations import PKOnlyObject from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.serializers import Serializer @@ -164,7 +166,11 @@ def get_related_instance(self): field = parent_serializer.fields.get(field_name, None) if field is not None: - return field.get_attribute(parent_obj) + instance = field.get_attribute(parent_obj) + if isinstance(instance, PKOnlyObject): + # need whole object + instance = get_attribute(parent_obj, field.source_attrs) + return instance else: try: return getattr(parent_obj, field_name)