TL;DR: I am getting this error and don't know why:
django.core.exceptions.ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "user-detail". You may have failed to include the related model in your API, or incorrectly configured the 'lookup_field' attribute on this field.
I am going through the django-rest-framework tutorial and am currently at a point where function based views (FBV) were switched to class, mixin, and generic based views (CBV, MBV, GBV respectively). After switching to GBV, when I went to test my API, I received this error AssertionError: Expected view SnippetDetail to be called with a URL keyword argument named "pk". Fix your URL conf, or set the '.lookup_field' attribute on the view correctly.
. I did some research and found that lookup_field
needs to be set to the in the urlpatterns. Currently, my urls.py look like this:
from django.conf.urls import url, include
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
# API endpoints
urlpatterns = format_suffix_patterns([
url(r'^$', views.api_root),
url(r'^snippets/$',
views.SnippetList.as_view(),
name='snippet-list'),
url(r'^snippets/(?P<id>[0-9]+)/$',
views.SnippetDetail.as_view(),
name='snippet-detail'),
url(r'^users/$',
views.UserList.as_view(),
name='user-list'),
url(r'^users/(?P<id>[0-9]+)/$',
views.UserDetail.as_view(),
name='user-detail')
])
# Login and logout views for the browsable API
urlpatterns += [
url(r'^auth/', include('rest_framework.urls',
namespace='rest_framework')),
]
and my views.py look like so:
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer, UserSerializer
from snippets.permissions import IsOwnerOrReadOnly
from rest_framework import generics
from rest_framework import permissions
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from django.contrib.auth.models import User
@api_view(['GET'])
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'snippets': reverse('snippet-list', request=request, format=format)
})
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly, )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly, )
when I add lookup_field = 'id'
in both UserDetail and SnippetDetail, the exception resolves itself. (Yay!). But, when I visit http://127.0.0.1/users/1/ ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "user-detail". You may have failed to include the related model in your API, or incorrectly configured the 'lookup_field' attribute on this field.
is thrown. When I check the console, however, there is a second exception:
django.urls.exceptions.NoReverseMatch: Reverse for 'user-detail' with arguments '()' and keyword arguments '{'pk': 1}' not found. 2 pattern(s) tried: ['users/(?P[0-9]+)\.(?P[a-z0-9]+)/?$', 'users/(?P[0-9]+)/$']
During handling of the above exception, another exception occurred:
django.core.exceptions.ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "user-detail". You may have failed to include the related model in your API, or incorrectly configured the 'lookup_field' attribute on this field.
What I find interesting is that the kwargs for the first exception is {'pk': 1}
, not {'id':1}
. After some help from chat, someone pointed me to this piece of information:
Note that when using hyperlinked APIs you'll need to ensure that both the API views and the serializer classes set the lookup fields if you need to use a custom value.
This is useful as User
serializer extends HyperlinkedModelSerializer
:
from rest_framework import serializers
from django.contrib.auth.models import User
from snippets.models import Snippet
class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)
class Meta:
model = User
fields = ('url', 'id', 'username', 'snippets')
the User
model and serializer has a reverse relationship with Snippet
. Now, when I add lookup_field='id'
to snippets attribute of UserSerializer
(snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True, lookup_field='id')
), like it asks me to do so here, the error is persistent.
What am I doing incorrectly? What can I do to fix this? Is it not having anything to do with lookup_id
?
I understand that I could replace <id>
with <pk>
in my urlpatterns, but I would like to understand why this is happening.
I just had the same problem and I found, that the
HyperlinkedIdentityField
wants to insert some placeholder into your URL. But I had used URLs which did not require any placeholders to be set. AListCreateAPIView
to be precise:I eventually fixed my second exception by printing the serializer in the Django shell/python interactive console. The result I got was this:
It turns out that to change
<pk>
to<id>
in urlspatterns, you need to addurl = HyperlinkedIdentityField(view_name='user-detail', lookup_field='id')
in theUserSerializer
class.