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.