djangodjango-rest-frameworkdjango-simple-history

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


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


Solution

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