Django动态下拉选择框提交表单返回form-invalid问题排查
Django表单提交返回form-invalid问题排查
问题描述
我有一个包含下拉选择框的表单,页面加载时会发送AJAX请求获取当前用户所属公司的费用分类,这部分功能正常,但提交表单时会返回form-invalid。我不熟悉Django表单,恳请帮忙排查代码问题。
模型代码
class Expense(models.Model): slug = models.SlugField(max_length=80, unique=True) debit = models.DecimalField(decimal_places=2, max_digits=10) date = models.DateTimeField(default=timezone.now) category = models.CharField(max_length=50, choices=()) remark = models.CharField(max_length=50, validators=[MinLengthValidator(3)]) company = models.ForeignKey(Company, on_delete=models.CASCADE)
视图代码
def expense_types(request, company_id): company = Company.objects.filter(company_id=company_id).first() types = ExpenseType.objects.filter(company=company).all() output = [] for typ in types: data = {} data['category'] = typ.category output.append(data) return JsonResponse({'categories': output}) # URL path('<str:company_id>/ExpenseTypes', views.expense_types, name='get-expense-types')
模板代码
<form method="POST" action="" enctype="multipart/form-data"> {% csrf_token %} {{ form|as_crispy_errors }} <div class="row"> <div class="col-md-6 col-sm-6"> {{ form.date|as_crispy_field }} </div> <div class="col-md-6 col-sm-6"> {{ form.debit|as_crispy_field }} </div> </div> <div class="row"> <div class="col-md-6 col-sm-6"> {{ form.category|as_crispy_field }} <input type="hidden" id="company_id", value="{{ user.company.company_id }}"> </div> <div class="col-md-6 col-sm-6"> {{ form.remark|as_crispy_field }} </div> </div> <div class="form-group"> <button class="btn btn-outline-warning btn-block" type="submit">Save</button> </div> </form> <script type="text/javascript"> let category = document.getElementById('id_category') let company_id = document.getElementById('company_id').value fetch('/' + company_id + '/ExpenseTypes').then(function(response){ response.json().then(function (data){ let optionHTML = '' for (let type of data.categories){ optionHTML += '<option value="' + type.category + '">' + type.category + '</option>' } category.innerHTML = '<option value="' + 1 + '" selected>-- Please choose expense type --</option>' + optionHTML }) }) </script>
问题分析及解决步骤
核心问题
- 模型字段验证冲突:模型中
category字段设置了choices=()空选项集合,Django自动生成的表单会将该字段设为ChoiceField,提交的值必须在预设的choices中,但AJAX动态加载的分类不在这个空集合里,导致验证失败。 - 默认选项值非法:JS中设置的默认选项
value="1"是数字类型,而模型category是CharField,且该值不属于任何合法分类,用户未选择分类时提交这个值会触发验证错误。
解决方案
1. 修改模型字段
去掉category字段的空choices参数,因为分类是动态加载的,不需要固定选项集合:
category = models.CharField(max_length=50) # 移除choices=()
2. 自定义表单,替换字段类型并添加验证
创建自定义表单,将category改为普通CharField,同时添加验证确保提交的分类属于当前用户的公司:
from django import forms from .models import Expense, ExpenseType class ExpenseForm(forms.ModelForm): # 替换默认的ChoiceField为CharField category = forms.CharField(max_length=50, label="费用分类") class Meta: model = Expense fields = ['date', 'debit', 'category', 'remark', 'company'] # 若company字段不需要用户输入,可设置为隐藏字段 widgets = { 'company': forms.HiddenInput() } def clean_category(self): category = self.cleaned_data.get('category') company = self.cleaned_data.get('company') # 验证分类是否属于当前公司 if not ExpenseType.objects.filter(company=company, category=category).exists(): raise forms.ValidationError("请选择有效的费用分类") return category
3. 修正JS默认选项值
将默认选项的value设为空字符串,避免提交非法值,同时提示用户选择分类:
let category = document.getElementById('id_category') let company_id = document.getElementById('company_id').value fetch('/' + company_id + '/ExpenseTypes').then(function(response){ response.json().then(function (data){ let optionHTML = '' for (let type of data.categories){ optionHTML += '<option value="' + type.category + '">' + type.category + '</option>' } // 默认选项设为空值,符合表单必填验证逻辑 category.innerHTML = '<option value="" selected>-- 请选择费用类型 --</option>' + optionHTML }) })
4. 视图中使用自定义表单
在处理表单的视图中,初始化表单时自动设置当前用户的公司:
def add_expense(request): if request.method == 'POST': form = ExpenseForm(request.POST, instance=Expense(company=request.user.company)) if form.is_valid(): form.save() # 重定向或返回成功提示 return redirect('expense-list') else: form = ExpenseForm(initial={'company': request.user.company}) return render(request, 'expense_add.html', {'form': form})
内容的提问来源于stack exchange,提问作者Abdullahi Hirsi-geele
相关产品推荐
相关产品推荐

