пропустить шаг x до шага y и проверить данные шага x

На самом деле у меня большая проблема с формой мастера 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)

Как видите, если нет вопросов, я пропускаю второй шаг (против_индикации), чтобы перейти к следующему шагу (предписание).

Проблема появляется здесь. При рендеринге последнего шага в форме мастера недостаточно данных. В запросе ddt есть это: с пропуском шага. Так что, если я загружу файл, он заполнит данные против_индикации вместо данных предписания и повторно отрендерит мне последний шаг...

Я попытался сделать все это, не пропуская второй шаг, и посмотреть, как выглядит запрос ddt: без пропуска шаг.

У кого-то есть решение, позволяющее иметь правильные данные, когда я пропускаю шаг, пожалуйста?

Спасибо за ваши дальнейшие ответы


person Mathieu Dfr    schedule 20.01.2016    source источник


Ответы (2)


Я не думаю, что get_context_data - правильный метод для этого; FormWizard — это очень специфический класс, который ограничивает возможности выполнения различных функций.

Типичный способ указать, когда FormWizard пропускает шаг, — это использовать condition_dictionary. Django использует структуру для включения формы только для шага, когда условия (установленные как вызываемые) возвращают True. Если нет, то форма этого шага не вызывает 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',) 
)
person Ian Price    schedule 20.01.2016
comment
Спасибо @IanPrice! Этот пример использования condition_dict действительно более понятен, чем официальный пример django. Это спасло мой день :) - person Mathieu Dfr; 27.01.2016
comment
Рад слышать! Да, документы действительно не очень хороши для FW. - person Ian Price; 27.01.2016
comment
И если я смог помочь, отметьте это как ответ и проголосуйте за него :) - person Ian Price; 27.01.2016
comment
@IanPrice Спасибо за отличный ответ. Я попытался улучшить его в другом ответе. Пожалуйста, дайте мне знать, если это поможет. - person David D.; 08.10.2020

Основываясь на отличном ответе Яна Прайса, я попытался внести некоторые улучшения, потому что заметил, что со временем структура может помочь, особенно если вы попытаетесь изменить порядок своих шагов:

  • Я использовал класс данных для централизации данных о пошаговом мастере, а затем генерировал необходимые данные для URL-адресов позже.
  • Я использовал лямбда-выражения для возврата True, а предоставление callable необязательно.
  • Я извлек STEP_X в файлы const.py для повторного использования.
  • Я пытался, насколько это возможно, собирать данные в самом представлении класса, а не в функциях.

Вот код (Python 3.7+):

const.py

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

просмотры.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())
person David D.    schedule 08.10.2020