When using django-simple-history and DRF, how can I create an extra action to access the history of an object?

Question:

I’m new to Python, Django and DRF but I’ve gone through all the tutorials needed to set up a basic REST server and for this example, we’ll pretend that it only serves one thing: a list of customers. Additionally, I have installed django-simple-history and registered my Customer model with it.

This is the code that I’ve written so far and everything seems to work just fine.

api/urls.py

from django.urls import path, include
from rest_framework import routers
import api.views as views

router = routers.DefaultRouter(trailing_slash=False)

router.register('customers', views.CustomerView)
router.register('customerhistory', views.CustomerHistoryView)

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

api/views.py

from rest_framework import viewsets
from retail.models.customers import Customer

class CustomerView(viewsets.ModelViewSet):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer
    filterset_fields = '__all__'
    search_fields = ['name']


class CustomerHistoryView(viewsets.ModelViewSet):
    queryset = Customer.history.all()
    serializer_class = CustomerHistorySerializer
    filterset_fields = '__all__'

api/serializers.py

from rest_framework import serializers
from retail.models.customers import Customer

class CustomerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer
        fields = '__all__'


class CustomerHistorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer.history.model
        fields = '__all__'

As it is, when I go to api/customerhistory/1, it gives me a lovely list of all the history for customer 1.

Question

What I’d like to do is access the same list by going to api/customer/1/history.

My Attempt So Far…

To do this, I’m removing the CustomerHistoryView and adding an extra action to the CustomerView class.

api/views.py

from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from retail.models.customers import Customer

class CustomerView(viewsets.ModelViewSet):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer
    filterset_fields = '__all__'
    search_fields = ['name']

    @action(detail=True)
    def history(self):
        history = Customer.history.all()
        serializer = CustomerHistorySerializer(history, many=True)
        return Response(serializer.data)

This produces the following error:

Exception Type: TypeError at /api/customers/1/history
Exception Value: history() got multiple values for argument 'pk'

What am I doing wrong?

Asked By: Kristin Green

||

Answers:

It looks like your history method is missing parameters.

Here are the docs for extra actions on viewsets.

@action(detail=True)
def history(self, request, pk=None):
    history = Customer.history.all()
    # ...

Note the extra parameters request and pk=None to history.

Answered By: stefanw