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

循环创建Crispy Form布局:Django表单布局定义求助

用Crispy Forms动态生成基于GET参数的下拉菜单

我来帮你搞定这个问题!核心思路是先在表单的__init__方法里根据GET请求的参数动态创建下拉字段,再用Crispy Forms的动态布局来渲染这些字段。下面是完整的实现方案:

第一步:完善表单类,动态添加字段并配置Crispy布局

首先补全你的ReportSample表单,在初始化时读取GET里的id列表,为每个id生成对应的下拉菜单,同时构建Crispy的布局:

from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Div, Submit
from .models import VariantAnnotationSampleRun, SampleRun

class ReportSample(forms.Form):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 从GET请求中获取所有id值
        sr_ids = self.data.getlist('id')
        
        # 为每个id动态生成下拉字段
        for idx, sr_id in enumerate(sr_ids):
            try:
                # 先获取对应的SampleRun实例(根据你的模型调整)
                sample_run = SampleRun.objects.get(id=sr_id)
                # 查询该SampleRun对应的选项数据,这里假设用id和name作为选项值和显示文本
                variant_choices = VariantAnnotationSampleRun.objects.filter(
                    sample_run_id=sample_run
                ).values_list('id', 'name')
                
                # 给每个字段起唯一名字(比如variant_0、variant_1)
                field_name = f"variant_selection_{idx}"
                self.fields[field_name] = forms.ChoiceField(
                    label=f"选择样本「{sample_run.name}」的注释项",
                    choices=variant_choices,
                    required=True,
                    widget=forms.Select(attrs={'class': 'form-control'})
                )
            except SampleRun.DoesNotExist:
                # 如果id对应的SampleRun不存在,跳过这个字段
                continue
        
        # 配置Crispy Form的基础设置
        self.helper = FormHelper()
        self.helper.form_method = 'get'  # 和请求方法保持一致
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-md-3'
        self.helper.field_class = 'col-md-9'
        
        # 动态构建Crispy布局
        self.helper.layout = self._build_dynamic_layout(sr_ids)
    
    def _build_dynamic_layout(self, sr_ids):
        # 初始化布局字段列表
        layout_components = []
        
        # 把每个动态生成的字段加入布局
        for idx, _ in enumerate(sr_ids):
            field_name = f"variant_selection_{idx}"
            # 可以给每个字段单独加样式容器
            layout_components.append(Div(
                Field(field_name),
                css_class='form-group mb-4'
            ))
        
        # 添加提交按钮
        layout_components.append(Submit('generate_report', '生成报告', css_class='btn btn-primary'))
        
        # 返回完整布局
        return Layout(*layout_components)

第二步:视图中使用表单

在视图里,我们直接把GET请求传入表单,然后渲染即可:

from django.shortcuts import render
from .forms import ReportSample

def report_generator(request):
    # 把GET参数传入表单,触发动态字段生成
    form = ReportSample(request.GET)
    
    if request.method == 'GET' and form.is_valid():
        # 处理用户选择的结果:提取所有动态字段的值
        selected_variants = []
        for field_name, value in form.cleaned_data.items():
            if field_name.startswith('variant_selection_'):
                selected_variants.append(value)
        
        # 这里写你的业务逻辑,比如生成报告
        return render(request, 'report_result.html', {'selected': selected_variants})
    
    # 如果是首次访问或者表单无效,渲染表单页面
    return render(request, 'report_form.html', {'form': form})

第三步:模板中渲染表单

模板里只需要加载Crispy标签,然后用{% crispy form %}渲染即可:

{% load crispy_forms_tags %}
<div class="container mt-5">
    <h2>样本注释报告生成器</h2>
    {% crispy form %}
</div>

关键注意事项

  • 字段命名唯一性:一定要给每个动态字段起唯一的名字(比如用索引后缀),避免字段覆盖。
  • 异常处理:要处理GET参数无效(比如id对应的数据不存在)的情况,避免表单初始化报错。
  • 布局灵活性:如果需要更复杂的布局(比如每个下拉框带说明文字),可以在_build_dynamic_layout里调整Div的结构,加入额外的HTML元素。

内容的提问来源于stack exchange,提问作者trouselife

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:39:40