如何在表单向导中使用 ModelFormSet
Posted
技术标签:
【中文标题】如何在表单向导中使用 ModelFormSet【英文标题】:How to work with ModelFormSet in Form Wizard 【发布时间】:2015-01-20 03:56:21 【问题描述】:Django 文档没有很好地记录这个主题。事实上,他们在文档中的唯一参考是这一段:
如何使用 ModelForm 和 ModelFormSet
WizardView.instance_dict. WizardView 支持 ModelForms 和 ModelFormSets。除了 initial_dict 之外,as_view() > 方法采用 instance_dict 参数,该参数应包含基于 >ModelForm 的步骤的模型实例和基于 ModelFormSet 的步骤的查询集。
我还没有找到任何关于如何使用它的好而清晰的例子。有人可以帮我解决这个问题吗?
具体来说:
forms.py 中要做什么? 如果我只在表单的某些步骤而不是所有步骤中需要 ModelFormSet 怎么办? 我需要在views.py 和模板中做什么?以我正在做的一个用例和小项目为例,我分享我的代码:
用例: 用户想要以多步骤形式注册,在第一步中他介绍了他的姓名和姓氏。 在第二步中他介绍了他的护照号码和酒店登记,他还想登记他的儿子和妻子,他们将与他一起去这家酒店(这里我想使用来自HotelRegistration模型的模型集)。 在第三步中,他键入了他的航班信息。如果表单有效并保存在数据库中,则会收到确认消息。这是我的代码:
models.py
class Event(models.Model):
name = models.CharField(max_length=100)
date_from = models.DateField(auto_now=False)
date_to = models.DateField(auto_now=False)
description = models.TextField()
def __unicode__(self):
return self.name
class Hotel(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class HotelRegistration(models.Model):
pax_first_name = models.CharField(max_length=50)
pax_last_name = models.CharField(max_length=50)
hotel = models.ForeignKey(Hotel)
def __unicode__(self):
return self.pax_first_name
class Registration(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
passport = models.CharField(max_length=15)
city_origin = models.CharField(max_length=50)
flight_date_from = models.DateField(auto_now=False)
flight_date_to = models.DateField(auto_now=False)
require_transfer = models.BooleanField(default=None)
event = models.ForeignKey(Event)
hotel_registration = models.ForeignKey(HotelRegistration, blank=True, null=True)
def __unicode__(self):
return self.first_name
forms.py
from django import forms
from .models import Registration
class FormStep1(forms.ModelForm):
class Meta:
model = Registration
fields = ['first_name', 'last_name']
widgets =
'first_name': forms.TextInput(attrs='placeholder': 'Nombre de la persona que esta reservando', 'label_tag': 'Nombre'),
'last_name': forms.TextInput(attrs='placeholder': 'Apellido')
class FormStep2(forms.ModelForm):
class Meta:
model = Registration
fields = ['passport', 'hotel_registration']
exclude = ('first_name', 'last_name', 'event' , 'city_origin', 'flight_date_from', 'flight_date_to', 'require_transfer')
widgets =
'passport': forms.NumberInput(attrs='placeholder':'Escriba su pasaporte')
class FormStep3(forms.ModelForm):
class Meta:
model = Registration
fields = ['city_origin', 'flight_date_from', 'flight_date_to', 'require_transfer', 'event']
exclude = ('first_name', 'last_name', 'hotel_registration')
widgets =
'city_origin': forms.TextInput(attrs='placeholder':'Ciudad desde donde esta viajando'),
'flight_date_from': forms.DateInput(format=('%d-%m-%Y'), attrs='class':'myDateClass', 'placeholder':'Select a date'),
'flight_date_to': forms.DateInput(format=('%d-%m-%Y'), attrs='class':'myDateClass', 'placeholder':'Select a date'),
'require_transfer': forms.Select(),
'event': forms.Select()
views.py
from django.shortcuts import render
from django.contrib.formtools.wizard.views import SessionWizardView
from django.http import HttpResponseRedirect
from django.views.generic import TemplateView
from django.forms.models import inlineformset_factory
from .models import Registration, HotelRegistration
from .forms import FormStep1, FormStep2, FormStep3
FORMS = [
("step1", FormStep1),
("step2", FormStep2),
("step3", FormStep3)
]
TEMPLATES =
"step1" : "wizard/step1.html",
"step2" : "wizard/step2.html",
"step3" : "wizard/step3.html"
class TestFormWizard(SessionWizardView):
instance = None
def get_form_instance(self, step):
if self.instance is None:
self.instance = Registration()
return self.instance
def get_form(self, step=None, data=None, files=None):
form = super(TestFormWizard, self).get_form(step, data, files)
HotelRegistFormSet = inlineformset_factory(HotelRegistration, Registration, can_delete=True, extra=1)
# determine the step if not given
if step is None:
step = self.steps.current
if step == '2':
hotel_registration_formset = HotelRegistFormSet(self.steps.current, self.steps.files, prefix="step2")
return form
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def done(self, form_list, **kwargs):
self.instance.save()
return HttpResponseRedirect('/register/confirmation')
class ConfirmationView(TemplateView):
template_name = 'wizard/confirmation.html'
模板
% extends "base.html" %
% load i18n %
% block content %
<p>Step wizard.steps.step1 of wizard.steps.count </p>
<form action="" method="post">% csrf_token %
<table>
wizard.management_form
% if wizard.form.forms %
wizard.form.management_form
% for form in wizard.form.forms %
form
% endfor %
% else %
wizard.form
% endif %
</table>
% if wizard.steps.prev %
<button name="wizard_goto_step" type="submit" value=" wizard.steps.first ">% trans "first step" %</button>
<button name="wizard_goto_step" type="submit" value=" wizard.steps.prev ">% trans "prev step" %</button>
% endif %
<input type="submit" value="% trans "submit" %"/>
</form>
% endblock %
【问题讨论】:
您解决了这个问题吗?我即将放弃使用带有向导视图的模型表单集。 您解决了这个问题吗?你呢,Nostalgio? 【参考方案1】:forms.py 中要做什么?
覆盖 SessionWizardView 的 get_form_instance 方法。这是 FormWizard 用来确定模型实例是否与模型表单一起使用的方法
WizardView.get_form_instance(step) 仅当 ModelForm 用作 step step 的表单时才会调用此方法。 返回一个 Model 对象,在为 step step 实例化 ModelForm 时,该对象将作为实例参数传递。如果初始化表单向导时没有提供实例对象,则返回 None。
这可以在 SessionWizardView 实现中的每个步骤有条件地完成。我不明白你想要做的足够好来给你一个确切的例子,所以这里有一个更通用的例子。
def get_form_instance(self, step):
if step == u'3':
past_data = self.get_cleaned_data_for_step(u'2')
hotel_name = past_data['hotel_field']
hotel = Hotel.objects.get(name=hotel_name)
return hotel #do NOT set self.instance, just return the model instance you want
return self.instance_dict.get(step, None) # the default implementation
如果我只在表单的某些步骤而不是所有步骤中需要 ModelFormSet 怎么办?
见上文;使用 'if step == (form/step name)' 表达式来确定每一步会发生什么。
在views.py和模板中我需要做什么?
使用 ModelForm 并向其传递模型对象实例将设置初始表单值。你需要更多吗?
希望这向您展示了 FormWizard 中预期的结构。与我使用过的 Django 的任何其他部分相比,FormWizard 需要一个非常特定的结构并且是一个整体类。
【讨论】:
以上是关于如何在表单向导中使用 ModelFormSet的主要内容,如果未能解决你的问题,请参考以下文章
django:如何在表单向导中使用 inlineformset?
如何修复modelformset错误:__init __()缺少1个必需的位置参数:'user'
具有动态表单添加的 Modelformset 仅保存第一个表单