如何通过django-import-export为每个关联字段值创建独立列
如何用django-import-export为每个票种生成单独列?
模型结构
class Event(models.Model): name = models.CharField(max_length=128) class PaymentRecord(models.Model): event = models.ForeignKey(Event, on_delete=models.SET_NULL, null=True, related_name='payments') payment_time = models.DateTimeField(auto_add_now=True) # 其他支付详情和用户标识字段 class TicketPurchase(models.Model): payment_record = models.ForeignKey(PaymentRecord, on_delete=models.CASCADE, related_name='tickets') tier_name = models.CharField(max_length=32) ticket_count = models.IntegerField()
需求说明
导出某活动的PaymentRecord数据时,需要为该活动的每个票种(如Silver、Gold、Platinum)生成单独列,显示对应支付记录的购票数量,预期输出格式如下:
| Event Name | Payment Time | Silver Tickets | Gold Tickets | Platinum Tickets |
|---|---|---|---|---|
| Event 1 | # time 1 | 3 | 0 | 0 |
| Event 1 | # time 2 | 1 | 1 | 1 |
当前实现是将所有票种信息汇总在单个列中,代码如下:
class PaymentRecordsResource(resources.ModelResource): class Meta: model = PaymentRecord ticket_count = Field(column_name='Tickets') def dehydrate_ticket_count(self, obj): tickets = '' for ticket in obj.tickets.all(): tickets += f'{ticket.tier_name}: {ticket.ticket_count}\n' if not tickets: tickets = '-' return tickets.strip()
解决方案
通过动态添加字段和预查询优化即可实现需求,完整代码示例如下:
from import_export import resources, fields from .models import PaymentRecord, TicketPurchase class PaymentRecordsResource(resources.ModelResource): event_name = fields.Field(column_name='Event Name') class Meta: model = PaymentRecord fields = ('payment_time',) # 基础字段 def __init__(self, event_id=None, *args, **kwargs): super().__init__(*args, **kwargs) self.event_id = event_id # 获取当前活动的所有唯一票种 if event_id: tier_names = TicketPurchase.objects.filter( payment_record__event_id=event_id ).values_list('tier_name', flat=True).distinct() # 为每个票种动态添加字段 for tier in tier_names: field_name = f"{tier.lower()}_tickets" # 添加列字段 setattr(self, field_name, fields.Field(column_name=f"{tier} Tickets")) # 动态生成对应字段的脱水方法 def make_dehydrate_func(target_tier): def dehydrate_func(self, obj): ticket = obj.tickets.filter(tier_name=target_tier).first() return ticket.ticket_count if ticket else 0 return dehydrate_func setattr(self, f"dehydrate_{field_name}", make_dehydrate_func(tier)) def dehydrate_event_name(self, obj): return obj.event.name if obj.event else "" def get_queryset(self): queryset = super().get_queryset() # 预加载关联的购票记录,避免N+1查询 queryset = queryset.prefetch_related('tickets') # 过滤指定活动的支付记录 if self.event_id: queryset = queryset.filter(event_id=self.event_id) return queryset
使用方式
导出时传入目标活动ID初始化Resource即可:
# 导出ID为1的活动支付数据 resource = PaymentRecordsResource(event_id=1) dataset = resource.export() # 后续可将dataset导出为Excel/CSV等格式
关键说明
- 动态字段基于活动ID生成,只会包含当前活动存在的票种列
prefetch_related预加载关联数据,解决循环访问关联对象导致的性能问题- 无对应票种的支付记录会显示0,完全匹配预期输出格式
内容的提问来源于stack exchange,提问作者Hrushikesh Vaidya
相关产品推荐
相关产品推荐

