Home > Software design >  Paginated REST API endpoint for django-simple-history records, using Django REST Framework
Paginated REST API endpoint for django-simple-history records, using Django REST Framework

Time:10-29

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).

CodePudding user response:

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.

  • Related