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

Django-simple-history序列化多对多关联对象问题求助

Django Simple History 多对多历史记录关联Employee序列化问题

我使用django-simple-history==3.2.0,现有模型定义如下:

class Employee(models.Model):
  last_name: str | None = models.CharField(max_length=100, null=True)  # type: ignore
  first_name: str | None = models.CharField(max_length=100, null=True)  # type: ignore
  middle_name: str | None = models.CharField(max_length=100, null=True)  # type: ignore
  job_title: str | None = models.CharField(max_length=512, null=True)  # type: ignore  
  
  class Meta:
    app_label = "task_flow"

class Assignment(models.Model):
  overdue: int = models.IntegerField(null=True) # type: ignore
  # ManyToManyField
  executors: QuerySet[Employee] = models.ManyToManyField(Employee, related_name="executor_assignments", blank=True)  # type: ignore
  coordinators: QuerySet[Employee] = models.ManyToManyField(Employee, related_name="coordinator_assignments", blank=True)  # type: ignore
  approvers: QuerySet[Employee] = models.ManyToManyField(Employee, related_name="approver_assignments", blank=True)  # type: ignore
  # ForeignKey
  status: Status | None = models.ForeignKey(Status, on_delete=RESTRICT, related_name="assignments")  # type: ignore
  access: Access = models.ForeignKey(Access, on_delete=RESTRICT, related_name="assignments", default=Access.Choices.general, null=True, blank=False)  # type: ignore
  document_type: DocumentType | None = models.ForeignKey(DocumentType, on_delete=SET_NULL, null=True, related_name="assignments")  # type: ignore
  domain: Domain = models.ForeignKey(Domain, on_delete=RESTRICT,  null=False, related_name="assignments", default=Domain.Choices.internal)  # type: ignore
  source: str | None = models.CharField(max_length=2048, null=True, blank=True)  # type: ignore
  created_at: datetime = models.DateTimeField(auto_now_add=True)  # type: ignore
  updated_at: datetime = models.DateTimeField(auto_now=True)  # type: ignore
  history = HistoricalRecords(m2m_fields=[executors, coordinators, approvers])

我需要序列化Assignment.history.all()查询集,同时包含关联Employee的所有字段,期望输出格式如下:

"results": [
        {
            "id": 551,
            "overdue": 243,
            "executors": [
                {
                    "id": 32,
                    "last_name": "Копытов",
                    "first_name": "О.",
                    "middle_name": "В.",
                    "job_title": "Генеральный директор ТОО \"Корпорация Казахмыс\"",
                    "user": 470,
                    "domain": 1
                },
             ...
         }
]

但调用instance.approvers.all()时,返回的是task_flow_historicalassignment_approvers表的实例,而非Employee对象,该如何解决?


解决方法

1. 直接获取中间表关联的Employee对象

django-simple-history生成的多对多历史中间表,会自动包含小写类名的字段(比如employee),指向原始的Employee模型实例。可以通过该字段直接获取员工对象:

# 假设instance是HistoricalAssignment实例
approver_employees = [item.employee for item in instance.approvers.all()]

2. 在DRF序列化器中自定义字段处理

如果使用Django REST Framework进行序列化,可在序列化器中定义SerializerMethodField,手动转换中间表实例为Employee数据:

from rest_framework import serializers
from task_flow.models import HistoricalAssignment, Employee

class EmployeeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Employee
        fields = ['id', 'last_name', 'first_name', 'middle_name', 'job_title', 'user', 'domain']

class HistoricalAssignmentSerializer(serializers.ModelSerializer):
    executors = serializers.SerializerMethodField()
    coordinators = serializers.SerializerMethodField()
    approvers = serializers.SerializerMethodField()

    def get_executors(self, obj):
        employees = [item.employee for item in obj.executors.all()]
        return EmployeeSerializer(employees, many=True).data
    
    def get_coordinators(self, obj):
        employees = [item.employee for item in obj.coordinators.all()]
        return EmployeeSerializer(employees, many=True).data
    
    def get_approvers(self, obj):
        employees = [item.employee for item in obj.approvers.all()]
        return EmployeeSerializer(employees, many=True).data

    class Meta:
        model = HistoricalAssignment
        fields = ['id', 'overdue', 'executors', 'coordinators', 'approvers'] # 按需添加其他字段

3. 预关联查询避免N+1性能问题

如果数据量较大,建议在查询历史记录时提前预关联中间表的Employee,减少数据库查询次数:

from django.db.models import Prefetch

historical_assignments = HistoricalAssignment.objects.prefetch_related(
    Prefetch('executors', queryset=HistoricalAssignment.executors.through.objects.select_related('employee')),
    Prefetch('coordinators', queryset=HistoricalAssignment.coordinators.through.objects.select_related('employee')),
    Prefetch('approvers', queryset=HistoricalAssignment.approvers.through.objects.select_related('employee'))
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 02:39:56