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

如何在Django邮件模板中添加动态新增的组件字段内容

问题描述

我正在开发一个表单,填写完成后会发送邮件,邮件需包含表单中的所有信息。表单设置了“添加组件”按钮,点击后会新增“组件选择”和“组件数量”字段,请问如何获取这些动态新增字段的数据并写入邮件?


现有代码

views.py

def home(request):
    if request.method == 'POST':
        form = Form(request.POST)
        
        if form.is_valid():
            type = form.cleaned_data['type']
            serialNumber = form.cleaned_data['serialNumber']
            itemNumber = form.cleaned_data['itemNumber']
            date = form.cleaned_data['date']
            phase = form.cleaned_data['phase']
            capacity = form.cleaned_data['capacity']
            centerQuantity = form.cleaned_data['centerQuantity']
            componentQuantity = form.cleaned_data['componentQuantity']
            componentSelection = form.cleaned_data['componentSelection']
            commentary = form.cleaned_data['commentary']

            html = render_to_string('emails/email.html', {
                'type': type,
                'serialNumber': serialNumber,
                'item': itemNumber,
                'date': date,
                'phase': phase,
                'capacity': capacity,
                'centerQuantity': centerQuantity,
                'componentQuantity': componentQuantity,
                'componentSelection': componentSelection,
                'commentary': commentary,
            }
            ) 
            send_mail('ATENÇÃO', 'Message', 'no-reply@xxx.net', ['test@xxx.net'], html_message=html, fail_silently=False)
            
            return redirect('home')
    else:
        form = Form()
        
    return render(request, 'template.html', {
        'form': form 
    })

HTML模板(template.html)

<body>
    <div>
        <h2> 发动机信息</h2>
        <table>
            <form action="." method="POST" hx-post=".">
                {% csrf_token %}
                <tbody>
                    <tr>
                        <th>查询类型</th>
                        <td>{{form.type}}</td>
                    </tr>
                    <tr>
                        <th>序列号</th>
                        <td>{{form.serialNumber}}</td>
                        <th>或物料编号</th>
                        <td>{{form.itemNumber}}</td>
                    </tr>
                    <tr>
                        <th>生产日期</th>
                        <td>{{form.date}}</td>
                        <th>相位</th>
                        <td>{{form.phase}}</td>
                        <th>功率</th>
                        <td>{{form.capacity}}</td>
                        <th>中心数量</th>
                        <td>{{form.centerQuantity}}</td>
                    </tr>

                </tbody>
        </table>
    </div>
    </div>
    <div id="component-form">
        <h2>组件</h2>
        <div class="parts-form">
            <table id="component">
                <tbody>
                    <tr>
                        <th>选择组件</th>
                        <td>{{form.componentSelection}}</td>
                    </tr>
                    <tr>
                        <th>组件数量</th>
                        <td>{{form.componentQuantity}}</td>
                    </tr>
                </tbody>
                <div id="form-added"></div>

            </table>
        </div>
        <button id="add-more" type="button">添加组件</button>
        <table>
            <tbody>
                <tr>
                    <th>备注</th>
                    <td>{{form.commentary}}</td>
                </tr>
            </tbody>
        </table>
        <input type="submit" id="button" value="提交">
        </form>
    </div>

    <script>
        const AddMoreBtn = document.getElementById('add-more')
        AddMoreBtn.addEventListener('click', add_new_form)
        function add_new_form(event) {
            if (event) {
                event.preventDefault()
            }
            //id
            const newId = document.getElementsByClassName('parts-form')
            const idIncrement = newId.length + 1
            //
            const formAdd = document.getElementById('form-added')
            //add new empty form
            var componentForm = document.getElementById('component').cloneNode(true)
            componentForm.setAttribute('class', 'parts-form')
            //id increment
            componentForm.setAttribute('id', `form-${idIncrement}`)
            formAdd.append(componentForm)
        }
    </script>

</body>

</html>

forms.py

from django import forms

material_type = (
    ('material', '按物料查询'),
    ('serial', '按序列号查询'),
)

component_type = (
    ('AIR FILTER', '空气滤清器'),
    ('ALUMINIUM FAN', '铝风扇'),
    ('BEARING CAP DE', '轴承端盖')
)


class Form(forms.Form):
    type = forms.ChoiceField(choices=material_type, initial="选择查询类型")
    serialNumber = forms.CharField(label='序列号', required=False)
    itemNumber = forms.IntegerField(label='物料编号', required=False)
    date = forms.DateField(label='生产日期',widget=forms.DateInput(attrs={'placeholder':'示例: 10/05/2022'}))
    phase = forms.CharField(widget=forms.TextInput(attrs={'placeholder': '示例: 单相'}))
    capacity = forms.CharField(widget=forms.TextInput(attrs={'placeholder': '示例: 15cv'}))
    centerQuantity = forms.IntegerField(label='中心数量')
    componentSelection = forms.ChoiceField(choices=component_type, label='组件选择')
    componentQuantity = forms.IntegerField(label='组件数量')
    componentSelection1 = forms.ChoiceField(choices=component_type, label='组件选择', required=False)
    componentQuantity1 = forms.IntegerField(label='组件数量', required=False)
    commentary = forms.CharField(widget=forms.Textarea(attrs={"rows":8, "cols":80}))

email.html

<h2 id="title">提交信息</h2>
<br>
<p>查询类型: {{type}}</p>
<p>序列号: {{serialNumber}}</p>
<p>物料编号: {{item}}</p>
<p>生产日期: {{date}}</p>
<p>功率: {{capacity}}</p>
<p>中心数量: {{centerQuantity}}</p>

<p>组件数量: {{componentQuantity}}</p>
<p>组件选择: {{componentSelection}}</p>

<p>备注: {{commentary}}</p> 

<style>
    #title{
        margin-bottom: 2px;
    }
</style>

解决方案

当前核心问题是:动态克隆的表单字段与原字段name属性完全一致,Django无法区分多组组件输入;同时表单定义仅固定两组组件字段,无法支持无限新增。以下是具体修改步骤:

1. 修改前端JS,给动态字段设置唯一name属性

克隆字段时要修改name为带索引的格式(如componentSelection_1、componentQuantity_1),让后端能识别多组数据:

const AddMoreBtn = document.getElementById('add-more')
AddMoreBtn.addEventListener('click', add_new_form)
function add_new_form(event) {
    if (event) {
        event.preventDefault()
    }
    // 获取已有组件表单数量
    const formCount = document.querySelectorAll('.parts-form').length;
    const formAdd = document.getElementById('form-added');
    
    // 克隆原表单结构
    const componentForm = document.getElementById('component').cloneNode(true);
    componentForm.setAttribute('class', 'parts-form');
    componentForm.setAttribute('id', `form-${formCount + 1}`);
    
    // 修改克隆字段的name属性,添加索引
    const selectField = componentForm.querySelector('select');
    const quantityField = componentForm.querySelector('input[type="number"]');
    
    selectField.setAttribute('name', `componentSelection_${formCount}`);
    quantityField.setAttribute('name', `componentQuantity_${formCount}`);
    
    // 清空克隆字段的默认值
    selectField.value = '';
    quantityField.value = '';
    
    formAdd.append(componentForm);
}

2. 修改后端视图,收集所有动态组件数据

无需在forms.py预定义多组字段,直接从request.POST提取所有带索引的组件数据:

def home(request):
    if request.method == 'POST':
        form = Form(request.POST)
        
        if form.is_valid():
            # 提取基础字段数据
            type = form.cleaned_data['type']
            serialNumber = form.cleaned_data['serialNumber']
            itemNumber = form.cleaned_data['itemNumber']
            date = form.cleaned_data['date']
            phase = form.cleaned_data['phase']
            capacity = form.cleaned_data['capacity']
            centerQuantity = form.cleaned_data['centerQuantity']
            commentary = form.cleaned_data['commentary']
            
            # 收集所有动态组件数据
            components = []
            # 遍历POST中所有组件相关字段
            for key in request.POST.keys():
                if key.startswith('componentSelection_'):
                    # 获取索引
                    idx = key.split('_')[-1]
                    selection = request.POST.get(key)
                    quantity = request.POST.get(f'componentQuantity_{idx}')
                    if selection and quantity:  # 只收集有值的组件
                        components.append({
                            'selection': selection,
                            'quantity': quantity
                        })
            # 加入原表单的第一个组件
            original_selection = form.cleaned_data['componentSelection']
            original_quantity = form.cleaned_data['componentQuantity']
            if original_selection and original_quantity:
                components.insert(0, {
                    'selection': original_selection,
                    'quantity': original_quantity
                })

            # 传递数据到邮件模板
            html = render_to_string('emails/email.html', {
                'type': type,
                'serialNumber': serialNumber,
                'item': itemNumber,
                'date': date,
                'phase': phase,
                'capacity': capacity,
                'centerQuantity': centerQuantity,
                'components': components,
                'commentary': commentary,
            }) 
            send_mail('注意', '表单提交信息', 'no-reply@xxx.net', ['test@xxx.net'], html_message=html, fail_silently=False)
            
            return redirect('home')
    else:
        form = Form()
        
    return render(request, 'template.html', {
        'form': form 
    })

3. 修改邮件模板,渲染所有组件数据

使用循环遍历所有组件数据,展示在邮件中:

<h2 id="title">提交信息</h2>
<br>
<p>查询类型: {{type}}</p>
<p>序列号: {{serialNumber}}</p>
<p>物料编号: {{item}}</p>
<p>生产日期: {{date}}</p>
<p>相位: {{phase}}</p>
<p>功率: {{capacity}}</p>
<p>中心数量: {{centerQuantity}}</p>

<h3>组件列表</h3>
{% for comp in components %}
    <p>组件: {{comp.selection}} | 数量: {{comp.quantity}}</p>
{% endfor %}

<p>备注: {{commentary}}</p> 

<style>
    #title{
        margin-bottom: 2px;
    }
</style>

可选:用Django表单集规范处理(进阶)

如果需要更严谨的表单验证,可使用Django表单集:

  1. 在forms.py定义单个组件表单:
class ComponentForm(forms.Form):
    selection = forms.ChoiceField(choices=component_type, label='组件选择')
    quantity = forms.IntegerField(label='组件数量')
  1. 视图中创建表单集:
from django.forms import formset_factory

ComponentFormSet = formset_factory(ComponentForm, extra=1)

def home(request):
    if request.method == 'POST':
        main_form = Form(request.POST)
        component_formset = ComponentFormSet(request.POST, prefix='components')
        
        if main_form.is_valid() and component_formset.is_valid():
            # 处理主表单数据
            # ...
            # 获取表单集数据
            components = []
            for form in component_formset:
                if form.cleaned_data:
                    components.append({
                        'selection': form.cleaned_data['selection'],
                        'quantity': form.cleaned_data['quantity']
                    })
            # 发送邮件...
    else:
        main_form = Form()
        component_formset = ComponentFormSet(prefix='components')
    return render(request, 'template.html', {
        'form': main_form,
        'component_formset': component_formset
    })
  1. 修改前端模板渲染表单集,同时调整JS逻辑处理表单集的隐藏字段(如form-TOTAL_FORMS),这种方式自带验证,适合复杂场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 06:40:25