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

如何通过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 NamePayment TimeSilver TicketsGold TicketsPlatinum Tickets
Event 1# time 1300
Event 1# time 2111

当前实现是将所有票种信息汇总在单个列中,代码如下:

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 01:35:26