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

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?



Solution 1:[1]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Willem Van Onsem