0

Overview

I'm setting up a new Django application with Django REST Framework (DRF), and this is my first time using the HyperlinkedModelSerializer for the API endpoint.

I've overridden the get_queryset() method on the ModelViewSet, so I've also the basename argument to the application router and explicitly defined the url attribute in the serializer as suggested here. This fixed issues that I was having with the model's own url attribute.

However, I'm getting the following error message when trying to serialize a ForeignKey field of the same class as the parent model. It fails with the following message:

Could not resolve URL for hyperlinked relationship using view name "employee-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.

Is there something special in the serializer I need to do to support using recursive model relationships like this?


Example code

# app/models.py

from django.db import models

class AbstractBase(models.Model):
  created = models.DateTimeField(auto_now_add=True)
  updated = models.DateTimeField(auto_now=True)

  class Meta:
    abstract = True

class Employee(AbstractBase):
  name = models.CharField(max_length=254)
  manager = models.ForeignKey('self', related_name='direct_reports',
    on_delete=models.SET_NULL, blank=True, null=True)
  ...

  def __str__(self):
    return str(self.name)
# app/views.py

from rest_framework import viewsets
from rest_framework.pagination import PageNumberPagination

from app import models
from app import serializers

# pagination defaults
class StandardResultsSetPagination(PageNumberPagination):
  page_size = 25
  page_size_query_param = 'page_size'
  max_page_size = 1000

class EmployeeViewSet(viewsets.ModelViewSet):
  pagination_class = StandardResultsSetPagination
  serializer_class = serializers.EmployeeSerializer
  http_method_names = ['options', 'get']

  def get_queryset(self):
    params = self.request.query_params
    queryset = models.Employee.objects.all()

    # apply url query filters...

    return queryset
# app/serializers.py

from app import models
from rest_framework import serializers

class EmployeeSerializer(serializers.HyperlinkedModelSerializer):
  url = serializers.HyperlinkedRelatedField(
    read_only=True, view_name='employees-detail')
  manager = serializers.HyperlinkedRelatedField(
    read_only=True, view_name='employees-detail')

  class Meta:
    model = models.Employee
    fields = ('url', 'name', 'manager')
# project/urls.py

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
  url(r'^admin/', admin.site.urls),
  url(r'^api/', include('app.urls')),
]
# app/urls.py

from django.conf.urls import url, include
from rest_framework import routers

from app import views

router = routers.DefaultRouter(trailing_slash=False)
router.register(r'employees', views.EmployeeViewSet, basename='employees')

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

Community
  • 1
  • 1
Micah Yeager
  • 210
  • 2
  • 10

2 Answers2

0

I have no idea why this worked, but changing the inherited serializer from serializers.HyperlinkedModelSerializer to serializers.ModelSerializer and removing the overridden url fixed things perfectly.

New serializer looks like this:

# app/serializers.py

from app import models
from rest_framework import serializers

class EmployeeSerializer(serializers.ModelSerializer):
  manager = serializers.HyperlinkedRelatedField(
    read_only=True, view_name='employees-detail')

  class Meta:
    model = models.Employee
    fields = ('url', 'name', 'manager')
Micah Yeager
  • 210
  • 2
  • 10
0

The problem with your code is using HyperlinkedRelatedField instead of HyperlinkedIdentityField. HyperlinkedRelatedField is for relations to other models. In this case, the link is to the model itself so use HyperlinkedIdentityField instead

Farouq
  • 1