跳过步骤x到步骤y并验证步骤x数据

时间:2016-01-20 16:09:10

标签: django django-formwizard

我在django向导表单上确实存在一个大问题。

我有3个步骤。第二步可以包含数据。最后一步是文件上传步骤。

在WizardForm类中,我重写了get_context_data方法并将其包含在其中:

if self.steps.current == 'against_indication':
        questions = None
        try:
            # get the machine
            machine_id = self.kwargs['pk']
            machine = Machine.objects.get(pk=int(machine_id))
            # check if there is against indications
            if machine.type_question is False:
                questions = YhappsQuestion.objects.filter(type_modalite=machine.type)
            else:
                questions = CustomQuestion.objects.filter(machine=machine)
        except Machine.DoesNotExist:
                pass
        if len(questions) == 0:
            # we modify the form wizard to skip against indication step
            self.render_next_step(form, **kwargs)
            #self.render_goto_step(step='against_indication', goto_step='prescription', **kwargs)

如你所见,如果没有问题,我会跳过第二步(against_indication)进入下一步(处方)。

此处出现问题。渲染最后一步时,向导表单中没有足够的数据。在ddt的请求中有它: with skip step。 因此,如果我上传文件,它将填写against_indication数据而不是处方数据,并重新渲染我最后一步......

我试图在不跳过第二步的情况下完成所有这些操作,看看ddt的请求如何: without skip step

当我跳过步骤时,有人有一个允许拥有正确数据的解决方案,PLZ?

感谢您的进一步答复

2 个答案:

答案 0 :(得分:1)

我认为get_context_data是正确的方法; FormWizard是一个非常具体的类,它限制了您可以执行不同功能的位置。

指定FormWizard跳过某个步骤的典型方法是使用condition_dictionary。当条件(设置为callables)返回True时,Django使用该结构仅包含步骤的表单。如果没有,则该步骤的形式不会强制调用form.is_valid(),从而绕过该步骤的验证。这也确保了为每个步骤创建表单的所有隐藏管理信息。

以下是一个如何运作的示例:

# I always specify index values for steps so that all functions can share them
STEP_ONE = u'0'
STEP_TWO = u'1'
STEP_THREE = u'2'


def YourFormWizard(SessionWizardView):
    # Your form wizard itself; will not be called directly by urls.py, but rather wrapped in a function that provide the condition_dictionary
    _condition_dict = { # a dictionary with key=step, value=callable function that return True to show step and False to not
        STEP_ONE: return_true, # callable function that says to always show this step
        STEP_TWO: check_step_two, # conditional callable for verifying whether to show step two
        STEP_THREE: return_true, # callable function that says to always show this step
    }
    _form_list = [ # a list of forms used per step
        (STEP_ONE,your_forms.StepOneForm),
        (STEP_TWO, your_forms.StepTwoForm),
        (STEP_THREE, your_forms.StepThreeForm),
    ]
    ...


def return_true(wizard): #  callable function called in _condition_dict
    return True # a condition that is always True, for when you always want form seen

def check_step_two(wizard): #  callable function called in _condition_dict
    step_1_info = wizard.get_cleaned_data_for_step(STEP_ONE)
    # do something with info; can retrieve for any prior steps
    if step_1_info == some_condition:
        return True # show step 2
    else: return False # or don't

''' urls.py '''

your_form_wizard = YourFormWizard.as_view(YourFormWizard._form_list,condition_dict= YourFormWizard._condition_dict)

urlpatterns = patterns('',
    ...
    url(r'^form_wizard_url/$', your_form_wizard, name='my-form-wizard',) 
)

答案 1 :(得分:0)

基于很好的Ian Price答案,我尝试进行一些改进,因为我注意到结构可能会随着时间的推移而有所帮助,尤其是如果您尝试更改步骤顺序的话:

  • 我使用数据类来集中有关步骤向导的数据,然后稍后为url生成所需的数据。
  • 我使用lambdas返回True并给出callable是可选的
  • 我在一个const.py文件中提取了STEP_X以使其可重复使用
  • 我试图在类视图本身而不是函数中收集数据

以下是代码(Python 3.7 +):

const.py

STEP_0 = '0'
STEP_1 = '1'
STEP_2 = '2'

views.py

from dataclasses import dataclass
from typing import Optional, Callable, Sequence


@dataclass
class WizardStepData:
    step: str
    form_class: any
    trigger_condition: Optional[Callable] = None

    def __init__(self, step, form_class, trigger_condition=None):
        """ if trigger_condition is not provided, we return a Callable that returns True """
        self.step = step
        self.form_class = form_class
        self.trigger_condition = trigger_condition if trigger_condition else lambda _: True


def YourFormWizard(SessionWizardView):
    @staticmethod
    def check_step_one(wizard) -> bool:
        pass  # ...

    @classmethod
    def get_wizard_data_list(cls) -> Sequence:
        return [
            WizardStepData(step=STEP_0,
                           form_class=StepZeroForm),
            WizardStepData(step=STEP_1,
                           form_class=StepOneForm,
                           trigger_condition=cls.check_step_one),
            WizardStepData(step=STEP_2,
                           form_class=StepTwoForm),
        ]

    @classmethod
    def _condition_dict(cls) -> dict:
        return {data.step: data.trigger_condition for data in cls.get_wizard_data_list()}

    @classmethod
    def _form_list(cls) -> list:
        return [(data.step, data.form_class) for data in cls.get_wizard_data_list()]

urls.py

# ...
your_form_wizard = YourFormWizard.as_view(form_list=YourFormWizard._form_list(),
                                          condition_dict=YourFormWizard._condition_dict())