Я хочу реализовать простой мастер FormWizard из двух частей. Форма 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]
Это динамически создаст форму со списками доступных вариантов.
Моя вторая форма, на самом деле я не хочу ничего вводить. Я просто хочу отобразить экран подтверждения, содержащий информацию о кредитной карте, информацию о яблоке и суммы денег (общая сумма, налог, доставка). Как только пользователь нажимает кнопку «ОК», я хочу, чтобы покупка яблока началась.
Мне удалось реализовать способ единой формы, передав объект request.user в kwargs. Однако с FormWizard я не могу этого понять.
Неправильно ли я подхожу к проблеме и не подходит ли FormWizard для этого? Если это так, как может метод Form __init__ получить доступ к объекту пользователя из HTTP-запроса?





Я не знаю, является ли ответ на собственный вопрос приемлемым поведением в StackOverflow, вот мое решение моей собственной проблемы.
Во-первых, откажитесь от FormWizard.
У меня одна форма.
Два представления: buy_apples и buy_apples_confirm
Первый просмотр обрабатывает только GET. Он распечатывает несвязанную форму с действием для перехода к URL-адресу второго представления.
Второй вид проверяет наличие параметра POST с именем «confirm». Если его нет (а его нет, когда представление загружается в первый раз), он:
Когда пользователь щелкает, чтобы купить яблоки, форма отправляется обратно, и представление buy_apples_confirm вызывается еще раз. На этот раз присутствует параметр POST с именем «confirm», поэтому мы фактически обрабатываем транзакцию покупки, и пользователь получает свои яблоки.
Я приветствую любую критику этого метода или лучших способов справиться с ситуацией. Я новичок в Django и считаю, что есть много разных способов решения проблемы. Но я хочу учиться у лучших.
Я не использовал его, но для ситуации, которую вы описываете, похоже, вы можете попробовать ФормаПредварительный просмотр вместо FormWizard. Из документации это похоже на то, что вам нужно.
Я не уверен, поможет ли это (с FormPreview), но что-то, что вы могли бы сделать в своем коде выше, - это не помещать этот код в метод в этом. например x = BuyAppleForm (), затем выполните x.set_choices_for_user (request.user) перед его отображением.
Спасибо, Крыс, за ответ на свой вопрос. Мне помогли, но замечания остались.
FormPreview не подходит, поскольку, насколько мне известно, он не поддерживает динамические формы. Он полагается на фиксированный класс формы для генерации оттуда. Но здесь мы генерируем динамически с помощью функции. Возможно, FormPreview когда-нибудь поддержит это (или уже поддерживает, и я не знаю как).
Решение Krys похоже на то же, что и FormPreview. Остается только хеш, поэтому пользователь может изменить данные в скрытых полях или вы проверите их снова ?. Если вы проверите его снова, это не будет следовать за DRY, потому что вы дублируете проверку (хорошо, может быть метод многократного использования, поэтому только небольшое повторение).
Что мне было интересно, как настроить виджет? Вы дублируете форму с новыми виджетами или есть способ изменить это динамически?
Как насчет изменения метода вызов, чтобы он принимал дополнительный параметр?
что-то похожее на это: http://d-w.me/blog/2010/3/18/15/
Когда я пытался понять FormWizard, я искал повсюду и нашел ответы, такие как большинство из них, которые просто говорят, что не используйте его. FormPreview будет работать нормально, поскольку OP интересуется только одноуровневой формой, но вопрос о том, как использовать FormWizard, все еще актуален.
Несмотря на то, что этот вопрос очень старый, я думаю, что здесь полезно ответить, потому что этот вопрос задается на очень многих сайтах, и я не вижу ни единого ответа на него, ни четкого решения в документации.
Я думаю, что с точки зрения вопроса OPs, переопределение process_step - это путь. Уловка заключается в создании формы (или представления) в этом методе, которая будет получать данные из первой формы.
Я добавил этот form_setup в свой forms.py как служебную оболочку (конструктор think):
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.
Линия for k, v in kwargs должна быть for k, v in kwargs.items(). Кроме этого, отличное решение :)
Спасибо, что указали на FormPreview. Однако в моем случае часть проблемы заключается в передаче дополнительного значения ** kwargs (request.user как «пользователь») в в этом конструктора формы (необходимого для генерации динамической формы), и я не понимаю, как это возможно с FormPreview.