Django表单如何将Select选中值传入action URL及视图优化
问题描述
我有一个包含多个<select>输入框的表单,代码如下:
<form method="POST" class="row" action="{% url 'solution_product_list' %}"> {% csrf_token %} {# main select div: usage or model? #} <div class="col-md-3 mx-md-5"> <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب بر اساس:</h2> <select required aria-label="Select usage or model" id="usage_model_select" class="form-select" onchange="set_usage_or_model_dic()"> <option selected>----</option> <option value="usage">کاربرد</option> <option value="model">مدل</option> </select> </div> {# usage or model select div #} <div class="col-md-3 mx-md-5"> {# usage select div #} <div class="usage visually-hidden" id="usage_div"> <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب کاربرد:</h2> <select required aria-label="Select usage" class="form-select" name="usage_select" onchange="set_sub_usage_list()" id="usage_select_id"> <option selected>----</option> {% for usage in usage_queryset %} <option value="{{ usage.id }}">{{ usage.usage_name_fa }}</option> {% endfor %} </select> </div> {# model select div #} <div class="model visually-hidden" id="model_div"> <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب مدل:</h2> <select required aria-label="Select model" class="form-select" name="model_select" onchange="set_pump_type_list()" id="model_select_id"> <option selected>----</option> {% for model in main_model_queryset %} <option value="{{ model.id }}">{{ model.model_name_fa }}</option> {% endfor %} </select> </div> </div> {# select sub_usage or pump_type div #} <div class="col-md-3 mx-md-5"> {# sub_usage select div #} <div class="sub_usage visually-hidden" id="sub_usage_div"> <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب کاربرد جزئی:</h2> <select required aria-label="Select sub_usage" class="form-select" name="sub_usage_select"> <option selected>همهی کابردهای جزئی</option> {% for sub_usage in sub_usage_queryset %} <option value="{{ sub_usage.id }}">{{ sub_usage.sub_usage_name_fa }}</option> {% endfor %} </select> </div> {# model select div #} <div class="pump-type visually-hidden" id="pump_type_div"> <h2 class="h5 nm-text-color fw-bold mb-4">انتخاب تیپ:</h2> <select aria-label="Select pump_type" class="form-select" name="pump_type_select"> <option selected>همهی تیپهای این مدل</option> {% for pump_type in pump_type_queryset %} <option value="{{ pump_type.id }}">{{ pump_type.type_name }}</option> {% endfor %} </select> </div> </div> <div> <input type="submit" value="مرحله بعدی" id="submit" class="btn btn-primary"> </div> </form>
目前表单的action是{% url 'solution_product_list' %},我希望把选中的select值传入这个URL,让它变成{% url 'solution_product_list' selected_value %}。之前搜过相关问题,尝试过redirect但没解决。
另外我的视图代码比较杂乱,代码如下:
def solution_product_list(request): product_list_queryset = None sub_usage_list = [] pump_type_list = [] if request.method == "POST": if request.POST["usage_select"] != "----": usage_select = request.POST["usage_select"] product_list_queryset = Product.objects.filter(usage__id=usage_select) sub_usage_list = SubUsage.objects.filter(usage__id=usage_select) if request.POST["sub_usage_select"] != "همهی کابردهای جزئی": sub_usage_select = request.POST["sub_usage_select"] product_list_queryset = Product.objects.filter( sub_usage__id=sub_usage_select ) elif request.POST["model_select"] != "----": model_select = request.POST["model_select"] product_list_queryset = Product.objects.filter(main_model__id=model_select) pump_type_list = PumpType.objects.filter( main_model__id=model_select ) if request.POST["pump_type_select"] != "همهی تیپهای این مدل": pump_type_select = request.POST["pump_type_select"] product_list_queryset = Product.objects.filter( pump_type__id=pump_type_select ) elif request.POST["head"] and request.POST["flow"]: flow = float(request.POST["flow"]) head = float(request.POST["head"]) head_flow_list_main = get_head_flow_list(product_list_queryset) product_list = return_filtered_products( head_flow_list_main, head, flow ) product_list_queryset = Product.objects.filter(id__in=product_list) page = request.GET.get("page", 1) paginator = Paginator(product_list_queryset, 10) try: product_list = paginator.page(page) except PageNotAnInteger: product_list = paginator.page(1) except EmptyPage: product_list = paginator.page(paginator.num_pages) queryset_dictionary = get_common_queryset() page_context = { "product_list": product_list_queryset, "sub_usage_queryset": sub_usage_list, "pump_type_queryset": pump_type_list, } print(product_list_queryset) context = { **queryset_dictionary, **page_context, } return render(request, "solutions/second_stage_select.html", context)
作为Django新手,希望得到问题的解决方法,以及代码格式和编写的优化建议。
解决方案
一、表单action传递选中值的实现
Django模板标签是服务器端渲染的,无法直接获取前端动态选中值,需要用JavaScript动态修改表单action:
- 给表单添加ID:方便JS定位元素
<form method="POST" class="row" id="product-filter-form" action="{% url 'solution_product_list' %}">
- 添加JS动态修改逻辑:在模板末尾或外部JS文件中添加以下代码
document.getElementById('product-filter-form').addEventListener('submit', function(e) { const mainSelectVal = document.getElementById('usage_model_select').value; let selectedValue = ''; // 根据主选择框类型获取对应选中值 if (mainSelectVal === 'usage') { selectedValue = document.getElementById('usage_select_id').value; // 如需传递子用途值,可添加: // const subUsageVal = document.querySelector('[name="sub_usage_select"]').value; // selectedValue = `${selectedValue}/${subUsageVal}`; } else if (mainSelectVal === 'model') { selectedValue = document.getElementById('model_select_id').value; // 如需传递泵类型值,可添加: // const pumpTypeVal = document.querySelector('[name="pump_type_select"]').value; // selectedValue = `${selectedValue}/${pumpTypeVal}`; } // 拼接新的action URL this.action = `${this.action}${selectedValue}/`; });
- 修改URL配置:确保urls.py中对应规则支持接收参数
# urls.py path('solution/products/<int:selected_value>/', views.solution_product_list, name='solution_product_list'), # 如需多参数,可调整为: # path('solution/products/<int:main_id>/<int:sub_id>/', views.solution_product_list, name='solution_product_list'),
- 视图接收参数:修改视图函数定义以接收URL参数
def solution_product_list(request, selected_value=None): # 可根据selected_value做初始过滤逻辑 # ...
二、视图代码优化建议
1. 安全获取POST参数,避免KeyError
直接访问request.POST["key"]会在字段不存在时抛出异常,改用get方法并设置默认值:
usage_select = request.POST.get("usage_select", "----") if usage_select != "----": # 后续逻辑
2. 拆分复杂逻辑为独立函数
把不同过滤逻辑拆成单独函数,提升可读性和复用性:
def filter_by_usage(post_data): usage_id = post_data.get("usage_select") queryset = Product.objects.filter(usage__id=usage_id) sub_usages = SubUsage.objects.filter(usage__id=usage_id) sub_usage_id = post_data.get("sub_usage_select") if sub_usage_id != "همهی کابردهای جزئی": queryset = Product.objects.filter(sub_usage__id=sub_usage_id) return queryset, sub_usages def filter_by_model(post_data): model_id = post_data.get("model_select") queryset = Product.objects.filter(main_model__id=model_id) pump_types = PumpType.objects.filter(main_model__id=model_id) pump_type_id = post_data.get("pump_type_select") if pump_type_id != "همهی تیپهای این مدل": queryset = Product.objects.filter(pump_type__id=pump_type_id) return queryset, pump_types
视图中调用示例:
if request.method == "POST": if request.POST.get("usage_select") != "----": product_list_queryset, sub_usage_list = filter_by_usage(request.POST) elif request.POST.get("model_select") != "----": product_list_queryset, pump_type_list = filter_by_model(request.POST) # ... 其他逻辑
3. 处理空QuerySet的分页问题
当product_list_queryset为None时,Paginator会报错,需先转为空QuerySet:
if product_list_queryset is None: product_list_queryset = Product.objects.none() # 生成空QuerySet paginator = Paginator(product_list_queryset, 10)
4. 修正上下文传递的分页对象
之前传递的是原始QuerySet,应该传递分页后的product_list对象,方便模板渲染分页控件:
context = get_common_queryset() context.update({ "product_list": product_list, "sub_usage_queryset": sub_usage_list, "pump_type_queryset": pump_type_list, })
5. 类型转换增加异常处理
处理flow和head的float转换时,捕获ValueError避免崩溃:
try: flow = float(request.POST.get("flow", 0)) head = float(request.POST.get("head", 0)) except ValueError: # 处理非数字输入,比如设置默认值或返回错误提示 flow = 0.0 head = 0.0
6. 移除调试代码
上线前删除print(product_list_queryset)这类调试语句。
内容的提问来源于stack exchange,提问作者Vahid
相关产品推荐
相关产品推荐

