为什么Django表单中动态生成的ChoiceField在模板中无法正常渲染?
问题原因
Django Form 的字段渲染、数据绑定、校验逻辑仅会处理内置的 self.fields 字典中存储的字段。你把动态生成的 ChoiceField 放在了自定义的 SundryFields 字典中,Django 不会对这些字段做任何处理,直接在模板输出时就会显示字段类的内存地址,也不会参与后续的表单校验和数据清洗。
修复步骤
1. 修改表单类代码
将动态生成的物料字段存入 self.fields,同时单独存储字段名列表方便后续遍历,避免和表单原有字段冲突:
name = forms.CharField(label="员工姓名", max_length = 200, required = False) jobsite = forms.CharField(label="工作地点", max_length = 200, required = True) date_needed = forms.DateField(initial = now, required = True) def __init__(self, *args, **kwargs): sundries = kwargs.pop('sundries') super(CreateNewOrder, self).__init__(*args, **kwargs) # 存储动态生成的物料字段名,方便后续遍历 self.sundry_field_names = [] for sundry in sundries: # 生成0到可用库存的可选值 quantity_list = [(i, i) for i in range(sundry.quantity_available + 1)] # 加前缀避免和其他字段重名 field_name = f"sundry_{sundry.name}" self.fields[field_name] = forms.ChoiceField(label=sundry.name, choices=quantity_list) self.sundry_field_names.append(field_name) # 新增方法:返回所有物料字段的可渲染实例,方便模板调用 def get_sundry_fields(self): return [self[field_name] for field_name in self.sundry_field_names]
2. 修改模板代码
直接调用表单新增的 get_sundry_fields 方法遍历渲染物料下拉框:
{% block title %}物料申请{% endblock %} {% block content %} <h1>当前可用物料列表</h1> <form method = "post" action="/orders/"> {% csrf_token %} <div class="mb-3"> <ul> <li>姓名: {{form.name}}</li> <li>工作地点: {{form.jobsite}}</li> <li>需用日期: {{form.date_needed}}</li> </ul> </div> <ul> {% for field in form.get_sundry_fields %} <li>{{ field.label }}: {{ field }} </li> {% endfor %} </ul> <button type="submit" name="submit">提交申请</button> </form> {% endblock %}
3. 修改视图中的数据处理逻辑
适配加了前缀的字段名,提取物料名称和申请数量:
def orders(response): if response.method == "POST": form = CreateNewOrder(response.POST, sundries = Sundry.objects.all()) if form.is_valid(): cleaned_data = form.cleaned_data order = Order(name=cleaned_data["name"], jobsite=cleaned_data["jobsite"], date_needed=cleaned_data["date_needed"]) order.save() del cleaned_data["name"] del cleaned_data["jobsite"] del cleaned_data["date_needed"] def find_requested(cleaned_data): requested = [] for name, value in cleaned_data.items(): # 过滤物料字段,且申请数量大于0 if name.startswith("sundry_") and int(value) > 0: sundry_name = name.replace("sundry_", "", 1) requested.append((sundry_name, value)) return requested for sundry_name, quantity in find_requested(cleaned_data): i = Item(order=order, text=sundry_name, quantity_requested=quantity) i.save() Sundry.objects.filter(name=i.text).update(quantity_available = F('quantity_available') - i.quantity_requested) subject = 'QCE仓库 - 申请单号#' + str(order.pk) message = '申请人: ' + order.name + "\n" for item in order.item_set.all(): message += ("申请物料: " + item.text + " 数量: " + str(item.quantity_requested) + "\n") message += ("需用日期: " + str(order.date_needed)) email_from = settings.EMAIL_HOST_USER recipient_list = ['braxtonagoss@gmail.com'] send_mail(subject, message, email_from, recipient_list) return HttpResponseRedirect('thanks') else: form = CreateNewOrder(sundries = Sundry.objects.all()) return render(response, "main/order.html", {"form":form} )
内容的提问来源于stack exchange,提问作者Braxton Goss
相关产品推荐
相关产品推荐

