使用基于类的通用视图处理表单
表单处理程序大致上分为3种:
- 初始化的GET请求,显示空值或预设值;
- 使用了无效的数据进行POST提交,重新显示表单并报错;
- 正确地进行POST提交,会重定向到其他网页;
可以观察一个简单的联系表:
from django import forms
class ContactForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
def send_email(self):
# send email using the self.cleaned_data dictionary
pass
可以使用FormView
来构成它的视图函数,:
from myapp.forms import ContactForm
from django.views.generic.edit import FormView
class ContactView(FormView):
template_name = 'contact.html'
form_class = ContactForm
success_url = '/thanks/'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
form.send_email()
return super().form_valid(form)
FormView
从TemplateResponseMixin
继承了template_name
来指定渲染使用的模板,form_valid
会重定向success_url
所指定的地址。
处理模型表单Model forms
在处理模型时,使用通用视图会很方便。通用视图会自动创建ModelForm
,只要在模型类中:
- 如果指定了
model
属性,就会使用该模型类; - 如果使用
get_object()
返回一个对象,就会使用该对象的类; - 如果给出了
queryset
,就会使用该查询集;
模型表单视图提供了一个form_valid()
实现,可以自动保存模型。 如果您有任何特殊要求,可以覆盖它; 见下面的例子。您甚至不需要为CreateView
或UpdateView
提供success_url
,它们将在模型对象上使用get_absolute_url()
。如果要使用自定义ModelForm(例如添加额外验证),只需在视图上设置form_class
即可。
首先为UserProfile
添加get_absolute_url
方法,告诉Django如何计算对象的规范URL的方法:
class UserProfile(models.Model):
"""为已经使用了内置User模型的项目来拓展用户模型 """
# user对象可使用 user.userprofile, 或 user.proflie 得到用户信息
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
location = models.CharField('地址', max_length=64, blank=True)
tel = models.CharField('联系方式', max_length=50, blank=True)
about_me = models.TextField('关于我')
mod_date = models.DateTimeField('上次修改日期', auto_now=True)
avatar = models.ImageField('用户头像', upload_to='avatar/%Y/%m/%d',
default='avatar/wenhuang.jpg', blank=True, null=True)
class Meta:
verbose_name = "用户信息"
app_label = 'users'
def __str__(self):
return f"{self.user.__str__()} profile"
def get_absolute_url(self):
return reverse('users:profile', args=(self.user.id,))
在views中导入UpdateView
来构建编辑用户信息时使用的表单:
from django.views.generic.edit import CreateView, DeleteView, UpdateView
@method_decorator(login_required, name='dispatch')
class ProfileUpdate(UpdateView):
model = UserProfile
template_name = "edit_profile.html"
# form_class = UserProfileForm
fields = ['location', 'tel', 'about_me', 'avatar']
其中:
model
指定使用的Model
类;template_name
指定渲染的模板;fields
指定表单显示的字段;
可以看到无需编写逻辑,只需要配置基于类的视图即可。
如过需要使用自定义的ModelForm
,可以使用form_class
来指定,在Meta
类中使用fields
来指定要显示的字段
class UserProfileForm(ModelForm):
class Meta:
model = UserProfile
fields = ('location', 'tel', 'about_me', 'avatar')
widgets = {
'about_me': Textarea(attrs={'cols': 80}),
}
但他和fields
同时存在时,就会抛出ImproperlyConfigured
错误。
最后,在URLconf中需要使用<int:pk>
的形式指定具体的对象:
urlpatterns = [
# path('edit_profile/<profile_id>', edit_profile, name='edit_profile'),
path('edit_profile/<int:pk>', ProfileUpdate.as_view(), name='edit_profile'),
]
CreateView
和DeleteView
的使用可以参考 Form handling with class-based
Model和request.user
例如,要使用CreateView跟踪创建对象的用户,可以使用自定义ModelForm
执行此操作。 首先,将外键关系添加到模型中:
from django.contrib.auth.models import User
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
# ...
在视图中,确保不要将created_by
添加到fields
字段中,需要重写form_valid()
来添加user:
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)
CBV 模式下的用户验证和权限
一般基于函数的视图要进行用户验证直接使用 @login_required 装饰器,而基于类的视图
除了像上文提到的使用@method_decorator(login_required, name='dispatch')
装饰器方法来进行用户验证,还可以继承LoginRequiredMixin
来实现:
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
redirect_field_name = 'redirect_to'
与@login_required装饰器类似的,如果没有设置login_url
,默认会重定向到settings.LOGIN_URL
,未经身份验证的用户的所有请求都将重定向到登录页面或显示HTTP 403 Forbidden错误,具体取决于raise_exception
参数-True:抛出PermissionDenied
错误;False(默认):会重定向到登录页面。
而要对登录的用户进行测试时,使用UserPassesTestMixin
类,需要重写test_func()
,同时可以设置AccessMixin
类任意值来定制如何处理验证未通过的用户:
from django.contrib.auth.mixins import UserPassesTestMixin
class MyView(UserPassesTestMixin, View):
def test_func(self):
return self.request.user.email.endswith('@example.com')
要进行权限验证时,要多重继承PermissionRequiredMixin
类
from django.contrib.auth.mixins import PermissionRequiredMixin
class MyView(PermissionRequiredMixin, View):
permission_required = 'polls.can_vote'
# Or multiple of permissions:
permission_required = ('polls.can_open', 'polls.can_edit')
就像使用@permission_required
装饰器一样,使用permission_required
字段指定特定的权限。同样的这个类也是继承自AccessMixin
的,所以可以设置它的任何属性。
CBV模式下添加消息
FBV模式下使用消息:
from django.contrib import messages
def confirm(request, token):
# ...
no_user_message = _('no this user please register')
messages.add_message(request, DANGER, no_user_message)
在CBV模式下,需要继承SuccessMessageMixin
类, 源码:
from django.contrib import messages
class SuccessMessageMixin:
"""
Add a success message on successful form submission.
"""
success_message = ''
def form_valid(self, form):
response = super().form_valid(form)
success_message = self.get_success_message(form.cleaned_data)
if success_message:
messages.success(self.request, success_message)
return response
def get_success_message(self, cleaned_data):
return self.success_message % cleaned_data
cleaning_data
是表单中用于字符串格式化的已清理数据,
使用时
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(SuccessMessageMixin, CreateView):
model = Author
success_url = '/success/'
success_message = "%(name)s was created successfully"