django学习第十四天--Forms和ModelForm(django formset)

网友投稿 331 2022-08-29


django学习第十四天--Forms和ModelForm(django formset)

Forms和ModelForm

进行数据校验,先看数据校验的过程

注册页面图解: 前端为了用户体验会做一些校验,不满足校验要求会报错 服务端也会对数据进行一些校验,不满足校验要求会报错 数据库也会对数据进行一些校验,不满足校验要求会报错 form组件和modleform组件就是让我们的数据校验过程更加简单一些,功能非常强大

Forms组件

提供了三个功能

1.能够帮我们生成HTML标签 2.标签中保留之前用户输入的数据 3.数据校验

forms组件的使用流程

1.在views.py文件中创建一个自定义的form类

from django import forms #需要先导入这个forms class RegisterForm(forms.Form): phone = forms.CharField() username = forms.CharField() password = forms.CharFied() #用label参数可以将HTML中的label标签中内容显示为中文 phone = forms.CharField(label='手机号') username = forms.CharField(label='用户名') password = forms.CharField(label='密码')

2.在views.py文件中写上视图函数

def register(request): if request.method == 'GET': form = RegisterForm() #将上面的form类进行实例化 return render(request,'register.html',{'form':form})#将实例化后的对象返回到html中生成对应的属性的标签

3.创建一个html文件,比如叫作register.html,内容如下

Title

注册页面

#novalidate取消浏览器自带的错误提示 {% csrf_token %}
{{ form.phone }}
{{ form.username }}
{{ form.password }}

保留原数据和校验功能

forms组件代码

class RegisterForm(forms.Form): #每个字段,默认都有一个required=True的参数,表示该字段数据不能为空 #phone = form.CharField(label='手机号',required=True) phone = forms.CharField( label='手机号', required=True, #错误提示信息的自定制 error_messages={ 'required':'小敏敏提示您,不能为空!', } ) username = forms.CharField(label='用户名') password = forms.CharField(label='密码')

views.py内容如下

def register(request): if request.method == 'GET': form = RegisterForm() return render(request,'register.html',{'form':form}) else: print(request.POST) form = RegisterForm(data=request.POST) #把数据交给了RegisterForm,那么在通过form来渲染标签时,就将原来的的数据以默认值的形式(value='值')生成在了对应标签上 if form.is_valid(): #执行校验,如果所有数据都校验通过了,返回True,但凡有一个数据没有通过校验,返回False print('通过校验的数据',form.cleaned_data) return HttpResponse('ok') print('错误信息>>>',form.errors) return render(request,'register.html',{'form':form})

register.html内容

Title

注册页面

{% csrf_token %}
{{ form.phone }} {{ form.phone.errors.0 }}
{{ form.username }} {{ form.username.errors.0 }}
{{ form.password }} {{ form.password.errors.0 }}

Form常用字段属性与插件

initial 初始值

#生成input标签有个默认值 username = fomrs.CharField(label='用户名',initial='小红')

widget 插件的意思,能够定制我们的标签显示效果

示例1 密文输入框 password = forms.CharField( label='密码', #CharField默认插件是TextInput相当于type='text',下面相当于修改type='password',完整写法forms.widgets.PasswordInput,下面是简写 widget = forms.PasswordInput, ) 示例2 给标签加上一些其他属性 password = forms.CharField( label='密码', #attrs={'标签属性':'属性值'} widget=forms.PasswordInput(attrs={'class':'c1 c2','placeholder':'请输入密码'}), )

生成单选下拉框 ChoiceField默认插件是widget=forms.Select

#sex = forms.fields.ChoiceField() fields可以不用写 sex = forms.ChoiceField( choices =((1,'女'),(2,'男')), #(1,'女')生成一个option标签,value值为1,文本内容为女 label ='性别', # initial =2 #初始值 )

单选radio框 widget=forms.RadioSelect

sex = forms.ChoiceField( choices=((1,'女'),(2,'男')), label='性别', #initial =2, #widget=forms.Select, #ChoiceField的默认插件 #input,type='radio'的单选框 #widget=forms.RadioSelect, #修改插件,并设置属性 widget=forms.RadioSelect(attrs={'class':'c1'}) )

多选下拉框 MultipleChoiceField

hobby = forms.MultipleChoiceField( choices=((1,'喝酒'),(2,'抽烟'),(3,'励志')), label='爱好' )

多选checkbox框 widget=forms.CheckboxSelectMultiple

hobby = forms.MultipleChoiceField( choices=((1,'喝酒'),(2,'抽烟'),(3,'励志')), label = '爱好', #widget=forms.CheckboxInput, #做单选功能的复(多)选框形式,必须勾选协议的那个选框 widget=forms.CheckboxSelectMultiple, )

单选功能的复选框形式,像那种勾选同意协议的那个选框 widget=forms.CheckboxInput 里面choices只有true或false

status = forms.MultipleChoiceField( choices=(('True',同意),('Flase',不同意)) label='同意是否勾选协议', widget=forms.CheckboxInput, )

date类型

bday = forms.CharField( label='生日', widget=forms.TextInput(attrs={'type':'date'}), 只要设置一个date属性就可以了 )

单选或者多选框使用数据库中的数据

方式1 forms.ModelChoiceField

models中的模型类 class Publish(models.Model): name = models.CharField(max_length=32) def __str__(self): return self.name form类中的字段写法 #生成选择框,并使用数据库中的数据 必须要用这个ModelChoiceField publish = forms.ModelChoiceField( queryset=models.Publish.objects.all(), #必须是queryset ) 会自动生成单选下拉框,并且option标签value属性对应的值,是queryset参数对应的queryset集合里面的models模型类对象的id值,option标签的文本内容是每个models模型类对象。 #生成标签的时候,页面显示第一层会有个自带的'--------',第二个往后才是数据

方式2 forms.ChoiceField

publish = forms.ChoiceField( choices=models.Publish.objects.all().values_list('id','name') #quertset[(1,'xx出版社'),] ) #生成标签的时候,没有上面那个自带的'--------',直接就显示的是数据

Form所有内置字段

Field

required=True, 是否不为空 widget=None, HTML插件,设置插件相当于可以更改标签并设置属性 label=None, 用于生成label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息{'required':'不能为空',} validators=[], 自定义验证规则 disabled=False, 是否可以编辑,默认False,可编辑

CharField(Field)

max_length=None, 最大长度 min_length=None, 最小长度 strip=True, 是否移除用户输入空白

IntegerField(Field)

max_value=None, 最大值 min_value=None, 最小值

FloatField(Field)

...

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

DurationField(Field) 时间间隔:%d %H:%M:%S.%f

...

RegexField(CharField)

regex, 自定义正则表达式 max_length=None 最大长度 min_length=None 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid':'...'}

EmailField(CharField)

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)

URLField(Field)BooleanField(Field)NullBooleanField(BooleanField)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.ModelMultipleChoiceFieldTypedChoiceField(ChoiceField)

coerce = lambda val:val 对选中的值进行一次转换 empty_value='' 空值的默认值

MultipleChoiceField(ChoiceField)TypedMultipleChoiceField(MultipleChoiceField)

coerce=lambda val:val 对选中的每一个值进行一次转换 empty_value='' 空值的默认值

ComboField(Field)

fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])

MultiValueField(Field)

PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用

SplitDateTimeField(MultiValueField)

input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']

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类型 复制代码

数据校验功能

格式不对的错误信息定制 'initial':'邮箱格式不对',

简单校验示例

form类 class DataForm(forms.Form): #不能为空,长度不能超过10位,不能短于4位 username = forms.CharField( label='用户名', max_length=10, min_length=4, error_messages={ 'required':'不能为空', 'max_length':'太长啦,受不了', 'min_length':'太短啦,不舒服', } ) #可以为空 phone = forms.CharField( label='手机号', required=False, ) #要满足邮箱格式,不能为空 email = forms.EmailField( label='邮箱', error_messages={ 'invalid':'邮箱格式不对', #invalid验证邮箱格式的 } ) #提交的数据不能超出选项 sex = forms.ChoiceField( label='性别', choices=((1,'女'),(2,'男')), widget=forms.RadioSelect, ) #form只校验类中写的属性对应的那些数据 #向csrf_token这些不会校验

views.py

def data(request): if request.method == 'GET': form = DataForm() return render(request,'data.html',{'form':form}) else: form = DataForm(request.POST) if form.is_valid(): #做校验,都通过返回True #校验成功之后的数据form.cleaned_data里面不包含没有校验的数据,也就是不会校验form类中没有指定的属性对应的数据 print(form.cleaned_data)# {'username': 'chao', 'phone': '', 'email': 'asdf@xx.com', 'sex': '1'} return HttpResponse('ok') else: print(form.errors) return render(request,'data.html',{'form':form})

data.html文件

Title {% csrf_token %}

{{ form.username }} {{ form.username.errors.0 }}
{{ form.phone }} {{ form.phone.errors.0 }}
{{ form.email }} {{ form.email.errors.0 }}
{{ form.sex }} {{ form.sex.errors.0 }}

RegexValidator验证器

示例代码

from django.core.validators import RegexValidator class DataForm(forms.Form): #不能为空,长度不能超过10位,不能短于4位 username = forms.CharField( ... #RegexValidator('正则','不符合正则时的错误信息') validators=[RegexValidator(r'^a','用户名必须以a开头'),RegexValidator(r'b$','用户名必须以b结尾')] )

校验函数

示例

import re from django.core.validators import RegexValidator from django.core.exceptions import ValidationError #ValidationError是django提供的一个数据校验失败的一个错误类型 #先定义一个函数,函数中我们可以做数据的校验,如果数据校验失败,我们可以raise ValidationError('错误信息') def mobile_match(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号格式有误') 使用 phone =forms.CharField( label='手机号', validators=[mobile_match,], #列表里面写我们自己定义的函数名 )

局部钩子

代码示例

class DataForm(forms.Form): #不能为空,长度不能超过10位,不能短于4位 username = forms.CharField( label='用户名', max_length=10, min_length=4, ) #可以为空 phone = forms.CharField( required=False, validators=[mobile_match,], ) 。。。 #局部钩子,能够对username这个字段的数据进行一个更高级的校验 #语法 clean_字段名称 def clean_username(self): uname = self.cleaned_data.get('username') #cleaned_data.get 获取数据 if '666' in uname: raise ValidationError('不能光喊6,没有用') #如果校验通过,需要return这个值 return uname def clean_phone(self): pass

全局钩子

示例代码

class DataForm(forms.Form): #密码 password = forms.CharField() #再次确认密码 confirm_password=forms.CharField() #多个字段进行比较时,一般都是用全局钩子固定函数名clean def clean(self): p1 = self.cleaned_data.get('password') p2 = self.cleaned_data.get('confirm_password') if p1 != p2: #直接raise错误,这个错误信息保存到了全局错误信息里面 #也就是self.errors里面 #所以要用add_error('属性名','错误提示信息') self.add_error('confirm_password','抱歉,两次密码不一致') self.add_error('password','抱歉,两次密码不一致') #如果校验通过,必须return self.cleaned_data return self.cleaned_data

源码部分,主要看cleaned_data,为什么会在没有走视图函数的时候,就能获取数据

def _clean_fields(self): for name, field in self.fields.items(): # print(name, field) # name属性名称,field是CharField()类的实例化对象 # value_from_datadict() gets the data from the data dictionaries. # Each widget type knows how to retrieve its own data, because some # widgets split data over several HTML fields. if field.disabled: value = self.get_initial_for_field(field, name) else: value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) try: if isinstance(field, FileField): initial = self.get_initial_for_field(field, name) value = field.clean(value, initial) else: # 将CharField中的max_length,required.. value = field.clean(value) # 将属性对应的CharField里面的简单校验规则进行了校验 self.cleaned_data[name] = value #self.cleaned_data['username'] = 'abc' if hasattr(self, 'clean_%s' % name): #clean_username value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self.add_error(name, e)

简单总结

首先对所有的我们自己定义的类中的属性进行循环,self.fields能够获取到所有的属性,循环过程中先对属性,比如username=form.CharField(max_length=12),里面的参数校验规则进行了校验,这个校验完成之后,给self.clean_data这个字典进行了赋值 self.clead_data['username'] ='sad'然后对该字段的局部钩子进行了校验,在局部钩子中我们可以拿到self.clean_data['username']这个数据进行处理,别忘了局部钩子校验成功之后,要return这个字段的值,然后才进入下一次循环,处理下一个属性当上面的所有循环执行完之后,才执行我们的全局钩子,全局钩子中校验成功之后别忘了return self.clean_data

静态文件配置和templates模板配置

可以在app01/app02应用文件夹下分别创建一个static名称的文件夹,存放自己应用的静态文件 可以在app01/app02应用文件夹下分别创建一个templates名称的文件夹,存放自己应用的html文件 django寻找html文件静态文件的查找顺序是先找总配置中的templates或者statics文件夹,如果找不到对应文件,就去每个应用下的static或者templates去找对应文件,顺序是按照settings.py配置文件中的INDSTALL_APP中注册app的顺序进行查找,找到就返回,不在继续往下找了,所以要注意,最好在static或者templates下面创建一个以应用名为名称的文件夹,将文件放到这里面,以防文件名冲突导致的问题。

-------------------------------------------

个性签名:代码过万,键盘敲烂!!!

如果觉得这篇文章对你有小小的帮助的话,记得“推荐”哦,博主在此感谢!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:django学习第十五天-modelform的补充(django modelviewset)
下一篇:django学习第十二天--ajax请求和csrftoken认证的三种方式(django csrftoken验证)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~