Paginated REST API endpoint for django-simple-history records, using Django REST Framework

471 Views Asked by At

I'm using django-simple-history to track model changes.

It's easy enough to get all history records for a model class or a model instance:

poll = Poll.objects.create(question="what's up?", pub_date=datetime.now())
poll.history.all()
# and
Choice.history.all()

But what I'm looking to do is to have endpoints like /history/model and /history/model/1 that returns paginated history records for either the class or class instance.

Now I've already got Django REST Framework (DRF) set up with pagination, and have all my views paginated, something like:

class MyModelViewSet(viewsets.ModelViewSet):
    serializer_class = MyModelSerializer
    permission_classes = [permissions.IsAuthenticated]
    queryset = MyModel.objects.all()

class MyModelSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = MyModel
        fields = ["name"]

class MyModel(models.Model):
    name = models.CharField(max_length=100, unique=True)

That all works fine.

How can I paginate the django-simple-history records?

Can I create a simple history serializer? Because there's not exactly a simple history model I can use (I think).

2

There are 2 best solutions below

0
ocobacho On BEST ANSWER

I am going to use the Poll model for this example and I have created an app called polls.

class Poll(models.Model):
   question = models.CharField(max_length=200)
   pub_date = models.DateTimeField('date published', auto_now_add=True)
   history = HistoricalRecords()

After you migrate a new table is created call 'polls_historicalpoll'. You can use inspectdb command to create a model for this or simple created manually by looking at the field in the database. For example.

class HistoricalPoll(models.Model):
    id = models.BigIntegerField()
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField()
    history_id = models.AutoField(primary_key=True)
    history_date = models.DateTimeField()
    history_change_reason = models.CharField(max_length=100, blank=True, null=True)
    history_type = models.CharField(max_length=1)
    history_user = models.ForeignKey(get_user_model(), models.DO_NOTHING, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'polls_historicalpoll'

Now you can create a serializer for this model:

#serializers.py
from rest_framework import serializers
from .models import HistoricalPoll

class HistoricalPollSerializer(serializers.ModelSerializer):

    class Meta:
        model = HistoricalPoll
        fields = '__all__'

Now that you have the serializer you can create a ModelViewset. To make things simple I have overwriten the get_queryset methode to filter by poll id but you can also use djangofilterbackend see https://www.django-rest-framework.org/api-guide/filtering/#djangofilterbackend

#views.py
from rest_framework import viewsets, permissions
from polls.models import HistoricalPoll
from polls.serializers import HistoricalPollSerializer


class HistoricalPollViewSet(viewsets.ModelViewSet):
    serializer_class = HistoricalPollSerializer
    permission_classes = [permissions.IsAuthenticated]
    queryset = HistoricalPoll.objects.all()

    def get_queryset(self):
        poll_id = self.request.GET.get('poll_id')
        return HistoricalPoll.objects.filter(id=poll_id)

Depending on your urls you can access all the history of polls like this: http://127.0.0.1:8000/api/v1/polls/historical_polls

Or you can filter by poll id like this: http://127.0.0.1:8000/api/v1/polls/historical_polls?poll_id=3

This worked perfectly for me. Let me know if it helps you.

0
Polla Toube On

django-simple-history provide a way a way for interacting with history model. Below is an example of how you can create a serailizer.

class PollHistorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Poll.history.model
        fields = '__all__'

Notice the Poll.history.model used to access the history model. You can then use this serailiser to paginate the model history.

class PollHistoryPaginatedView(generics.ListAPIView):
    pagination_class = YourPaginationClass
    serializer_class = PollHistorySerializer
    
    def get_queryset(self):
        return Poll.history.all()