Home > Enterprise >  Django REST Framework reverse() not finding match for router's "{basename}-detail"
Django REST Framework reverse() not finding match for router's "{basename}-detail"

Time:04-06

This question is extremely similar to: How to fix this NoReverseMatch exception in Django rest frameworks routers? but that hasn't been answered/resolved and after a lot of investigating here I am looking for help.

I am trying to build an API with test-driven development. As common practice I begin my tests by saving constant variables for the URLS using django.urls.reverse()

The problem is reverse('{app}:{basename}-list') works fine, but reverse('{app}:{basename}-detail') throws the exception:

django.urls.exceptions.NoReverseMatch: Reverse for 'designer-detail' with no arguments not found. 2 pattern(s) tried: ['api/design/designer/(?P<pk>[^/.] )\\.(?P<format>[a-z0-9] )/?$', 'api/design/designer/(?P<pk>[^/.] )/$']

My test.py: (notice the list url runs first and throws no exception)

from rest_framework.test import APITestCase, APIClient
from django.contrib.auth import get_user_model
from rest_framework_simplejwt.tokens import RefreshToken
from django.urls import reverse
from rest_framework import status
from designs.models import Designer
from designs.serializers import DesignerSerializer


DESIGNER_LIST_URL = reverse('designs:designer-list')
DESIGNER_DETAIL_URL = reverse('designs:designer-detail')

My app/urls.py:

from rest_framework.routers import DefaultRouter
from designs.views import DesignerViewset


app_name = 'designs'

router = DefaultRouter()
router.register(r'designer', DesignerViewset, 'designer')

urlpatterns = router.urls

My project/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/user/', include('user.urls')),
    path('api/design/', include('designs.urls'))
]

My serializers.py:

from rest_framework import serializers
from designs.models import Designer


class DesignerSerializer(serializers.ModelSerializer):
    """
    Model serializer for Designer.
    """
    class Meta:
        model = Designer
        fields = "__all__"

And my views.py:

from rest_framework.viewsets import ModelViewSet
from designs.models import Designer
from designs.serializers import DesignerSerializer


class DesignerViewset(ModelViewSet):
    """
    ModelViewSet for the Designer model.
    """
    queryset = Designer.objects.all()
    serializer_class = DesignerSerializer

I have tried tinkering with the basename, the router, the app_name, the urlpatterns, but nothing seems to work. I have gone through the official docs on vanilla django and DRF. Whats most confusing is why "designs:designer-list" runs as expected but "designs:designer-detail" fails? The DefaultRouter is supposed to take care of this. I manually tested this last comparison in the Django shell.

What am I missing?

CodePudding user response:

The reason this raises an error is because the URL has a pk parameter, and you thus need to provide a value for that in order to define the URL path.

You can pass the value of named parameters with the kwarg=… parameter, so:

reverse('designs:designer-detail', kwargs={'pk': 42})

where 42 is the primary key of the item for which you want to obtain details.

  • Related