Можете ли вы использовать Мастер форм для форм модели в django?
У меня есть одна модель, и я создал форму из модели, используя ModelForm
. Теперь я хочу распространять форму на две страницы. Например, первые три поля будут отображаться на первой странице, после чего пользователь нажимает следующее, а последние три поля появляются на второй странице. Затем он нажимает кнопку "Отправить", и пользовательские данные добавляются в базу данных.
Я взглянул на документы для Мастера форм и, похоже, он будет работать и для форм модели? Может кто-то подтвердить это?
И если это так, может кто-нибудь объяснить процесс создания класса WizardView
.
Этот пример приведен в документах, и я не понимаю, что такое два вторых параметра. Является ли form_list просто списком объектов формы, которые вы создали на основе ваших классов форм? И что такое **kwargs
?
class ContactWizard(SessionWizardView):
def done(self, form_list, **kwargs):
do_something_with_the_form_data(form_list)
return HttpResponseRedirect('/page-to-redirect-to-when-done/')
Заранее благодарим за помощь!
Ответы
Ответ 1
Мастер форм встроен в Django 1.4, поэтому это хороший способ. Он должен делать то, что вы хотите, но вам может потребоваться несколько настроек.
Не беспокойтесь о kwargs
в done()
на данный момент - вам не понадобятся.
form_list
- это список форм, которые вы хотите использовать для своих шагов - от urls.py
urlpatterns = patterns('',
(r'^contact/$', ContactWizard.as_view([ContactForm1, ContactForm2])),
)
[ContactForm1, ContactForm2]
будет передан в done()
как form_list
.
Что вам нужно сделать, так это разбить ваш ModelForm
на отдельные формы. Самый простой способ сделать это (если вы хотите, чтобы ваша модель на нескольких формах) заключалась в том, чтобы не использовать ModelForm
, а просто создавать свою собственную форму. Это довольно легко:
from django import forms
class ContactForm1(forms.Form):
subject = forms.CharField(max_length=100)
sender = forms.EmailField()
class ContactForm2(forms.Form):
message = forms.CharField(widget=forms.Textarea)
Как только ваши формы отражают части вашей модели, просто создайте views
и patterns
, как описано в docs и set do_something_with_the_form_data(form_list)
к функции, которая завершает вашу модель из данных формы, а затем выполняет сохранение.
Вы можете использовать ModelForm
, но - только если вы можете убедить его создать различные формы для Мастера форм, которые будут использоваться для каждого шага - это будет сложная часть.
Ответ 2
Скажите, что ваша модель имеет два поля
class AModel( Model ):
fieldA = CharField()
fieldB = CharField()
Мы хотим установить каждое поле на отдельном шаге с помощью FormWizard
. Итак, мы создаем два ModelForm
s, каждый из которых показывает одно поле:
class Form1( ModelForm ):
class Meta:
model = AModel
fields = ( 'fieldA', )
class Form2( ModelForm ):
class Meta:
model = AModel
fields = ( 'fieldB', )
Мы вызываем наш мастер форм AWizard
; запись url.py
должна выглядеть примерно так:
url( r'^$', AWizard.as_view( [ Form1, Form2 ] ) ),
В реализации AWizard
нам нужно убедиться, что все формы записывают свои данные в один экземпляр, который мы затем сохраняем в базе данных:
class AWizard( SessionWizardView ):
instance = None
def get_form_instance( self, step ):
if self.instance is None:
self.instance = AModel()
return self.instance
def done( self, form_list, **kwargs ):
self.instance.save()
Обратите внимание, что мы переопределяем метод get_form_instance
. Этот метод возвращает экземпляр модели, к которому привязаны формы.
Вы можете подумать (я сделал), что этот метод создает экземпляр для первого запроса (первый шаг мастера), а затем продолжает использовать тот же самый экземпляр для всех шагов.
Собственно, это немного сложнее. Для каждого запроса создается новый экземпляр AWizard
, который, в свою очередь, создает новый экземпляр AModel
. Итак, шаги не используют один экземпляр для начала.
Магия происходит, когда отправляется последняя форма. На этом этапе все формы проверяются, каждая форма вызывает get_form_instance
, и они заполняют один экземпляр AModel
.
Затем этот экземпляр сохраняется в done
.
Ответ 3
Вид, предложенный @wuerg, не работал у меня, я должен был сделать это:
class AWizard( SessionWizardView ):
def dispatch(self, request, *args, **kwargs):
self.instance = AModel()
return super(ApplyWizard, self).dispatch(request, *args, **kwargs)
def get_form_instance( self, step ):
return self.instance
def done( self, form_list, **kwargs ):
self.instance.save()
return HttpResponseRedirect(reverse(thanks))
Ответ 4
Мне пришлось изменить решение @wuerg и @madmen для работы в моей утилите (сохранение модели после каждого шага). Большим преимуществом этого подхода является то, что он всегда использует один и тот же экземпляр AModel
вместо создания нового экземпляра для каждого шага:
class AWizard(SessionWizardView):
instance = AModel()
def dispatch(self, request, *args, **kwargs):
return super(AWizard, self).dispatch(request, *args, **kwargs)
def get_form_instance(self, step):
return self.instance
def done(self, form_list, **kwargs):
self.save_model()
return render_to_response('done.html')