如何用django-import-export导入CSV到一对多关联多表?求示例
Django-Import-Export 一对多关联数据导入实现方案
针对你需要将扁平CSV中的多组act_X_*字段解析为一对多关联副表的场景,以下是基于django-import-export的最佳实践实现:
1. 补全主表模型(SlateDoc)
首先确保SlateDoc模型包含CSV中对应的主表字段:
# models.py from django.db import models class SlateDoc(models.Model): filename = models.CharField(max_length=255) int_house_num = models.CharField(max_length=20) series_title = models.CharField(max_length=255) episode_title = models.CharField(max_length=255) producer = models.CharField(max_length=255) # 其他未列出的字段按需添加 def __str__(self): return self.filename class ActTimecodes(models.Model): slatedoc = models.ForeignKey( SlateDoc, on_delete=models.CASCADE, related_name="acts" ) act_number = models.IntegerField(verbose_name="Act", default=1) tc_in = models.CharField(max_length=11, default="00:00:00:00") tc_out = models.CharField(max_length=11, default="00:00:00:00") dur = models.CharField(max_length=11, default="00:00:00:00") class Meta: ordering = ["act_number", "tc_in", "tc_out"]
2. 自定义Resource类
创建SlateDocResource,重写导入逻辑以处理一对多关联:
# resources.py from import_export import resources, fields from .models import SlateDoc, ActTimecodes class SlateDocResource(resources.ModelResource): # 映射CSV列到主表字段 filename = fields.Field(column_name='filename', attribute='filename') int_house_num = fields.Field(column_name='int_house_num', attribute='int_house_num') series_title = fields.Field(column_name='series_title', attribute='series_title') episode_title = fields.Field(column_name='episode_title', attribute='episode_title') producer = fields.Field(column_name='producer', attribute='producer') class Meta: model = SlateDoc import_id_fields = ['id'] # 用id匹配或更新现有记录 fields = ('id', 'filename', 'int_house_num', 'series_title', 'episode_title', 'producer') def before_save_instance(self, instance, row, **kwargs): # 将当前CSV行数据临时存储到实例属性 instance._import_row = row def save_instance(self, instance, using_transactions=True, dry_run=False): # 先保存主表实例 super().save_instance(instance, using_transactions, dry_run) # 预览模式(dry_run)不写入副表数据 if dry_run: return row = instance._import_row act_instances = [] max_acts = 6 # 根据CSV中的最大act数量调整 # 遍历所有act编号,解析对应字段 for act_num in range(1, max_acts + 1): act_num_str = f"{act_num:02d}" tc_in = row.get(f'act_{act_num_str}_in') tc_out = row.get(f'act_{act_num_str}_out') dur = row.get(f'act_{act_num_str}_dur') # 跳过空值的act记录 if not all([tc_in, tc_out, dur]): continue # 构造副表实例 act_instances.append(ActTimecodes( slatedoc=instance, act_number=act_num, tc_in=tc_in, tc_out=tc_out, dur=dur )) # 批量创建副表记录,提升导入效率 if act_instances: ActTimecodes.objects.bulk_create(act_instances)
3. 后台管理配置
在admin中注册Resource,启用导入功能:
# admin.py from django.contrib import admin from import_export.admin import ImportExportModelAdmin from .models import SlateDoc, ActTimecodes from .resources import SlateDocResource class SlateDocAdmin(ImportExportModelAdmin): resource_class = SlateDocResource list_display = ('filename', 'series_title', 'episode_title') admin.site.register(SlateDoc, SlateDocAdmin) admin.site.register(ActTimecodes)
关键实现说明
before_save_instance:临时存储当前CSV行数据,供后续解析副表字段使用。save_instance:先完成主表实例的保存,再批量创建关联的ActTimecodes记录,确保外键关联有效。- 批量创建:使用
bulk_create替代循环create,大幅提升大量数据导入的性能。 - 空值过滤:跳过缺失关键字段的act记录,避免无效数据入库。
- 预览模式兼容:
dry_run判断确保导入预览时不会写入副表数据。
内容的提问来源于stack exchange,提问作者stevec
相关产品推荐
相关产品推荐

