If I have a viewset with the following code:
class ExtraRouteViewset(viewsets.GenericViewSet):
@list_route(methods=['get'])
def somefunction(self, request):
return Response({
'key': 'value',
'reverse': reverse('extraroute-somefunction'),
})
@list_route(methods=['get'], url_path='arguments/(?P<thing>[^/]+)')
def arguments(self, request, thing):
return Response({
'key': thing,
'reverse': reverse('extraroute-arguments', kwargs={'thing': 'something'}),
})
I would expect both methods to work. However, the second reverse
raises a NoReverseMatch
. Examining the url patterns (by navigating to a non-existing url) shows the following url patterns:
^demo/ ^ ^extraroute/arguments/(?P<thing>[^/]+)/$ [name='extraroute-arguments/(?P<thing>[^/]+)']
^demo/ ^ ^extraroute/arguments/(?P<thing>[^/]+)/\.(?P<format>[a-z0-9]+)$ [name='extraroute-arguments/(?P<thing>[^/]+)']
^demo/ ^ ^extraroute/somefunction/$ [name='extraroute-somefunction']
^demo/ ^ ^extraroute/somefunction/\.(?P<format>[a-z0-9]+)$ [name='extraroute-somefunction']
The view name seems to be extraroute-arguments/(?P<thing>[^/]+)
instead of extraroute-arguments
? And indeed, if I use reverse('extraroute-arguments/(?P<thing>[^/]+)', kwargs={'thing': 'something'})
it works. Am I missing something very obvious here, or is this a bug in django-rest-framework
?
This is using Django 1.8a and django-rest-framework 3.0.5.
Well, in the second example, you send url_path='arguments/(?P<thing>[^/]+)'
. Django REST framework use it to create both an URL pattern
and a URL Name
. But the implementation is too pure to strip the regex expression.
#inside urls.py
router = SimpleRouter()
router.routes.append(
Route(
url=r'^{prefix}/arguments/(?P<thing>[^/]+)$',
name='{basename}-arguments',
mapping={
'get': 'arguments',
},
initkwargs={}
),
)
router.register('extraroute', ExtraRouteViewset, base_name='extraroute')
urlpatterns = router.urls
and then in the views.py remove the @list_route
decorators since its no more needed (and will cause a route clash)
#inside views.py
class ExtraRouteViewset(viewsets.GenericViewSet):
#...
def arguments(self, request, thing):
return Response({
'key': thing,
'reverse': reverse('extraroute-arguments', kwargs={'thing': 'something'}),
})
I have to mention that this actually adds a hardcoded Route
pattern inside the default SimpleRouter
(which has patterns for list, create, retrieve, update, partial update, destroy). This means that every viewset which get registered via this router instance will be able to implement an arguments
method and this method will be called when the regex match it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With