3

I have the following setup:

I want to list all holidays of a specific year. That's why I leave out the default list view and implement my own like this:

class HolidayViewSet(mixins.RetrieveModelMixin, GenericViewSet):
@list_route()
def year(self, request, year=get_today().year):
    public_holidays = self.get_queryset().filter(date__year=year)
    page = self.paginate_queryset(public_holidays)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)
    serializer = self.get_serializer(public_holidays, many=True)
    return Response(serializer.data)

If I use the default /holiday/year/ I get a result for the current year.

But whe I try to pass a parameter, I'll get a 404. The 404 page (in debug mode) even shows me the correct URL pattern:

api/v1/ ^holiday/year/$ [name='holiday-year']
api/v1/ ^holiday/year\.(?P<format>[a-z0-9]+)/?$ [name='holiday-year']

In the documentation this aspect is unfortunately not covered.

Any ideas why my route to holiday/year/2017 is not working?

4
  • Check this question: stackoverflow.com/questions/28841600/… Commented Mar 26, 2018 at 15:44
  • Thanks but it doesn't seem like a clean solution. Commented Mar 26, 2018 at 15:47
  • You are using an really out of date version of the DRF documentation. What your version of Django REST framework ? This part explains how the urls are built: django-rest-framework.org/api-guide/routers/#simplerouter Commented Mar 26, 2018 at 16:02
  • Thanks for the input. But django tells me that there is already a route. I'd like to use it without the hazzle of creating separate routes. Commented Mar 27, 2018 at 7:12

3 Answers 3

3

Ok, my workaround is using django-filter.

My filter:

class HolidayFilter(filters.FilterSet):
    """
    This filter can be used to filter holidays by different values in API views or viewsets.
    See http://django-filter.readthedocs.io/en/1.1.0/guide/rest_framework.html
    """
    year = filters.NumberFilter(name='date', lookup_expr='year')

    class Meta:
        model = Holiday
        fields = ['date']

My viewset:

class HolidayListViewSet(ModelViewSet):

    def list(self, request, *args, **kwargs):
        # Apply filters
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        # Pagination
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        # Serializer
        serializer = self.get_serializer(queryset, many=True)
        # Response
        return Response(serializer.data)

The URL:

/api/v1/holiday/?year=2016
Sign up to request clarification or add additional context in comments.

Comments

0

DRF differentiates list and detail requests. list request is not expected to have additional path parameter(i.e. /{id}), while detail request, in opposite, defined by it.

What it means is @list_route decorator creates /holiday/year endpoint, and @detail_route will create /holiday/{id}/year endpoint.

3 Comments

Well, regarding to the 404 page, I get a route as I want. Even with list_route. But how can I use it?
@Ron "I get a route as I want" -- not quite. ^holiday/year\.(?P<format>[a-z0-9]+)/?$ generates path of format holiday/year.[a-z0-9]+. Not holiday/year/{id}
I know. But unfortunately - as I pointed out - I wasn't able to use this route. I didn't try id, I tried years.
0

I think my approach is so simple but its useful. just determine your Router like below:

urlpatterns += [
    path(r'holiday/<int:year>/', TestListView.as_view()),
]

The URL:

api/v1/holiday/2016/

Then create your view like this:

class TestListView(generics.ListAPIView):
    serializer_class = TestSerializer

    def get(self, request, *args, **kwargs):
        year = kwargs.get('year', '')
        query_set = Test.objects.filter(year__exact=year) # Test is my test model
        serializer = self.get_serializer(query_set, many=True)
        return Response(serializer.data)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.