代码之家  ›  专栏  ›  技术社区  ›  Ghislain Leveque

如何制作“工作流”窗体

  •  4
  • Ghislain Leveque  · 技术社区  · 16 年前

    对于我的项目,我需要许多“工作流”表单。我解释自己:

    用户在第一个字段中选择一个值,验证表单,并根据第一个字段值显示新字段。然后,根据其他字段,可以出现新字段…

    我如何才能以通用的方式实现它?

    4 回复  |  直到 16 年前
        1
  •  3
  •   lprsd    16 年前

    我想你要找的解决办法是 django form wizard

    基本上,您为不同的页面定义单独的表单,并根据前一个屏幕中的输入自定义下一个表单,最后,您将所有表单的数据收集在一起。

    具体来看 process step 窗体向导上的高级选项。

    FormWizard.process_step()
    """
    Hook for modifying the wizard's internal state, given a fully validated Form object. The Form is guaranteed to have clean, valid data.
    This method should not modify any of that data. Rather, it might want to set self.extra_context or dynamically alter self.form_list, based on previously submitted forms.
    Note that this method is called every time a page is rendered for all submitted steps.
    The function signature:
    """
    
    def process_step(self, request, form, step):
        # ...
    

    如果您只需要根据同一表单中的其他下拉列表修改下拉列表值,那么应该查看实现的 dajaxproject

        2
  •  1
  •   Orange Box    16 年前

    我认为这取决于问题的规模。

    您可以编写一些显示和隐藏表单字段的通用JavaScript(然后在表单本身中应用这些CSS类)。对于显示和隐藏字段的数量相对较少的情况,这将很好地工作。

    如果你想更进一步,你需要考虑在Django开发动态表单。我建议你不要像Ghislain建议的那样修改类中的[字段]。这里有一篇关于 dynamic forms 它向你展示了一些方法。

    我可以想象,一个好的解决方案可能是将上面文章中的动态形式与 django FormWizard . 表单向导将引导您浏览各种不同的表单,然后允许您在最后保存总体数据。

    但是它有一些问题,因为你不可能不丢失你所执行的步骤的数据就轻易地后退一步。同时显示所有表单需要对表单向导进行一点自定义。有些API没有文档记录或被认为是公共的(所以要小心它在将来的django版本中会发生变化),但是如果你看一下 the source 您可以很容易地扩展和重写表单向导的某些部分,以完成所需的操作。

    最后一个简单的表单向导方法是使用5个静态表单,然后在向导中自定义表单选择,更改下一个表单是什么,只显示相关表单。这同样可以很好地工作,但这取决于表单在以前的选择上有多大的变化。

    希望有帮助,如果有什么问题可以问!

        3
  •  -1
  •   Peter Rowell    16 年前

    听起来你想要一个半封闭式的解决方案。结帐 Taconite plugin 对于jQuery。我用这个来填充表单上的下拉列表等。工作得很好。

    至于“一般性”…您的容器类上可能有返回子类列表的标准方法,然后有一个模板fragmen t,它知道如何以某种“标准”方式格式化子类。

        4
  •  -1
  •   Ghislain Leveque    16 年前

    好吧,我发现了一个根本不使用Ajax的解决方案,而且对我来说还不错:

    根据需要创建尽可能多的表单,并使它们成为彼此的子类。将整数隐藏字段放入第一个字段:

    class Form1(forms.Form):
        _nextstep = forms.IntegerField(initial = 0, widget = forms.HiddenInput())
        foo11 = forms.IntegerField(label = u'First field of the first form')
        foo12 = forms.IntegerField(label = u'Second field of the first form')
    
    class Form2(Form1):
        foo21 = forms.CharField(label = u'First field of the second form')
    
    class Form3(Form2):
        foo31 = forms.ChoiceField([],
            label=u'A choice field which choices will be completed\
                depending on the previous forms')
        foo32 = forms.IntegerField(label = u'A last one')
    
        # You can alter your fields depending on the data.
        # Example follows for the foo31 choice field
        def __init__(self, *args, **kwargs):
            if self.data and self.data.has_key('foo12'):
                self.fields['foo31'].choices = ['make','a','nice','list',
                    'and you can','use your models']
    

    好的,这是表单的视图:

    def myview(request):
        errors = []
        # define the forms used :
        steps = [Form1,Form2,Form3]
        if request.method != 'POST':
            # The first call will use the first form :
            form = steps[0]()
        else:
            step = 0
            if request.POST.has_key('_nextstep'):
                step = int(request.POST['_nextstep'])
            # Fetch the form class corresponding to this step
            # and instantiate the form
            klass = steps[step]
            form = klass(request.POST)
            if form.is_valid():
                # If the form is valid, increment the step
                # and use the new class to create the form
                # that will be displayed
                data = form.cleaned_data
                data['_nextstep'] = min(step + 1, len(steps) - 1)
                klass = steps[data['_nextstep']]
                form = klass(data)
            else:
                errors.append(form.errors)
        return render_to_response(
            'template.html',
            {'form':form,'errors':errors},
            context_instance = RequestContext(request))
    

    我看到的唯一问题是,如果在模板中使用表单,它将调用form.errors,从而自动验证新表单(例如,Form2)和前一个表单(Form1)的数据。所以我要做的是迭代表单中的项目,只使用item.id,item.label和item。因为我已经在视图中提取了前一个表单的错误并将其传递给模板,所以我添加了一个DIV来在页面顶部显示它们。