如何制作“工作流程”表格

时间:2009-12-09 16:32:51

标签: django django-forms

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

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

如何以通用方式实现?

4 个答案:

答案 0 :(得分:3)

我认为您正在寻找的解决方案是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

答案 1 :(得分:1)

我认为这取决于问题的严重程度。

您可以编写一些显示和隐藏表单字段的通用JavaScript(然后在表单本身中应用这些css类)。这对于显示和隐藏字段的数量相对较少很有效。

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

我认为一个好的解决方案可能是将上面帖子中的动态表单与django FormWizard结合起来。 FormWizard将引导您完成各种不同的表单,然后允许您在最后保存整个数据。

它有一些陷阱,因为你不能轻易地退回一步而不会丢失你的步骤数据。同时显示所有表单将需要对FormWizard进行一些自定义。有些API没有记录或被认为是公开的(所以要警惕它在Django的未来版本中的变化)但是如果你看一下the source你可以很容易地扩展和覆盖表单向导的部分来做你的事情需要。

最后一个更简单的FormWizard方法是说5个静态表单,然后在向导中自定义表单选择并更改下一个表单,只显示相关表单。这又可以很好地运作,但这取决于形式在以前的选择上有多大变化。

希望有所帮助,如有任何问题可以提出任何问题!

答案 2 :(得分:-1)

听起来你想要一个AJAXy类型的解决方案。查看Taconite plugin以获取jQuery。我用这个来填充表格上的下拉等。工作得非常好。

至于“通用”......您可能在容器类上有标准方法返回子项列表,然后有一个模板片段知道如何以某种“标准”方式格式化它。

答案 3 :(得分:-1)

好的,我找到了一个根本不使用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}},它会调用form.errors,因此会使用前一个(Form1)的数据自动验证新表单(例如Form2)。所以我所做的就是遍历表单中的项目,只使用{{item.id}},{{item.label}}和{{item}}。由于我已经在视图中获取了上一个表单的错误并将其传递给模板,因此我添加了一个div以在页面顶部显示它们。