Django FormWizard с динамическими формами

Я хочу реализовать простой FormWizard из 2 частей. Форма 1 будет динамически генерироваться примерно так:

class BuyAppleForm(forms.Form):
   creditcard = forms.ChoiceField(widget = forms.RadioSelect)
   type = forms.ChoiceField(widget = forms.RadioSelect)
   def __init__(self,*args, **kwargs):
        user = kwargs['user']
        del kwargs['user']

        super(BuyAppleForm, self).__init__(*args, **kwargs)

        credit_cards = get_credit_cards(user)
        self.fields['creditcard'].choices = [(card.id,str(card)) for card in credit_cards]

        apple_types= get_types_packages()
        self.fields['type'].choices = [(type.id,str(type)) for type in apple_types]

Это будет динамически создавать форму со списками доступных вариантов.

Моя вторая форма, я на самом деле не хочу никакой информации. Я просто хочу отобразить экран подтверждения, содержащий информацию о кредитной карте, информацию о яблоке и суммы денег (итого, налог, доставка). Как только пользователь нажмет OK, я хочу начать покупку яблок.

Я смог реализовать единственный способ, передав объект request.user в kwargs. Однако с FormWizard я не могу понять это.

Неправильно ли я подхожу к проблеме и не является ли FormWizard правильным способом для этого? Если это так, как форма __init__ метод доступа к объекту пользователя из запроса HTTP?

5 ответов

Решение

Я не знаю, является ли ответ на собственный вопрос приемлемым поведением в Stackru, вот мое решение моей собственной проблемы.

Во-первых, канаву FormWizard.

У меня есть одна форма. Два взгляда: buy_apples а также buy_apples_confirm

Первый вид только обрабатывает GET. Он распечатывает несвязанную форму с действием, чтобы перейти к URL второго вида.

Второе представление проверяет наличие параметра POST с именем "подтверждения". Если он отсутствует (как это не происходит при первой загрузке представления), он:

  1. Настраивает виджет на все поля, чтобы быть HiddenInput
  2. Записывает шаблон, который выдает резюме заказа. Этот шаблон также устанавливает скрытое поле, называемое "подтверждение", равным 1 (хотя это поле не существует в форме)

Когда пользователь щелкает, чтобы купить яблоки, форма отправляется обратно и buy_apples_confirm представление вызывается еще раз. На этот раз присутствует параметр POST, называемый "подтвердить", поэтому мы фактически обрабатываем транзакцию покупки, и пользователь получает свои яблоки.

Я приветствую любые критические замечания по этому методу или лучшие способы справиться с ситуацией. Я новичок в Django и обнаружил, что есть много разных способов решения проблемы. Я хочу учиться у лучших, хотя.

Я не использовал его, но для описываемой вами ситуации кажется, что вы можете попробовать FormPreview вместо FormWizard. Из документации это звучит как то, что вы ищете.

Когда я пытался выяснить FormWizard, я искал все и нашел ответы, такие как большинство из них, которые просто говорят, что не используют его. FormPreview будет работать нормально, так как OP интересует только одноуровневая форма, но вопрос о том, как использовать FormWizard, остается в силе.

Несмотря на то, что этот вопрос настолько старый, я думаю, что здесь полезно ответить, потому что этот вопрос задается на очень многих сайтах, и я не вижу ни сплоченного ответа на него, ни четкого решения в документах.

Я думаю, что с точки зрения вопроса OP, переопределение process_step - это путь. Хитрость заключается в создании формы (или представления) в этом методе, которая будет получать данные из первой формы.

Я добавил этот form_setup в мой forms.py в качестве служебной оболочки (думаю, конструктор):

def form_setup(**kwargs):
    def makeform(data, prefix=None, initial=None):
        form = FormLev2(data, prefix, initial)
        for k, v in kwargs.items():
            if k == 'some_list':
                form.fields['some_list'].choices = v
            ...
        return form
    return makeform

Затем переопределите process_step следующим образом:

def process_step(self, request, process, step):
    if step == 1
        if form.is_valid():  #form from step 1
            objs = Table.objects.filter(...) #based on last form 
            self.form_list[1] = form_setup(some_list=[(o.id,o.name) for o in objs])  #(*)
    ...

Таким образом, вы можете динамически изменять form_list(*) в том смысле, что вы изменяете form_list в экземпляре FormWizard, а не сами определения форм. Функция-обертка необходима для этой функции, так как она возвращает функцию, которая будет создавать новый объект Form, который затем используется в FormWizard для вызова с данными для следующей формы, и позволяет использовать данные из предыдущей формы.,

Редактировать: для комментария Эрика и прояснить последнюю часть.

Также обратите внимание, что process_step будет вызываться с шагом [0,n] после шага n.

Как насчет изменения метода вызова, чтобы получить дополнительный параметр?

что-то похожее на это: http://d-w.me/blog/2010/3/18/15/

Спасибо, Крыс, что ответили на свой вопрос. Помог мне, но я все же получил некоторые замечания.

FormPreview - это не тот путь, который, насколько я знаю, не поддерживает динамические формы. Он опирается на фиксированный класс формы для генерации оттуда. Но мы генерируем динамически здесь с помощью функции. Может быть, FormPreview поддержит это однажды (или уже делает, и я не знаю, как).

Решение Krys, похоже, делает то же самое, что и FormPreview. Оставлен только хеш, поэтому пользователь может изменять данные в скрытых полях или вы проверяете это снова? Если вы проверите это снова, это не будет следовать за СУХОЙ, потому что вы дублируете проверку (хорошо, может быть в методе многократного использования, так что только незначительное повторение).

Что мне было интересно, как вы настраиваете виджет? Вы дублируете форму с новыми виджетами или есть способ изменить это динамически?

Другие вопросы по тегам