Form
局部钩子、全局钩子
ModelForm
两种的区别
Form介绍
form组件的主要功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
使用form组件实现书籍的增加功能
首先定义一个类

在views.py中定义一个BookForm的类 from django import forms #自定义一个form类 class BookForm(forms.Form): title = forms.CharField( max_length = 12, max_length = 6, label = "书名", #页面 label标签显示的中文名 widget = forms.widgets.TextInput(attrs={"class":"form-control"}) #widget 插件 ) publish_date = forms.DateField( label = "出版日期" widget = forms.widgets.DateInput(attrs={"type":"date","class":"form-control"}) ) phone = forms.CharField( label = "手机号", max_length = 11, required = True, #是否允许为空 widget = forms.widgets.TextInput(attrs={"class":"form-control"}) ) publisher = forms.ModelChoiceField( queryset = models.Publisher.objects.all(), #queryset:查询数据库中的数据 widget = forms.widgets.Select(attrs = {"class":"form-control}) ) authors = forms.ModelMultipleChoiceField( queryset = models.Author.objects.all(), widget = forms.widgets.SelectMultiple(attrs={"class":"form-control"}) )
对应的html操作

<form action="" method="post" novalidate autocomplete="off"> {%csrf_token%} {% for field in form_obj %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} <p> {{ field.errors.0 }}</p> </div> {% endfor %} <input type="submit" class="btn btn-info"> </form>
添加书籍函数

def addbook(request): form_obj = BookForm() if request.POST: #做校验 form_obj = BookForm(request.POST) if form_obj.is_valid(): #做数据有效性的校验 # 因为有多对多的字段,需要额外处理 authors = form_obj.cleaned_data.pop("authors") # 创建新书籍对象 book_obj = models.Book.objects.create(**form_obj.cleaned_data) # 将书籍对象和作者建立关联 book_obj.authors.add(*authors) return redirect('/book_list/') return render(request,'addbook.html',locals())
Form常用字段与插件
创建Form类时,主要涉及到【字段】和【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML
initial
初始值,input框里面的初始值
class LoginForm(forms.Form): username = forms.CharField( min_length = 8, label = "用户名" initial = "张三" #设置默认值 ) pwd = forms.CharField(min_length=6,label="密码")
error_messages
重写错误信息
class LoginForm(forms.Form): username = forms.CharField( min_length = 8, label = "用户名", initial = "张三", error_messages = { "required": "不能为空", "invalid": "格式错误", "min_length": "用户名最短8位" ) pwd = forms.CharField(min_length=6,label="密码")
radioSelect
单radio值为字符串,choices不建议固定,具体方法下方有说明
class LoginForm(forms.Form): ... ... gender = forms.fields.ChoiceField( choices = ((1,'男'), (2,'女'), (3,'保密')), label = "性别", initial = 3, widget = forms.widgets.RadioSelect() )
单选select
class LoginForm(forms.Form): ... hobby = forms.fields.ChoiceField( choice = ((1, "篮球"), (2, "足球"), (3, "双色球"), ), label = "爱好", initial = 3, widget = forms.widgets.Select() )
多选select
class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=[1, 3], widget = forms.widgets.SelectMultiple() )
单选CheckBox
class Login(forms.Form): ... keep = forms.fields.ChoiceField( label="是否记住密码", initial="checked", widget = forms.widgets.CheckboxInput() )
多选checkbox
class LoginForm(forms.Form): ... hobby = forms.fields.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget = forms.widgets.CheckboxSelectMultiple() )
关于choice的注意事项:
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段,**获取的值无法实时更新**,那么需要自定义构造方法从而达到此目的
具体方法:
class FInfo(forms.Form): ... publisher = forms.ModelChoiceField( queryset=models.Publisher.objects.all(), widget=forms.widgets.Select(attrs={"class": "form-control"}) )
Django Form所有内置字段

Field required=True 是否运行为空 widget=None, HTML插件 label=None, 用于生成label标签或显示内容 initial=None, 初始值 help_text=' ', 帮助信息(在标签旁显示) error_messages=None 错误信息{'required': '不能为空', 'invalid': '格式错误'} validators=[] 自定义验证规则 localize=False, 是否支持本地化 disabled=False, 是否可以编辑 label_suffix=None Label内容后缀 CharField(Field) max_length=None 最大长度 min_length=None 最小长度 strip=True 是否移除用户输入空白 IntegerField(Field) max_value=None, 最大值 min_value=None, 最小值 DecimalField(IntegerField) max_value=None, 最大值 min_value=None, 最小值 max_digits=None, 总长度 decimal_places=None, 小数位长度 BaseTemporalField(Field) input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} FileField(Field) allow_empty_file=False 是否允许空文件 ImageField(FileField) ... 注:需要PIL模块,pip3 install Pillow 以上两个字典使用时,需要注意两点: - form表单中 enctype="multipart/form-data" - view函数中 obj = MyForm(request.POST, request.FILES) ChoiceField(Field) choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查询数据库中的数据 empty_label="---------", # 默认空显示内容 to_field_name=None, # HTML中value的值对应的字段 limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField) ... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField) coerce = lambda val: val 对选中的值进行一次转换 empty_value= '' 空值的默认值 TypedMultipleChoiceField(MultipleChoiceField) coerce = lambda val: val 对选中的每一个值进行一次转换 empty_value= '' 空值的默认值 FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 required=True, widget=None, label=None, initial=None, help_text='' GenericIPAddressField protocol='both', both,ipv4,ipv6支持的IP格式 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符) UUIDField(CharField) uuid类型
自定义校验规则
1、使用内置的正则校验器: validators=[]
from django.core.validators import RegexValidator class BookForm(forms.Form): phone = forms.CharField( max_length=11, validators=[RegexValidator(r'^1[35789]\d{9}$',"手机号格式不正确")] )
2、手写一个校验函数
from django.core.exceptions import ValidationError # 自定义一个字段的校验规则函数 def phone_validate(value): # 拿用户填写的手机号去数据库中校验 is_exits = models.Book.objects.filter(phone=value) if is_exits: #如果手机号被注册,不能再注册 raise ValidationError("该手机号已经被注册") else: return value # 第二步在自定义的BookForm类中phone字段添加phone_validate from django.core.validators import RegexValidator class BookForm(forms.Form): ... phone = forms.CharField( max_length=11, validators = [RegexValidator(r'^1[356789]\d{9}$','手机号格式不正确'),phone_validate] )
3、使用钩子函数做校验
局部钩子(hook)
from django.core.exceptions import ValidationError black_list = ["alex",] class BookForm(forms.Form): ... # 定义一个局部钩子,对title字段做有效性验证 def clean_title(self): value = self.cleand_data.get("title") #判断有没有敏感词 #第一种方法: #if "alex" in value: # raise ValidationError("alex已被和谐") #else: # return value #第二种方法: for v in black_list: if v in value: raise ValidationError("{}为敏感词".format(v)) return value
全局钩子 (两个字段值做比较)
#全局钩子,在form类内部定义一个 clean(self) 方法 black_list = ["alex",] class BookForm(forms.Form): def clean(self): #取到所有字段的数据 # self.add_error("字段","密码和确认密码不一致") title = self.cleand_data.get("title") for v in black_list: if v in title: self.add_error("title","包含敏感词{}".format(v)) return title
form组件给html标签设置默认值
# 以修改书籍功能为例: def editbook(request,edit_id): book_obj = models.Book.objects.filter(id=edit_id).first() # 把ORM中一个对象快速转换成字典格式 from django.forms import model_to_dict book_dict = model_to_dict(book_obj) book_dict["publish_date"] = book_obj.publish_date.strftime("%Y-%m-%d") form_obj = BookForm(book_dict) if request.POST: # 从用户提交过来的数据中取数据 form_obj = BookForm(request.POST) if form_obj.is_valid(): #去数据库更新数据 book_obj.title = form_obj.cleaned_data.get("title") book_obj.publish_date = form_obj.cleaned_data.get("publish_date") book_obj.publisher_id = form_obj.cleaned_data.get("publisher") book_obj.save() book_obj.authors.set(form_obj.cleaned_data.get("authors")) return redirect('/book_list/') return render(request, 'editbook.html', locals())
ORM中把一个对象转换成字典格式
from django.forms import model_to_dict book_obj = models.Book.objects.filter(id=edit_id).first() book_dict = model_to_dict(book_obj)
补充进阶
批量添加样式
可通过重写form类的init方法来实现
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短8位"
}
...
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
for field in iter(self.fields):
self.fields[field].widget.attrs.update({
'class': 'form-control'
})
ModelForm
form与model的终极结合,具有以下功能:验证、数据库操作。
# 写一个和Model类一一对应的form
from django import forms
class BookModelForm(forms.ModelForm): class Meta: model = models.Book #对应的model中的类 fields = "__all__" # model类里所有的字段都展示 # fields = ["title",] # 指定展示某些字段 exclude = None #除了指定字段,其他字段都显示 labels = { #设置label标签名 "title":"书名", "phone":"手机号", "publish_date":"出版日期", "publisher":"出版社", "authors":"作者", } widgets = { #设置每个字段的插件信息 "title":forms.widgets.TextInput(attrs={"class":"form-control"}), "phone": forms.widgets.TextInput(attrs={"class": "form-control"}), "publish_date":forms.widgets.DateInput(attrs={"type":"date","class":"form-control"}), "publisher": forms.widgets.Select(attrs={"class": "form-control"}), "authors": forms.widgets.SelectMultiple(attrs={"class": "form-control"}), } error_messages = { #设置每个字段的报错提示信息 "publisher":{ "required":"必须选一个出版社!" } }
class Meta: 常用参数 model = models.Student # 对应的Model中的类 fields = "__all__" # 字段,如果是__all__,就是表示列出所有的字段 exclude = None # 排除的字段 labels = None # 提示信息 help_texts = None # 帮助提示信息 widgets = None # 自定义插件 error_messages = None # 自定义错误信息
instance = book_obj #实例名

def editbook(request,edit_id): book_obj = models.Book.objects.filter(id=edit_id).first() form_obj = BookModelForm(instance=book_obj) if request.POST: # 从用户提交过来的数据中取数据 form_obj = BookModelForm(request.POST,instance=book_obj) if form_obj.is_valid(): #去数据库更新数据 form_obj.save() return redirect('/book_list/') return render(request, 'editbook.html', locals())

def addbook(request): form_obj = BookModelForm() if request.POST: #做校验 form_obj = BookModelForm(request.POST) if form_obj.is_valid(): #做数据有效性的校验 form_obj.save() return redirect('/book_list/') # 实例化BookForm return render(request,'addbook.html',locals())

class Meta: model, #对应model的类 fields #字段 exclude=None #排查的字段 labels=None #提示信息 widgets=None #自定义插件 error_messages=None #自定义错误信息 field_classes=None #自定义字段类 验证执行过程: is_valid -> full_clean --> 钩子 --> 整体错误 字典字段验证: def clean_字段名(self): return "新值" 验证: model_form_obj.is_valid() model_form_obj.clean() model_form_obj.cleand_data 用于创建: model_form_obj = XXOOModelForm(request.POST) 用于更新和初始化 obj = model.tb.objects.get(id=1) model_form_obj = XXOOModelForm(request.POST,instance=obj)
Form和ModelForm区别
model可以操作数据库字段,form验证也有那几个字段,虽然耦合度很低,但是代码还是有重复的。如果使用ModelForm,可以利用model
里的字段,form里的字段就可以不用写了。