155

I am building a project in Django Rest Framework where users can login to view their wine cellar. My ModelViewSets were working just fine and all of a sudden I get this frustrating error:

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.

The traceback shows:

    [12/Dec/2013 18:35:29] "GET /bottles/ HTTP/1.1" 500 76677
Internal Server Error: /bottles/
Traceback (most recent call last):
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/django/core/handlers/base.py", line 114, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/views.py", line 399, in dispatch
    response = self.handle_exception(exc)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/views.py", line 396, in dispatch
    response = handler(request, *args, **kwargs)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/mixins.py", line 96, in list
    return Response(serializer.data)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/serializers.py", line 535, in data
    self._data = [self.to_native(item) for item in obj]
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/serializers.py", line 325, in to_native
    value = field.field_to_native(obj, field_name)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/relations.py", line 153, in field_to_native
    return self.to_native(value)
  File "/Users/bpipat/.virtualenvs/usertest2/lib/python2.7/site-packages/rest_framework/relations.py", line 452, in to_native
    raise Exception(msg % view_name)
Exception: 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 have a custom email user model and the bottle model in models.py is:

class Bottle(models.Model):    
      wine = models.ForeignKey(Wine, null=False)
      user = models.ForeignKey(User, null=False, related_name='bottles')

My serializers:

class BottleSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Bottle
        fields = ('url', 'wine', 'user')

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('email', 'first_name', 'last_name', 'password', 'is_superuser')

My views:

class BottleViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows bottles to be viewed or edited.
    """
    queryset = Bottle.objects.all()
    serializer_class = BottleSerializer

class UserViewSet(ListCreateAPIView):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

and finally the url:

router = routers.DefaultRouter()
router.register(r'bottles', views.BottleViewSet, base_name='bottles')

urlpatterns = patterns('',
    url(r'^', include(router.urls)),
    # ...

I don't have a user detail view and I don't see where this issue could come from. Any ideas?

Thanks

Konrad Borowski
  • 11,584
  • 3
  • 57
  • 71
bpipat
  • 3,700
  • 4
  • 20
  • 22
  • a mention to bpipat for asking the same questions that I need, this is 3 consecutive hit for his question all with different errors – Atharva Kadlag May 04 '21 at 18:09

22 Answers22

145

Because it's a HyperlinkedModelSerializer your serializer is trying to resolve the URL for the related User on your Bottle.
As you don't have the user detail view it can't do this. Hence the exception.

  1. Would not just registering the UserViewSet with the router solve your issue?
  2. You could define the user field on your BottleSerializer to explicitly use the UserSerializer rather than trying to resolve the URL. See the serializer docs on dealing with nested objects for that.
Carlton Gibson
  • 7,278
  • 2
  • 36
  • 46
  • 1
    Great thanks, I had commented out the UserViewSet in my routers, that solved it! – bpipat Dec 14 '13 at 10:46
  • 9
    THATS THE POINT ---- do it explicitly --- to much magic is to much time lost. – andilabs Jul 18 '14 at 14:05
  • The reason it was not working was because django wanted to show related data from User in your current view for parameter User. Usually it picks up a list of available values. Since UserViewSet was not defined it was not able to pull the details to render the webpage. Adding UserViewSet and regisetering under the default router makes thing complete to render all components. – Doogle Sep 16 '18 at 15:54
90

I came across this error too and solved it as follows:

The reason is I forgot giving "**-detail" (view_name, e.g.: user-detail) a namespace. So, Django Rest Framework could not find that view.

There is one app in my project, suppose that my project name is myproject, and the app name is myapp.

There is two urls.py file, one is myproject/urls.py and the other is myapp/urls.py. I give the app a namespace in myproject/urls.py, just like:

url(r'', include(myapp.urls, namespace="myapp")),

I registered the rest framework routers in myapp/urls.py, and then got this error.

My solution was to provide url with namespace explicitly:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="myapp:user-detail")

    class Meta:
        model = User
        fields = ('url', 'username')

And it solved my problem.

lukeaus
  • 11,465
  • 7
  • 50
  • 60
bovenson
  • 1,170
  • 8
  • 6
  • @boveson, this works like a charm! Thanks you solved hours of frustration on my side. – lmiguelvargasf Nov 25 '18 at 20:47
  • This also made it working for me. One also important point on my side was the correct spelling of the base_name in the Route! – maggie Jan 11 '19 at 09:53
  • 1
    The key here is the namespace prefix prevents reverse from working..... – boatcoder Feb 01 '19 at 14:08
  • I had problem like this and this answer fixed my problem after 3 hours of searching! @bovenson – rahnama7m Jul 29 '19 at 20:32
  • or you can make use of extra_kwargs as drf recommends: `extra_kwargs = {'url': {'view_name': 'myapp:user-detail'}}` – ChrisRob Jan 25 '20 at 15:11
  • That works, but it requires setting the url namespace for every serializer class. I wonder if there's a more practical way to do that. There's a [question](https://stackoverflow.com/questions/58789733/how-to-make-drf-hyperlinkedmodelserializer-elegantly-work-with-app-name) about this that currently has no answer. – bwdm Apr 21 '21 at 22:19
  • After seven days of trying different approaches, this solved my problem. – Alireza Afzal Aghaei Dec 23 '21 at 22:48
26

Another nasty mistake that causes this error is having the base_name unnecessarily defined in your urls.py. For example:

router.register(r'{pathname}', views.{ViewName}ViewSet, base_name='pathname')

This will cause the error noted above. Get that base_name outta there and get back to a working API. The code below would fix the error. Hooray!

router.register(r'{pathname}', views.{ViewName}ViewSet)

However, you probably didn't just arbitrarily add the base_name, you might have done it because you defined a custom def get_queryset() for the View and so Django mandates that you add the base_name. In this case you'll need to explicitly define the 'url' as a HyperlinkedIdentityField for the serializer in question. Notice we are defining this HyperlinkedIdentityField ON THE SERIALIZER of the view that is throwing the error. If my error were "Could not resolve URL for hyperlinked relationship using view name "study-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field." I could fix this with the following code.

My ModelViewSet (the custom get_queryset is why I had to add the base_name to the router.register() in the first place):

class StudyViewSet(viewsets.ModelViewSet):
    serializer_class = StudySerializer

    '''custom get_queryset'''
    def get_queryset(self):
        queryset = Study.objects.all()
        return queryset

My router registration for this ModelViewSet in urls.py:

router.register(r'studies', views.StudyViewSet, base_name='studies')

AND HERE'S WHERE THE MONEY IS! Then I could solve it like so:

class StudySerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="studies-detail")
    class Meta:
        model = Study
        fields = ('url', 'name', 'active', 'created',
              'time_zone', 'user', 'surveys')

Yep. You have to explicitly define this HyperlinkedIdentityField on itself for it to work. And you need to make sure that the view_name defined on the HyperlinkedIdentityField is the same as you defined on the base_name in urls.py with a '-detail' added after it.

user1847
  • 3,571
  • 1
  • 26
  • 35
  • 2
    This worked for me, however I had to put the full route `:studies-detail`. For example my if my app is called `tanks`, then the full path would be `HyperlinkedIdentityField(view_name="tanks:studies-detail")`. To figure that out I used [django-exensions](https://github.com/django-extensions/django-extensions) `show_urls` command, to see the full route and the label that the router was automatically making. – dtasev Mar 30 '18 at 12:46
25

Maybe someone can have a look at this : http://www.django-rest-framework.org/api-guide/routers/

If using namespacing with hyperlinked serializers you'll also need to ensure that any view_name parameters on the serializers correctly reflect the namespace. For example:

urlpatterns = [
    url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
    url(r'^api/', include(router.urls, namespace='api')),
]

you'd need to include a parameter such as view_name='api:user-detail' for serializer fields hyperlinked to the user detail view.

class UserSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="api:user-detail")

    class Meta:
        model = User
        fields = ('url', 'username')
polarise
  • 2,303
  • 1
  • 19
  • 28
JackPy
  • 350
  • 3
  • 6
  • 2
    Summarized, giving your api a namespace causes the error in the title, you likely don't want to do it unless you want to change it in many places. – Mark Nov 15 '16 at 20:58
  • worked for me! my `urls.py` was double nested in in my `newsite` project: (1) `newsite/urls.py` (created by django) (2) `polls/urls.py` (3) `polls/api/v1/urls.py` ............ I have to mention nested name using `url = serializers.HyperlinkedIdentityField(view_name="polls:polls_api:user-detail")` – Grijesh Chauhan Nov 26 '18 at 12:29
13

This code should work, too.

class BottleSerializer(serializers.HyperlinkedModelSerializer):

  user = UserSerializer()

  class Meta:
    model = Bottle
    fields = ('url', 'wine', 'user')
Infinite Recursion
  • 6,511
  • 28
  • 39
  • 51
caglar
  • 380
  • 3
  • 8
  • 3
    Worth noting that `UserSerializer` must be implemented (it isn't ready to import), as shown in http://www.django-rest-framework.org/api-guide/serializers/ – Caumons Dec 29 '14 at 10:11
  • This worked for me, but for it to work I had to change router.register(r'bottles', views.BottleViewSet, base_name='bottles') to router.register(r'bottles', views.BottleViewSet). I don't know why this change was required. – manpikin Sep 25 '19 at 08:34
9

Today, I got the same error and below changes rescue me.

Change

class BottleSerializer(serializers.HyperlinkedModelSerializer):

to:

 class BottleSerializer(serializers.ModelSerializer):
Manish Pal
  • 292
  • 2
  • 6
  • 20
8

I ran into this error after adding namespace to my url

 url('api/v2/', include('api.urls', namespace='v2')),

and adding app_name to my urls.py

I resolved this by specifying NamespaceVersioning for my rest framework api in settings.py of my project

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.NamespaceVersioning'}
  • This solved my issue, ty. What does this do under the hood though? I did the same as in bovenson's & user1847's answers and as in this [issue](https://github.com/encode/django-rest-framework/issues/2760#issuecomment-112196186), but it only solved some of the namespace URL issues for me, e.g. "database:entry-detail" was found just fine, but it was still looking for "measurement-detail", even though I originally copy-pasted the two serializers. Some part of DRF was still referring to the old "measurement-detail", even though I only had "database:measurement-detail" **everywhere** in my project. – Lotramus Apr 20 '22 at 16:52
  • I believe this is the correct solution if you want to namespace a DRF router. – mihow Jul 04 '23 at 05:38
7

TL;DR: It may be as simple as removing a trailing 's' from the router basename. No need to define a url field in your serializer.

For the original poster, the issue was resolved simply by registering the UserViewSet, as suggested in the top answer.

However, if anyone else has this issue even with all ViewSets registered, I think I've figured out what's going wrong, and I've found a solution that's cleaner than a lot of the others here.

In my case, I encountered this issue after trying to create a ViewSet with a custom get_queryset() function. When I replaced the ViewSet's queryset field with a custom get_queryset() function, I was then hit with this error:

AssertionError: `basename` argument not specified, and could not automatically determine the name from the viewset, as it does not have a `.queryset` attribute.

So, of course, I went to urls.py and modified my registration to include a basename as such:

router.register(r'messages', MessageViewSet, basename='messages')

But then I was hit with this error (as we see in the original post):

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

After reading the DRF docs on routers, I learned that the router automatically generates two url patterns for you, which have names:

  1. 'basename-list'
  2. 'basename-detail'

Because I set my basename='messages' (note the 's' at the end), my url patterns were named:

  1. 'messages-list'
  2. 'messages-detail'

Since DRF was looking a url pattern named 'message-detail' (note here the lack of 's'), I realized that I could resolve this simply by removing the trailing 's' from my basename as such:

router.register(r'messages', MessageViewSet, basename='message')

My final serializer and ViewSet implementations were as simple as this!

class MessageSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Message
        fields = ['url', 'message', 'timestamp', 'sender', ...]

class MessageViewSet(viewsets.ModelViewSet):
    serializer_class = MessageSerializer

    def get_queryset(self):
        return Message.objects.filter(...)
Ikaia Chu
  • 71
  • 2
  • 3
  • Amazing thanks! I got this error after following the official tutorial part 6 of Django REST framework which has this exact bug: https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#using-routers – Géry Ogam Aug 08 '22 at 15:44
3

It appears that HyperlinkedModelSerializer do not agree with having a path namespace. In my application I made two changes.

# rootapp/urls.py
urlpatterns = [
    # path('api/', include('izzi.api.urls', namespace='api'))
    path('api/', include('izzi.api.urls')) # removed namespace
]

In the imported urls file

# app/urls.py
app_name = 'api' // removed the app_name

Hope this helps.

Cody Wikman
  • 829
  • 10
  • 13
2

Same Error, but different reason:

I define a custom user model, nothing new field:

from django.contrib.auth.models import (AbstractUser)
class CustomUser(AbstractUser):
    """
    custom user, reference below example
    https://github.com/jonathanchu/django-custom-user-example/blob/master/customuser/accounts/models.py

    # original User class has all I need
    # Just add __str__, not rewrite other field
    - id
    - username
    - password
    - email
    - is_active
    - date_joined
    - method, email_user
    """

    def __str__(self):
        return self.username

This is my view function:

from rest_framework import permissions
from rest_framework import viewsets
from .models import (CustomUser)
class UserViewSet(viewsets.ModelViewSet):
    permission_classes = (permissions.AllowAny,)
    serializer_class = UserSerializer

    def get_queryset(self):
        queryset = CustomUser.objects.filter(id=self.request.user.id)
        if self.request.user.is_superuser:
            queryset = CustomUser.objects.all()
        return queryset

Since I didn't give queryset directly in UserViewSet, I have to set base_name when I register this viewset. This is where my error message caused by urls.py file:

from myapp.views import (UserViewSet)
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, base_name='customuser')  # <--base_name needs to be 'customuser' instead of 'user'

You need a base_name same as your model name - customuser.

Belter
  • 3,573
  • 5
  • 42
  • 58
2

If you're extending the GenericViewSet and ListModelMixin classes, and have the same error when adding the url field in the list view, it's because you're not defining the detail view. Be sure you're extending the RetrieveModelMixin mixin:

class UserViewSet (mixins.ListModelMixin,
                   mixins.RetrieveModelMixin,
                   viewsets.GenericViewSet):
Rowinson Gallego
  • 850
  • 1
  • 9
  • 14
1

I had the same problem , I think you should check your

get_absolute_url

object model's method input value (**kwargs) title. and use exact field name in lookup_field

hassanzadeh.sd
  • 3,091
  • 1
  • 17
  • 26
  • my model doesn't have a `get_absolute_url`. it's not declared for django User model too. While I have issue with my own model, I don't have with User model. – mahyard Oct 20 '20 at 15:36
1

A bit late but in Django 3 and above, include doesn't support namespace without specifying the app_name. Checking the source code for include, we see that the condition

if namespaces and not app_name:
    ....

is checked. And still from the source code, app_name is gotten like;

urlconf_module, app_name = arg

where arg is the first argument of the include. This tells us that, our include should be defined as

include((app.urls, app_name), namespace='...')

Example

Say you have a project myproject and an app myapp. Then you want to establish an address. You should use a viewset and define a router as below

myapp.urls

router.register('address', exampleviewset, basename='address')

myproject.urls

path('api/v1/', include(('myapp.urls', 'myapp'), namespace='myapp')),

serializers.py

class AddressSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="myapp:address-detail")
    class Meta:
        model = Address
        fields = ('url',...)

Apparently, we can't use fields='__all__'. We must include url explicitly and list the remaining fields we need.

0

I ran into the same error while I was following the DRF quickstart guide http://www.django-rest-framework.org/tutorial/quickstart/ and then attempting to browse to /users. I've done this setup many times before without problems.

My solution was not in the code but in replacing the database.

The difference between this install and the others before was when I created the local database.

This time I ran my

./manage.py migrate
./manage.py createsuperuser

immediately after running

virtualenv venv
. venv/bin/activate
pip install django
pip install djangorestframework

Instead of the exact order listed in the guide.

I suspected something wasn't properly created in the DB. I didn't care about my dev db so I deleted it and ran the ./manage.py migrate command once more, created a super user, browsed to /users and the error was gone.

Something was problematic with the order of operations in which I configured DRF and the db.

If you are using sqlite and are able to test changing to a fresh DB then it's worth an attempt before you go dissecting all of your code.

0

Bottle = serializers.PrimaryKeyRelatedField(read_only=True)

read_only allows you to represent the field without having to link it to another view of the model.

0

I got that error on DRF 3.7.7 when a slug value was empty (equals to '') in the database.

mrmuggles
  • 2,081
  • 4
  • 25
  • 44
0

I ran into this same issue and resolved it by adding generics.RetrieveAPIView as a base class to my viewset.

Jace Browning
  • 11,699
  • 10
  • 66
  • 90
0

I was stuck in this error for almost 2 hours:

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

When I finally get the solution but I don't understand why, so my code is:

#models.py
class Users(models.Model):
    id          = models.AutoField(primary_key=True)
    name        = models.CharField(max_length=50, blank=False, null=False)
    email       = models.EmailField(null=False, blank=False) 
    class Meta:
        verbose_name = "Usuario"
        verbose_name_plural = "Usuarios"

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


#serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Users
        fields = (
            'id',
            'url',
            'name',        
            'email',       
            'description', 
            'active',      
            'age',         
            'some_date',   
            'timestamp',
            )
#views.py
class UserViewSet(viewsets.ModelViewSet):
    queryset = Users.objects.all()
    serializer_class = UserSerializer

#urls_api.py
router = routers.DefaultRouter()
router.register(r'users',UserViewSet, base_name='users')

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

but in my main URLs, it was:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #api users
    url(r'^api_users/', include('usersApi.users_urls', namespace='api')),

]

So to finally I resolve the problem erasing namespace:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #api users
    url(r'^api_users/', include('usersApi.users_urls')),

]

And I finally resolve my problem, so any one can let me know why, bests.

Cam T
  • 1,965
  • 2
  • 8
  • 8
0

If you omit the fields 'id' and 'url' from your serializer you won't have any problem. You can access to the posts by using the id that is returned in the json object anyways, which it makes it even easier to implement your frontend.

0

It is worth noting that if you create an action with detail=False (typo?) then this errors will be raised, replace it with detail=True:

@action(detail=True)
...
Drdilyor
  • 1,250
  • 1
  • 12
  • 30
0

I wanted to stay with everything as-is out of the box so I just added a User serializer:

class UserSerializer(serializers.HyperlinkedModelSerializer):

class Meta:
    model = User
    fields = ['id', 'username']

A Viewset:

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

And added to urls:

router.register(r'users', UserViewSet)
elad silver
  • 9,222
  • 4
  • 43
  • 67
0

From DRF Docs:

drf docs note source

Note: If using namespacing with hyperlinked serializers you'll also need to ensure that any view_name parameters on the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as view_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view.

The automatic view_name generation uses a pattern like %(model_name)-detail. Unless your models names actually clash you may be better off not namespacing your Django REST Framework views when using hyperlinked serializers.

Solution

example of setting view_name

from rest_framework import serializers
from myapp.models import Post
from django.contrib.auth.models import User


class PostSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name="api:post-detail")
    author = serializers.HyperlinkedRelatedField(view_name="api:user-detail", read_only=True)
    viewers = serializers.HyperlinkedRelatedField(view_name="api:user-detail", read_only=True, many=True)

    class Meta:
        model = Post
        fields = ('id', 'title', 'url', 'author', 'viewers')


class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = "__all__"