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

如何创建批量录入无成本产品成本信息的Django表单?

批量录入无关联成本产品的表单实现

需求说明

现有Product(含产品名称字段)和一对一关联的ProductCosts(含成本、产量字段)模型,已实现单独的产品录入和成本录入功能,现在需要做一个批量表单:

  • 第一列展示还没关联成本记录的产品名称
  • 对应行提供空白的成本、产量输入框
  • 表单行数和无成本关联的产品数量一致

模型代码(参考)

# models.py
from django.db import models

class Product(models.Model):
    name = models.CharField("产品名称", max_length=150)
    # 其他字段...

class ProductCosts(models.Model):
    product = models.OneToOneField(Product, on_delete=models.CASCADE, verbose_name="关联产品")
    costs = models.DecimalField("成本", max_digits=10, decimal_places=2)
    produced = models.IntegerField("产量", null=True, blank=True)
    # 其他字段...

解决方案

1. 定义批量表单类

不用ModelForm,直接基于Form类动态生成字段,匹配每个无成本关联的产品:

# forms.py
from django import forms
from .models import Product, ProductCosts

class BulkProductCostsForm(forms.Form):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 筛选所有未关联成本的产品
        unassigned_products = Product.objects.filter(productcosts__isnull=True)
        
        for product in unassigned_products:
            # 为每个产品创建成本字段
            self.fields[f'costs_{product.id}'] = forms.DecimalField(
                label=f"{product.name} 的成本",
                max_digits=10,
                decimal_places=2,
                required=True  # 若允许空成本,改成False
            )
            # 为每个产品创建产量字段
            self.fields[f'produced_{product.id}'] = forms.IntegerField(
                label=f"{product.name} 的产量",
                required=False,
                widget=forms.NumberInput(attrs={'placeholder': '可选填'})
            )

2. 编写视图处理提交逻辑

处理表单的POST请求,批量创建ProductCosts实例:

# views.py
from django.shortcuts import render, redirect
from .forms import BulkProductCostsForm
from .models import Product, ProductCosts

def bulk_add_costs(request):
    if request.method == 'POST':
        form = BulkProductCostsForm(request.POST)
        if form.is_valid():
            # 遍历表单数据,提取产品ID和对应值
            for field_name, value in form.cleaned_data.items():
                if field_name.startswith('costs_'):
                    product_id = field_name.split('_')[1]
                    try:
                        product = Product.objects.get(id=product_id)
                        # 获取对应产量值
                        produced_val = form.cleaned_data.get(f'produced_{product_id}')
                        # 创建成本记录
                        ProductCosts.objects.create(
                            product=product,
                            costs=value,
                            produced=produced_val
                        )
                    except Product.DoesNotExist:
                        # 处理产品不存在的异常,可选:记录日志或返回错误
                        continue
            # 提交后跳转到产品列表或成本列表页面
            return redirect('product_list')  # 替换成你的URL名称
    else:
        form = BulkProductCostsForm()
    
    # 获取未关联成本的产品,用于模板渲染产品名称
    unassigned_products = Product.objects.filter(productcosts__isnull=True)
    return render(request, 'bulk_add_costs.html', {
        'form': form,
        'unassigned_products': unassigned_products
    })

3. 编写模板(表格布局)

用表格展示产品和对应输入框,直观清晰:

<!-- templates/bulk_add_costs.html -->
{% extends 'base.html' %}
{% load crispy_forms_tags %}  <!-- 如果你用了crispy forms,没用到可以删掉 -->

{% block content %}
<h2>批量录入产品成本</h2>
<form method="post">
    {% csrf_token %}
    <table class="table table-striped table-bordered">
        <thead>
            <tr>
                <th>产品名称</th>
                <th>成本</th>
                <th>产量</th>
            </tr>
        </thead>
        <tbody>
            {% for product in unassigned_products %}
            <tr>
                <td>{{ product.name }}</td>
                <!-- 用crispy渲染字段,没用到crispy就用下面的原生输入框 -->
                <td>{{ form|crispy_field:"costs_"|add:product.id }}</td>
                <td>{{ form|crispy_field:"produced_"|add:product.id }}</td>
                
                <!-- 原生输入框写法(替代crispy):
                <td>
                    <input type="number" step="0.01" name="costs_{{ product.id }}" 
                           class="form-control" required>
                </td>
                <td>
                    <input type="number" name="produced_{{ product.id }}" 
                           class="form-control" placeholder="可选填">
                </td>
                -->
            </tr>
            {% empty %}
            <tr>
                <td colspan="3" class="text-center text-muted">所有产品都已关联成本记录</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    {% if unassigned_products %}
    <button type="submit" class="btn btn-dark">批量提交</button>
    {% endif %}
</form>
{% endblock %}

补充提示

  • 如果需要对表单做更复杂的样式调整,可直接修改模板里的HTML结构和CSS类
  • 若要允许部分产品不填成本,把表单里的required=True改成False,提交时跳过空值的产品
  • 可以在视图里添加错误提示,比如产品不存在时返回页面并提示用户

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:55:22