Search code examples
djangoapidjango-rest-frameworkjson-api

Django REST Framework JSON API: Could not resolve URL for hyperlinked relationship when using ResourceRelatedField


I am writing an HTTP API that uses django + django REST framework + django REST framework JSON API. When I tried to do PATCH request I received this error,

Incorrect type. Expected URL string, received OrderedDict

when updating relationship. Looking through issues I understood I need to use ResourceRelatedField.

I'm trying to follow documentation here Related fields. Below is how my code looks like.

urls.py

from django.conf import settings
from django.conf.urls import include, url
from rest_framework import routers
from events import views as e_views

router = routers.DefaultRouter()
router.register(r'events', e_views.EventViewSet)
router.register(r'event-types', e_views.EventTypeViewSet)

urlpatterns = [
    url(r'^', include('events.urls')),
    url(r'^', include(router.urls)),
]

events/urls.py

from django.conf.urls import url

from .views import EventRelationshipView

urlpatterns = [
    url(
        regex=r'^events/(?P<event_pk>[^/.]+)/relationships/(?P<related_field>[^/.]+)$',
        view=EventRelationshipView.as_view(),
        name='event-relationships'
    )
]

models.py

from datetime import timedelta

from django.db import models
from django.contrib.gis.db import models as gis_models


class EventType(models.Model):
    title = models.CharField(max_length=16)

    def __str__(self):
        return str(self.title)


class Event(models.Model):
    timestamp = models.DateTimeField()
    delay = models.DurationField(default=timedelta(0))
    event_type = models.ForeignKey(EventType)
    name = models.CharField(max_length=16)
    location = models.CharField(max_length=200, blank=True)
    coordinates = gis_models.PointField(null=True, blank=True)
    depth = models.CharField(max_length=64, blank=True)
    size = models.IntegerField(null=True, blank=True)
    comments = models.TextField(blank=True)
    is_virtual = models.BooleanField(default=False)
    is_visible = models.BooleanField(default=True)

    def __str__(self):
        return str(self.name)

serializers.py

from .models import Event, EventType
from rest_framework_json_api import serializers
from rest_framework_json_api.relations import ResourceRelatedField


class EventTypeSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = EventType
        fields = ('url', 'id', 'title')


class EventSerializer(serializers.HyperlinkedModelSerializer):

    included_serializers = {'event_type': EventTypeSerializer}

    # THIS PART HERE DOESN'T WORK
    event_type = ResourceRelatedField(
        queryset=EventType.objects,
        related_link_view_name='eventtype-detail',
        related_link_url_kwarg='pk',
        self_link_view_name='event-relationships'
    )

    class Meta:
        model = Event
        fields = ('url', 'id', 'timestamp', 'delay', 'event_type', 'name', 'location',
                  'coordinates', 'depth', 'size', 'comments', 'is_virtual', 'is_visible')

    class JSONAPIMeta:
        included_resources = ['event_type']

views.py

# -*- coding: utf-8 -*-
import logging

from rest_framework import viewsets
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework_json_api.parsers import JSONParser
from rest_framework_json_api.renderers import JSONRenderer
from rest_framework_json_api.views import RelationshipView

import django_filters

from .models import Event, EventType
from .serializers import EventSerializer, EventTypeSerializer


class EventFilter(django_filters.rest_framework.FilterSet):
    start_timestamp = django_filters.DateTimeFilter(name="timestamp", lookup_expr='gte')
    end_timestamp = django_filters.DateTimeFilter(name="timestamp", lookup_expr='lte')

    class Meta:
        model = Event
        fields = ['event_type', 'start_timestamp', 'end_timestamp']


class EventViewSet(viewsets.ModelViewSet):
    queryset = Event.objects.all()
    serializer_class = EventSerializer
    parser_classes = (JSONParser,)
    renderer_classes = (JSONRenderer,)
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    filter_class = EventFilter
    search_fields = ('name', 'location', 'comments')
    ordering_fields = '__all__'


class EventTypeViewSet(viewsets.ModelViewSet):
    queryset = EventType.objects.all()
    serializer_class = EventTypeSerializer
    parser_classes = (JSONParser,)
    renderer_classes = (JSONRenderer,)


class EventRelationshipView(RelationshipView):
    queryset = Event.objects

class EventTypeRelationshipView(RelationshipView):
    queryset = EventType.objects

Error

[08/Feb/2017 15:07:44] ERROR [django.request:124] Internal Server Error: /events/6/
Traceback (most recent call last):
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 39, in inner
    response = get_response(request)
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 217, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 215, in _get_response
    response = response.render()
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/django/template/response.py", line 109, in render
    self.content = self.rendered_content
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/rest_framework/response.py", line 72, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/rest_framework_json_api/renderers.py", line 480, in render
    json_api_data = self.build_json_resource_obj(fields, serializer_data, resource_instance, resource_name)
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/rest_framework_json_api/renderers.py", line 390, in build_json_resource_obj
    relationships = cls.extract_relationships(fields, resource, resource_instance)
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/rest_framework_json_api/renderers.py", line 129, in extract_relationships
    field_links = field.get_links(resource_instance)
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/rest_framework_json_api/relations.py", line 106, in get_links
    self_link = self.get_url('self', self.self_link_view_name, self_kwargs, request)
  File "/home/nargiza/virtualenvs/myenv/local/lib/python2.7/site-packages/rest_framework_json_api/relations.py", line 90, in get_url
    raise ImproperlyConfigured(msg % view_name)
ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "event-relationships".

Solution

  • Renaming event_pk to pk in url argument solved the problem.