Можете ли вы использовать Мастер форм для форм модели в 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')