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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 06:15:58