Django单位转换器:实现单位类型选择后动态过滤单位选项
Django单位转换器:动态过滤单位选项解决方案
一、先修复双表单模式的核心错误
1. 解决「此字段为必填项」提示
问题根源是UnitConversionForm初始化时未根据是否选择单位类型动态设置字段必填性。修改forms.py:
# forms.py class UnitConversionForm(forms.Form): from_unit = forms.ModelChoiceField(queryset=Unit.objects.none(), required=False) to_unit = forms.ModelChoiceField(queryset=Unit.objects.none(), required=False) value = forms.FloatField(required=False) def __init__(self, unit_type=None, *args, **kwargs): super().__init__(*args, **kwargs) if unit_type: # 过滤对应类型的单位 self.fields['from_unit'].queryset = Unit.objects.filter(unit_type=unit_type) self.fields['to_unit'].queryset = Unit.objects.filter(unit_type=unit_type) # 选择类型后设为必填 self.fields['from_unit'].required = True self.fields['to_unit'].required = True self.fields['value'].required = True
2. 修复UnboundLocalError
确保视图中所有代码分支都定义conversion_form变量,修改views.py:
# views.py def unit_converter(request): unit_type_form = UnitTypeForm(request.POST or None) conversion_form = UnitConversionForm() result = None if request.method == 'POST': # 处理单位类型选择提交 if 'unit_type' in request.POST: if unit_type_form.is_valid(): selected_type = unit_type_form.cleaned_data['unit_type'] conversion_form = UnitConversionForm(unit_type=selected_type) # 处理转换提交 elif 'convert' in request.POST: selected_type_id = request.POST.get('unit_type_id') selected_type = UnitType.objects.get(id=selected_type_id) conversion_form = UnitConversionForm(unit_type=selected_type, data=request.POST) if conversion_form.is_valid(): # 执行转换逻辑(示例) from_unit = conversion_form.cleaned_data['from_unit'] to_unit = conversion_form.cleaned_data['to_unit'] value = conversion_form.cleaned_data['value'] result = value * from_unit.conversion_factor / to_unit.conversion_factor context = { 'unit_type_form': unit_type_form, 'conversion_form': conversion_form, 'result': result, 'selected_type_id': request.POST.get('unit_type_id') } return render(request, 'unit_converter.html', context)
二、推荐:无刷新动态更新单位选项(AJAX实现)
用前端JS配合后端接口,实现选择类型后无需刷新页面更新单位列表,体验更流畅:
1. 新增后端AJAX接口
# views.py from django.http import JsonResponse def get_units_by_type(request): unit_type_id = request.GET.get('unit_type_id') if not unit_type_id: return JsonResponse({'units': []}) # 返回对应类型的单位ID和名称 units = Unit.objects.filter(unit_type_id=unit_type_id).values('id', 'name') return JsonResponse({'units': list(units)})
2. 配置URL
# urls.py from django.urls import path from . import views urlpatterns = [ path('unit-converter/', views.unit_converter, name='unit_converter'), path('get-units/', views.get_units_by_type, name='get_units'), ]
3. 修改前端页面(unit_converter.html)
<form id="unit-type-form"> {{ unit_type_form.as_p }} <button type="submit">选择单位类型</button> </form> <form id="conversion-form" method="post"> {% csrf_token %} <input type="hidden" id="unit_type_id" name="unit_type_id"> <div> <label>转换值:</label> <input type="number" name="value" required> </div> <div> <label>从:</label> <select name="from_unit" id="from_unit" required> <option value="">请先选择单位类型</option> </select> </div> <div> <label>到:</label> <select name="to_unit" id="to_unit" required> <option value="">请先选择单位类型</option> </select> </div> <button type="submit" name="convert" disabled>转换</button> </form> {% if result %} <p>转换结果:{{ result }}</p> {% endif %} <script> document.getElementById('unit-type-form').addEventListener('submit', function(e) { e.preventDefault(); const unitTypeSelect = this.querySelector('select'); const unitTypeId = unitTypeSelect.value; if (!unitTypeId) return; // 请求对应类型的单位列表 fetch(`/get-units/?unit_type_id=${unitTypeId}`) .then(response => response.json()) .then(data => { const fromUnitSelect = document.getElementById('from_unit'); const toUnitSelect = document.getElementById('to_unit'); const convertBtn = document.querySelector('#conversion-form button[type="submit"]'); // 清空现有选项 fromUnitSelect.innerHTML = ''; toUnitSelect.innerHTML = ''; // 添加新单位选项 data.units.forEach(unit => { const option = document.createElement('option'); option.value = unit.id; option.textContent = unit.name; fromUnitSelect.appendChild(option.cloneNode(true)); toUnitSelect.appendChild(option); }); // 设置隐藏字段并启用转换按钮 document.getElementById('unit_type_id').value = unitTypeId; convertBtn.disabled = false; }); }); </script>
4. 简化转换表单定义
此时UnitConversionForm可简化,后端添加二次验证防止恶意提交:
# forms.py class UnitConversionForm(forms.Form): from_unit = forms.ModelChoiceField(queryset=Unit.objects.all()) to_unit = forms.ModelChoiceField(queryset=Unit.objects.all()) value = forms.FloatField() unit_type_id = forms.IntegerField(widget=forms.HiddenInput()) def clean(self): cleaned_data = super().clean() from_unit = cleaned_data.get('from_unit') to_unit = cleaned_data.get('to_unit') unit_type_id = cleaned_data.get('unit_type_id') # 验证两个单位属于选中的类型 if from_unit and from_unit.unit_type_id != unit_type_id: raise forms.ValidationError("单位类型不匹配") if to_unit and to_unit.unit_type_id != unit_type_id: raise forms.ValidationError("单位类型不匹配") return cleaned_data
三、关键注意事项
- 双表单模式下,必须保证视图所有代码路径都初始化
conversion_form,避免变量未定义错误 - AJAX模式下,前端要做基础交互限制(如未选类型禁用转换按钮),后端必须添加二次验证,防止非法提交
- 确保
Unit模型的conversion_factor字段(基准转换系数)定义正确,保证转换逻辑准确
内容的提问来源于stack exchange,提问作者anasdlk
相关产品推荐
相关产品推荐

