Search code examples
djangodjango-rest-frameworkdjango-filterdjango-rest-viewsets

How to use lookup_field along with FilterSet in Django Rest Framework?


I am working on developing an API for a project and it contains a course list page where

  1. All the courses offered by a particular college is listed.
  2. Should support retrieving of a specific course using lookup_field as course_slug.
  3. Supports filtering courses based on some other parameters which I am implementing with the help of Django FilterSet along with filterset_class (filterset_class).

The problem is, once the courses are filtered based on the requirements, the query params is included in the URL as well. Now, I am not able do the retrieve operation on it, as the URL cannot recognize the lookup_field added and mistakes it to a pattern in query params.

ViewSet.py

class CollegeCourseViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = CourseSerializer
    lookup_field = 'slug'
    filter_backends = (SearchFilter, DjangoFilterBackend,)
    search_fields = ['name']
    filterset_class = CourseFilter

    def get_queryset(self):
        queryset = Course.objects.all()
        return queryset.filter(college_courses__college__slug=self.kwargs['college_slug'])

    def get_object(self):
        obj = super().get_object()
        if self.action == 'retrieve':
            obj.additional = CollegeCourse.objects.filter(course=obj, college__slug=self.kwargs['college_slug']).first()
        return obj

    def get_serializer_class(self):
        if self.action == 'retrieve':
            return CollegeCourseSerializer
        return super().get_serializer_class()

filters.py

class CourseFilter(filters.FilterSet):
name = filters.MultipleChoiceFilter(field_name='college_courses__stream__name',
                                    choices=choices("STREAM_NAME_CHOICES"))
category = filters.MultipleChoiceFilter(field_name='college_courses__stream__category',
                                        choices=choices("STREAM_CATEGORY_CHOICES"))
program = filters.MultipleChoiceFilter(field_name='college_courses__stream__program',
                                       choices=choices("STREAM_PROGRAM_CHOICES"))

class Meta:
    model = Course
    fields = [
        'name',
        'category',
        'program'
    ]

urls.py

router = routers.DefaultRouter()
router.register(r'courses', CollegeCourseViewSet, basename='college_course')

urlpatterns = [
    path('api/college/v1/colleges/<slug:college_slug>/', include(router.urls)),
]

Suppose I filter based on category = Agriculture, I get a list of three courses. Now, I would like to retrieve one of the courses using its slug. But here is the Bad Request message

URL

GET /api/college/v1/colleges/clslug1/courses/?category=Agriculture/crslug2

crslug2 is the slug of the course.

Message

"Select a valid choice. Agriculture/crslug2 is not one of the available choices."

Is there any way to add the lookup_field after the query params?

Thanks in advance.


Solution

  • Okay so answering my own post. Basically everything was fine except that the URL was not RESTful.

    The lookup_field must be given before any query params in RESTful apis.

    So the correct format is

    GET /api/college/v1/colleges/clslug1/courses/crslug2/?category=Agriculture