在学习 Django Form 和 ModelForm 之前,先实践下基础 Django 表单 | 菜鸟教程。我在实践时遇到了编码问题并提供了如下解决方案:【Python】’ascii’ codec can’t decode byte 0xe4 in position 0: ordinal not in range(128)
如果我们手动将输入之后的数据在 views 中都获取到再传递到网页,这样是可行的,但是很不方便,所以 Django 提供了更简单易用的 forms 来解决验证等这一系列的问题。
Form对象封装了一系列Field和验证规则,Form类都必须直接或间接继承自django.forms.Form,定义Form有两种方式:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100,label='主题')
message = form.CharField(widget=forms.TextArea)
sender = form.EmailField()
cc_myself = forms.BooleanField(required=False)
Field:Form对象中的一个字段,如:EmailField表示email字段,如果这个字段不是有效的email格式,就会产生错误。
Django Form提供了许多钩子供开发者做不同层次的验证,在Form.full_clean()方法中主要包括了三个层次的验证,并且这三个验证依次进行:
form类的运行顺序是init,clean,validte,save
其中clean和validate会在form.is_valid()方法中被先后调用。
# 获取文章详细信息,包含一个CommentForm表单
def detail(req, article_id):
if req.method == 'POST':
form = CommentForm(req.POST)
if form.is_valid():
name = form.cleaned_data['name']
address = form.cleaned_data['address']
email = form.cleaned_data['email']
context = form.cleaned_data['context']
article = Article.objects.get(pk=article_id)
comment = Comment(article=article, name=name, address=address, email=email, context=context)
comment.save()
return HttpResponseRedirect('blog/detail.html')
cleaned_data 就是读取表单返回的值,返回类型为字典dict型。
cleaned_data[‘email’] 读取name为 ‘email’的表单提交值,并赋予 email变量。
cleaned_data中对应的值都必须按照model中定义的字段类型取值,否则校验不通过或保存时报错。
在Form的基础上,Django还提供了一种ModelForm。如果你的FORM表单十分贴近数据Model,那么可以用ModelForm来节省大量代码。
models.py
class UserType(models.Model):
caption = models.CharField(max_length=32)
class UserInfo(models.Model):
username = models.CharField(max_length=32)
email = models.EmailField()
user_type = models.ForeignKey(to='UserType',to_field='id')
forms.py
from django import forms
from django.forms import fields
class UserInfoForm(forms.Form):
# username = models.CharField(max_length=32) <-- models
username = fields.CharField(max_length=32)
# email = models.EmailField() <-- models
email = fields.EmailField()
# user_type = models.ForeignKey(to='UserType',to_field='id') <-- models
user_type = fields.ChoiceField(
choices=models.UserType.objects.values_list('id','caption')
)
# 下面的操作是让数据在网页上实时更新。
def __init__(self, *args, **kwargs):
super(UserInfoForm,self).__init__(*args, **kwargs)
self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')
使用情景:如果我们在models里设计数据库时,已经设计好了一个类(就是数据库的表)之后想复用这个类的信息来作为表单的模型
forms.py
from django.forms import ModelForm
class UserInfoModelForm(forms.ModelForm):
class Meta:
model = models.UserInfo # 与models建立了依赖关系
fields = "__all__"
views.py
def index(request):
if request.method == "GET":
obj = UserInfoModelForm()
return render(request,"index.html",{'obj':obj})
elif request.method == "POST":
obj = UserInfoModelForm(request.POST)
print(obj.is_valid()) # 这是方法,别忘记了加括号
print(obj.cleaned_data)
print(obj.errors)
return render(request,"index.html",{'obj':obj})
如何定义http上定义的字段呢,自定义写成中文的?之前的用法是在Form里写上label。Model Form定义要用verbose_name
models.py
class UserInfo(models.Model):
username = models.CharField(max_length=32, verbose_name='用户')
email = models.EmailField(verbose_name='邮箱')
user_type = models.ForeignKey(to='UserType',to_field='id', verbose_name='类型')
如果不在model里定义,在modelForm里实现,利用labels
class UserInfoModelForm(forms.ModelForm):
class Meta:
model = models.UserInfo
fields = "__all__"
labels = {
'username':'用户名',
'email':'邮箱',
}
Form验证:
UserInfoForm -> Form -> BaseForm( 包含is_valid等方法)
ModelForm验证:
UserInfoModelForm -> ModelForm -> BaseModelForm -> BaseForm
ModelForm因为model和form耦合太密切,所以一般写小程序用它。