django泛型(类)视图中的多个表单类
我希望使用django 1.3中基于类的通用视图处理表单,但有时必须在一个表单中管理多个表单类。但是,基于FormMixin的现有视图似乎采用单个表单类 通用视图是否可以这样做?我将如何做 编辑:为了澄清,我有一个表单,但有多个(基于模型表单的)类。例如,在django文档中的inline_formset示例中,我想展示一个页面,在该页面中,作者和他的书可以以单一形式同时编辑:django泛型(类)视图中的多个表单类,django,django-forms,django-views,django-class-based-views,Django,Django Forms,Django Views,Django Class Based Views,我希望使用django 1.3中基于类的通用视图处理表单,但有时必须在一个表单中管理多个表单类。但是,基于FormMixin的现有视图似乎采用单个表单类 通用视图是否可以这样做?我将如何做 编辑:为了澄清,我有一个表单,但有多个(基于模型表单的)类。例如,在django文档中的inline_formset示例中,我想展示一个页面,在该页面中,作者和他的书可以以单一形式同时编辑: author_form = AuthorForm(request.POST, instance = author) b
author_form = AuthorForm(request.POST, instance = author)
books_formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
面对类似的问题,我得出结论,这是不可能的 虽然每页有多个表单本身就是一个设计错误,带来了各种各样的麻烦。例如,用户填写两个表单,在其中一个表单上单击submit,然后从另一个表单上丢失数据。解决方法需要复杂的控制器,该控制器需要了解页面上所有表单的状态。(有关相关问题的讨论,另请参见。) 如果每页有多个表单不是您的确切要求,我建议您考虑其他解决方案 例如,通常一次只能向用户显示一个可编辑的表单 在我的例子中,我切换到了
django formwizard
(不是一个django.contrib,它有点旧,似乎正在重新设计,但是更新:从django的1.4版开始,django formwizard
应用程序将在django.contrib
,取代旧的formwizard。它已经在主干中,)。对于用户来说,我让它看起来好像页面上实际上有多个表单,但只有一个是可编辑的。用户必须按预定顺序填写表格。这使得处理多个表单变得更加容易
否则,如果表单确实需要一次全部呈现,那么将它们组合成一个表单可能是有意义的
更新(澄清后): 不,您也不能使用generic
FormView
处理表单集。尽管您的示例似乎很容易实现:我认为它与表单集上的Django文档非常相似。它处理两个表单集,您只需要将其中一个替换为表单(我认为您仍然需要指定前缀,以避免元素“id
属性”可能发生冲突)
简而言之,在您的例子中,我将子类化django.views.generic.base.View
并重写get()
和post()
方法来处理表单和表单集,类似于django文档中的上述示例
在这种情况下,我认为可以使用一个按钮来提交表单和表单集可编辑
另一更新:
Django trac中有一个活动的最近票证。如果一切顺利,Django将添加新的通用视图:
FormSetView
、ModelFormSetView
和InlineFormSetView
。特别是,最后一个“提供了一种使用内嵌表单集显示和处理模型的方法”。django的原则之一是,您可以用几个较小的表单构建一个大表单,只需一个提交按钮。这就是为什么
-标记不是由django本身生成的
通用视图的问题,不管是否基于类,以及背景中的多种形式,当然是天空的限制。这些表单可能以某种方式相互关联:一个“母亲”——表单和可选的额外数据,取决于母亲中的数据(比如onetoone)。还有一个模型,它通过外键和/或中间表连接到其他几个模型,在这些表中,您可以使用form+formset。然后是一个全表单集类型的页面,就像在管理员中,当您直接在列表视图中编辑一些字段时一样。其中每一种都是不同类型的多表单视图,我不认为创建一个涵盖所有情况的通用视图是有成效的
但是,如果您有一个“母亲”模型,您可以使用标准的
UpdateView
或CreateView
并在处理母亲模型的代码之后,为从get()
和post()
调用的额外表单添加方法。例如,在表单\u valid()
中,如果母表单有效,则可以处理其他表单。您将拥有母亲的pk,然后使用该pk以其他形式连接数据。在一个查看页面上显示两个模型的字段
您必须扩展django.views.generic.View类并重写get(请求)和post(请求)方法
我就是这样做的
我正在使用Django 1.11
这是我的表单(由两个表单组成)的外观:
我的视图类,它呈现了我的两个表单:
from django.views.generic import View
class UserRegistrationView(View):
# Here I say which classes i'm gonna use
# (It's not mandatory, it's just that I find it easier)
user_form_class = UserForm
profile_form_class = ProfileForm
template_name = 'user/register.html'
def get(self, request):
if request.user.is_authenticated():
return render(request, 'user/already_logged_in.html')
# Here I make instances of my form classes and pass them None
# which tells them that there is no additional data to display (errors, for example)
user_form = self.user_form_class(None)
profile_form = self.profile_form_class(None)
# and then just pass them to my template
return render(request, self.template_name, {'user_form': user_form, 'profile_form': profile_form})
def post(self, request):
# Here I also make instances of my form classes but this time I fill
# them up with data from POST request
user_form = self.user_form_class(request.POST)
profile_form = self.profile_form_class(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
user_profile = profile_form.save(commit=False)
# form.cleaned_data is a dictionary which contains data validated
# by fields constraints (Say we have a field which is a number. The cleaning here would
# be to just convert a string which came from the browser to an integer.)
username = user_form.cleaned_data['username']
password = user_form.cleaned_data['password']
# This will be clarified later
# You can save each object individually if they're not connected, as mines are (see class UserProfile below)
user.set_password(password)
user.userprofile = user_profile
user.save()
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect('user:private_profile')
# else: # form not valid - each form will contain errors in form.errors
return render(request, self.template_name, {
'user_form': user_form,
'profile_form': profile_form
})
我有一个用户
和用户档案
模型。
User
是django.contrib.auth.models.User
和UserProfile
如下所示:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
friends = models.ManyToManyField('self', null=True, blank=True)
address = models.CharField(max_length=100, default='Some address 42')
def get_absolute_url(self):
return reverse('user:public_profile', kwargs={'pk': self.pk})
def __str__(self):
return 'username: ' + self.user.username + '; address: ' + self.address
@receiver(post_save, sender=User) # see Clarification 1 below
def create_user_profile(sender, instance, created, **kwargs):
if created: # See Clarification 2 below
UserProfile.objects.create(user=instance, address=instance.userprofile.address)
@receiver(post_save, sender=User)
def update_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
澄清1:@receiver(保存后,发送方=用户)
- 当用户被保存时(我在某处编写了User.save()(User是类User的实例)),UserProfile也将被保存
- 如果正在创建用户,则创建用户配置文件 其中user=刚才通过UserForm提交的用户实例
- 地址从ProfileForm中收集并添加到用户实例 正在调用user.save()
from django.views.generic import View
class UserRegistrationView(View):
# Here I say which classes i'm gonna use
# (It's not mandatory, it's just that I find it easier)
user_form_class = UserForm
profile_form_class = ProfileForm
template_name = 'user/register.html'
def get(self, request):
if request.user.is_authenticated():
return render(request, 'user/already_logged_in.html')
# Here I make instances of my form classes and pass them None
# which tells them that there is no additional data to display (errors, for example)
user_form = self.user_form_class(None)
profile_form = self.profile_form_class(None)
# and then just pass them to my template
return render(request, self.template_name, {'user_form': user_form, 'profile_form': profile_form})
def post(self, request):
# Here I also make instances of my form classes but this time I fill
# them up with data from POST request
user_form = self.user_form_class(request.POST)
profile_form = self.profile_form_class(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
user_profile = profile_form.save(commit=False)
# form.cleaned_data is a dictionary which contains data validated
# by fields constraints (Say we have a field which is a number. The cleaning here would
# be to just convert a string which came from the browser to an integer.)
username = user_form.cleaned_data['username']
password = user_form.cleaned_data['password']
# This will be clarified later
# You can save each object individually if they're not connected, as mines are (see class UserProfile below)
user.set_password(password)
user.userprofile = user_profile
user.save()
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect('user:private_profile')
# else: # form not valid - each form will contain errors in form.errors
return render(request, self.template_name, {
'user_form': user_form,
'profile_form': profile_form
})
用户表单:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True)
password_confirmation = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name', 'password', 'password_confirmation')
def clean(self):
cleaned_data = super(UserForm, self).clean()
password = cleaned_data.get("password")
password_confirmation = cleaned_data.get("password_confirmation")
if password != password_confirmation:
self.fields['password'].widget = forms.PasswordInput()
self.fields['password_confirmation'].widget = forms.PasswordInput()
self.add_error('password', "Must match with Password confirmation")
self.add_error('password_confirmation', "Must match with Password")
raise forms.ValidationError(
"Password and Password confirmation do not match"
)
class ProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('address',)
档案格式:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True)
password_confirmation = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name', 'password', 'password_confirmation')
def clean(self):
cleaned_data = super(UserForm, self).clean()
password = cleaned_data.get("password")
password_confirmation = cleaned_data.get("password_confirmation")
if password != password_confirmation:
self.fields['password'].widget = forms.PasswordInput()
self.fields['password_confirmation'].widget = forms.PasswordInput()
self.add_error('password', "Must match with Password confirmation")
self.add_error('password_confirmation', "Must match with Password")
raise forms.ValidationError(
"Password and Password confirmation do not match"
)
class ProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('address',)
我希望我能很好地理解你的问题,这将对你(以及其他人)有所帮助 谢谢你的警告。如果我理解正确的话,我的案例有点不同,虽然我只有一个表单,但它代表了多个模型。我编辑了我的问题来澄清它。正是国际扶轮