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
相关产品推荐
相关产品推荐

