[Fixed]-Django REST framework – multiple lookup fields?


1. Add the new routes

# in urls.py

urlpatterns = [
    url(r'vessels/id/(?P<pk>[0-9A-Fa-f\-]+)/$', StarshipDetail.as_view(), name='starship-detail-pk'),
    url(r'vessels/hull/(?P<hull_no>[0-9A-Za-z]+)/$', StarshipDetail.as_view(), name='starship-detail-hull'),

Tweak the regex for hull_no as you want. Note that I gave distinct names to each route, starship-detail-pk and starship-detail-hull.

2. Add the hull field in the serializer

# in serializers.py

class StarshipListSerialiser(HyperlinkedModelSerializer):
    uri = UUIDHyperlinkedIdentityField(view_name='starship-detail-pk', format='html')
    hull_no = UUIDHyperlinkedIdentityField(view_name='starship-detail-hull', format='html', lookup_field='hull_no')

    class Meta:
         model = Starship
         fields = ('uri', 'name', 'hull_no')

3. Modify the view so it can also resolve objects based on hull

# in serializers.py

from django.shortcuts import get_object_or_404

from rest_framework.views import APIView, Response

from starwars.serializers import StarshipDetailSerialiser
from starwars.models import Starship

class StarshipDetail(APIView):

    def get(self, request, pk=None, hull_no=None, format=None):
        lookup = {'hull_no': hull_no} if pk is None else {'pk': pk}
        vessel = get_object_or_404(Starship, **lookup)
        serializer = StarshipDetailSerialiser(vessel, context={'request': request})
        return Response(serializer.data)

That should be enough to get you going with the detail view:

drf screencap

As a final note, you should be aware that it’s not RESTful for the same resource to be available at two different URLs like this. Perhaps, as an alternate design decision, you might like to consider just defining the “one true route” for a resource, and adding in a “convenience” redirect from the other locator to the canonical URL.


Leave a comment