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

如何一次性创建包含多列的CSV数据Schema模型?

解决Django中Schema与关联Column的批量创建及表单实现问题

一、模型参考定义

假设你的模型结构如下(如果已有定义可跳过此部分):

from django.db import models

class Schema(models.Model):
    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

class Column(models.Model):
    SCHEMA_FIELD_TYPES = (
        ('string', '字符串'),
        ('integer', '整数'),
        ('date', '日期'),
    )
    schema = models.ForeignKey(Schema, on_delete=models.CASCADE, related_name='columns')
    name = models.CharField(max_length=255)  # CSV表头名称
    field_type = models.CharField(max_length=50, choices=SCHEMA_FIELD_TYPES)
    order = models.IntegerField(default=0)  # 排序序号

    class Meta:
        ordering = ['order']

    def __str__(self):
        return f"{self.schema.name} - {self.name}"

二、一次性创建Schema及其关联列

事务+批量创建方案

用Django事务保证原子性,避免出现Schema创建成功但列创建失败的不一致情况:

from django.db import transaction

def create_schema_with_columns(schema_name, columns_data):
    # columns_data格式示例:
    # [
    #     {'name': '用户名', 'field_type': 'string', 'order': 1},
    #     {'name': '年龄', 'field_type': 'integer', 'order': 2},
    #     {'name': '注册日期', 'field_type': 'date', 'order': 3}
    # ]
    with transaction.atomic():
        schema = Schema.objects.create(name=schema_name)
        # 构造Column对象列表
        column_objs = [
            Column(
                schema=schema,
                name=col['name'],
                field_type=col['field_type'],
                order=col['order']
            ) for col in columns_data
        ]
        # 批量插入提升效率
        Column.objects.bulk_create(column_objs)
    return schema

调用示例:

columns = [
    {'name': '商品名称', 'field_type': 'string', 'order': 1},
    {'name': '价格', 'field_type': 'integer', 'order': 2}
]
create_schema_with_columns('商品信息', columns)

三、实现动态添加列的表单

使用Django表单集(FormSet)支持任意数量的列添加:

1. 定义表单及表单集

from django import forms

class ColumnForm(forms.ModelForm):
    class Meta:
        model = Column
        fields = ['name', 'field_type', 'order']
        widgets = {
            'order': forms.NumberInput(attrs={'min': 0}),
        }

# 创建关联Schema的内联表单集
ColumnFormSet = forms.inlineformset_factory(
    Schema,
    Column,
    form=ColumnForm,
    extra=1,  # 默认显示1个空列表单
    can_delete=True,  # 允许删除已添加的列
    can_order=False  # 用自定义order字段,关闭表单集自带排序
)

2. 视图处理逻辑

from django.shortcuts import render, redirect

def schema_create(request):
    if request.method == 'POST':
        # 处理Schema名称表单
        schema_form = forms.ModelForm(model=Schema, fields=['name'])(request.POST)
        # 处理列表单集
        formset = ColumnFormSet(request.POST, instance=Schema())
        if schema_form.is_valid() and formset.is_valid():
            # 先保存Schema实例
            schema = schema_form.save()
            # 将表单集关联到Schema并保存所有列
            formset.instance = schema
            formset.save()
            return redirect('schema_list')  # 替换为你的Schema列表页路由
    else:
        schema_form = forms.ModelForm(model=Schema, fields=['name'])()
        formset = ColumnFormSet(instance=Schema())
    
    return render(request, 'schema_create.html', {
        'schema_form': schema_form,
        'formset': formset
    })

3. 模板实现(schema_create.html)

添加JS支持动态添加列表单:

<form method="post">
    {% csrf_token %}
    <div class="schema-section">
        <h3>Schema名称</h3>
        {{ schema_form.as_p }}
    </div>

    <div class="columns-section">
        <h3>列配置</h3>
        {{ formset.management_form }}
        {% for form in formset %}
            <div class="column-form">
                {{ form.as_p }}
                {% if form.instance.pk %}
                    {{ form.DELETE }} <label for="{{ form.DELETE.id_for_label }}">删除此列</label>
                {% endif %}
            </div>
        {% endfor %}
        <button type="button" id="add-column-btn">添加新列</button>
    </div>

    <button type="submit" class="save-btn">保存Schema</button>
</form>

<script>
document.getElementById('add-column-btn').addEventListener('click', function() {
    const columnForms = document.querySelectorAll('.column-form');
    const lastForm = columnForms[columnForms.length - 1];
    const newForm = lastForm.cloneNode(true);
    
    // 更新表单集计数
    const totalFormsInput = document.getElementById('id_columns-TOTAL_FORMS');
    const currentFormNum = parseInt(totalFormsInput.value);
    totalFormsInput.value = currentFormNum + 1;
    
    // 更新表单字段的name和id属性,适配Django表单集规则
    newForm.innerHTML = newForm.innerHTML.replace(/columns-\d+/g, `columns-${currentFormNum}`);
    
    // 清空新表单的输入值
    newForm.querySelectorAll('input, select').forEach(input => {
        input.value = '';
        if (input.type === 'checkbox') input.checked = false;
    });
    
    lastForm.parentNode.appendChild(newForm);
});
</script>

四、关键注意事项

  • 事务原子性:必须用transaction.atomic()包裹创建逻辑,防止部分数据写入失败。
  • 表单集规则:动态添加表单时必须更新TOTAL_FORMS计数和字段命名,否则Django无法解析表单数据。
  • 排序逻辑:Column模型的Meta.ordering配置确保查询时按order字段排序,生成CSV时直接遍历schema.columns.all()即可得到有序列。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 01:10:28