Django Rest Framework routing with primary key prefix

Viewed 674

I'm using DRF DefaultRouter as follows.

# urls.py
router = DefaultRouter()
router.register('book', BookingViewSet, basename='booking')
router.register('book/<int:bk>/reservation', ReservationViewSet, basename='reservation')

urlpatterns = [
   path('', include(router.urls)),
]

# view
class ReservationViewSet(viewsets.ModelViewSet):
    serializer_class = ReservationSerializer
    queryset = Reservation.objects.all() # for testing only

But when I visit the URL /book/1/reservation/ it says no url pattern found.

lending/ ^book/<int:bk>/reservation/$ [name='reservation-list']
lending/ ^book/<int:bk>/reservation\.(?P<format>[a-z0-9]+)/?$ [name='reservation-list']
lending/ ^book/<int:bk>/reservation/(?P<pk>[^/.]+)/$ [name='reservation-detail']
lending/ ^book/<int:bk>/reservation/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='reservation-detail'] 

The current path, lending/book/1/reservation/, didn’t match any of these. 

I'm using bk to capture book id.

2 Answers

That's because it implements the <int:bk> as regex, so without any interpretation. Probably the simplest way to do this is with two routers:

router1 = DefaultRouter()
router1.register('book', BookingViewSet, basename='booking')

router2 = DefaultRouter()
router2.register('reservation', ReservationViewSet, basename='reservation')

urlpatterns = [
   path('', include(router1.urls)),
   path('book/<int:bk>/', include(router2.urls)),
]

In the ReservationViewSet you can then for example filter with bk:

class ReservationViewSet(viewsets.ModelViewSet):
    serializer_class = ReservationSerializer

    def get_queryset(self):
        return Reservation.objects.filter(book_id=self.kwargs['bk'])

Just wanted to add this to the accepted answer ... You can also do this with the same router, just embed the pk in the URL in regex form as the error message suggests:

# urls.py
router = DefaultRouter()
router.register('book', BookingViewSet, basename='booking')
router.register('book/(?P<bk>[^/.]+)/reservation', ReservationViewSet, basename='reservation')

urlpatterns = [
   path('', include(router.urls)),
]

You can then access the bk argument in your view using self.kwargs['bk'].

Related