You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于DRF实现django-simple-history记录的分页REST API端点?

Django-simple-history 结合DRF实现分页历史记录端点

1. 创建历史记录序列化器

django-simple-history会为每个追踪模型自动生成对应的历史模型(例如原模型为Book,历史模型则是Book.history.model),你可以为这些历史模型编写DRF序列化器,支持单个模型或通用适配多模型:

单个模型序列化器

from rest_framework import serializers
from .models import Book

class BookHistorySerializer(serializers.ModelSerializer):
    # 自定义字段展示修改人、操作类型
    changed_by = serializers.CharField(source='history_user.username', read_only=True, allow_null=True)
    change_type = serializers.SerializerMethodField()

    def get_change_type(self, obj):
        # 将history_type转换为可读文本
        return {
            '+': '创建',
            '~': '更新',
            '-': '删除'
        }.get(obj.history_type, '未知')

    class Meta:
        model = Book.history.model
        fields = ['history_id', 'history_date', 'change_type', 'changed_by', 'name', 'price']  # 指定需要返回的字段

通用序列化器(适配多模型)

如果需要支持多个模型的历史记录序列化,可写一个动态生成序列化器的方法:

from rest_framework import serializers

class GenericHistorySerializer(serializers.ModelSerializer):
    changed_by = serializers.CharField(source='history_user.username', read_only=True, allow_null=True)
    change_type = serializers.SerializerMethodField()

    def get_change_type(self, obj):
        return {
            '+': '创建',
            '~': '更新',
            '-': '删除'
        }.get(obj.history_type, '未知')

    class Meta:
        model = None
        fields = '__all__'

def get_history_serializer(model):
    """动态生成对应模型的历史序列化器"""
    class DynamicHistorySerializer(GenericHistorySerializer):
        class Meta(GenericHistorySerializer.Meta):
            model = model.history.model
    return DynamicHistorySerializer

2. 编写分页历史视图

利用DRF的ListAPIView实现分页,由于你已配置全局分页,视图会自动继承该配置:

模型级历史列表视图(对应/history/model)

from rest_framework.generics import ListAPIView
from django.apps import apps
from rest_framework.exceptions import NotFound
from .serializers import get_history_serializer

class ModelHistoryListView(ListAPIView):
    serializer_class = None

    def get_queryset(self):
        # 从URL参数获取模型名称,转换为大写首字母格式匹配模型类
        model_name = self.kwargs['model_name'].capitalize()
        try:
            # 替换your_app_name为实际应用名
            model = apps.get_model(app_label='your_app_name', model_name=model_name)
        except LookupError:
            raise NotFound(f"模型 {model_name} 不存在")
        # 按修改时间倒序返回所有历史记录
        return model.history.all().order_by('-history_date')

    def get_serializer_class(self):
        model_name = self.kwargs['model_name'].capitalize()
        model = apps.get_model(app_label='your_app_name', model_name=model_name)
        return get_history_serializer(model)

实例级历史列表视图(对应/history/model/1)

from rest_framework.generics import ListAPIView
from django.apps import apps
from rest_framework.exceptions import NotFound
from .serializers import get_history_serializer

class InstanceHistoryListView(ListAPIView):
    serializer_class = None

    def get_queryset(self):
        model_name = self.kwargs['model_name'].capitalize()
        obj_id = self.kwargs['obj_id']
        try:
            model = apps.get_model(app_label='your_app_name', model_name=model_name)
        except LookupError:
            raise NotFound(f"模型 {model_name} 不存在")
        # 按修改时间倒序返回指定实例的所有历史记录
        return model.history.filter(id=obj_id).order_by('-history_date')

    def get_serializer_class(self):
        model_name = self.kwargs['model_name'].capitalize()
        model = apps.get_model(app_label='your_app_name', model_name=model_name)
        return get_history_serializer(model)

3. 配置URL路由

在应用的urls.py中添加路由映射:

from django.urls import path
from .views import ModelHistoryListView, InstanceHistoryListView

urlpatterns = [
    # 模型级历史记录端点
    path('history/<str:model_name>', ModelHistoryListView.as_view(), name='model-history'),
    # 实例级历史记录端点
    path('history/<str:model_name>/<int:obj_id>', InstanceHistoryListView.as_view(), name='instance-history'),
]

关键说明

  • 替换代码中的your_app_name为你的实际Django应用名称
  • 若需权限控制,可在视图类中添加permission_classes = [IsAuthenticated](需导入rest_framework.permissions.IsAuthenticated)
  • 全局分页配置会自动生效,若需单独调整分页参数,可在视图中重写pagination_class
  • 历史模型默认包含history_id、history_date、history_type、history_user(已配置用户追踪时)及原模型的所有字段

内容的提问来源于stack exchange,提问作者himmip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 19:50:26