Lut*_*ger 15 django django-forms django-views django-class-based-views
我想将django 1.3的基于类的通用视图用于表单,但有时必须在一个表单中管理多个表单类.但是,看起来基于FormMixin的现有视图假定单个表单类.
通用视图是否可行,我该怎么做?
编辑:澄清一下,我有一个表单但不止一个(基于ModelForm)类.例如在Django文档的inline_formset例子,我想提出一个网页,一个作家和他的书可以一次编辑,在一个单一的形式:
author_form = AuthorForm(request.POST, instance = author)
books_formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
Run Code Online (Sandbox Code Playgroud)
面对类似的问题,我得出的结论是,这是不可能的.
虽然每页有多个表单本身就是一个设计错误,但存在各种各样的麻烦.例如,用户填写两种形式,其中一种形式的点击提交,另一种形式的数据丢失.解决方法需要复杂的控制器,需要了解页面上所有表单的状态.(另见这里有关相关问题的一些讨论.)
如果每页有多个表单不是您的确切要求,我建议您查看其他解决方案.
例如,通常一次只能向用户显示一个可编辑的表单.
在我的情况下,我切换到django-formwizard(不是一个django.contrib一个,这有点旧,似乎目前正在重新设计,但这一个 更新:从Django的1.4版开始,django-formwizard应用程序将可用django.contrib,取代旧的formwizard它已经在主干,见文档).对于用户,我认为页面上实际上有多个表单,但只有一个是可编辑的.并且用户必须按预定顺序填写表单.这使得处理多种形式变得更加容易.
否则,如果表格确实需要一次性呈现,将它们合并为一个可能是有意义的.
更新(澄清后):
不,你也不能使用泛型处理formset FormView.虽然你的例子似乎很容易实现:我认为它与关于formsets的Django文档中的这个例子非常相似.它处理两个formset,你只需要用表单替换一个(我认为你仍然需要指定前缀以避免元素id属性的可能冲突).
简而言之,在你的情况下,我将子类django.views.generic.base.View和覆盖get()以及post()处理表单和formset的方法类似于上面的例子来自Django docs.
在这种情况下,我认为可以同时呈现表单和表单集可编辑 - 只需一个按钮即可提交它们.
另一个更新:
在Django trac中有一个活跃的最近票,#16256更多基于类的视图:formsets派生的通用视图.如果一切顺利,新的通用视图将被添加到Django的:FormSetsView,ModelFormSetsView和InlineFormSetsView.特别是,最后一个'提供了一种显示和处理模型的方法,使用它的内联表格集'.
在单个视图页面上显示两个模型的字段
您必须扩展 django.views.generic.View 类并重写 get(request) 和 post(request) 方法。
我就是这样做的。
我正在使用Django 1.11。
我的View类呈现了我的两种形式:
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
})
Run Code Online (Sandbox Code Playgroud)
我有User和UserProfile型号。
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()
Run Code Online (Sandbox Code Playgroud)
澄清1: @receiver(post_save, sender=User)
说明 2:如果创建:(来自 View 类的说明)
如果正在创建用户,则创建一个 UserProfile,其中 user = 刚刚通过 UserForm 提交的用户实例
地址是从 ProfileForm 收集的,并在调用 user.save() 之前添加到用户实例中
我有两种形式:
用户表单:
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"
)
Run Code Online (Sandbox Code Playgroud)
个人资料表格:
class ProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('address',)
Run Code Online (Sandbox Code Playgroud)
我希望我能很好地理解你的问题,这会对你(以及其他人)有所帮助。:)