Fork me on GitHub

Django:form表单

window.location.href = 域名
#实现当前页面刷新

1.小知识:箭头函数

箭头函数,es6有,好处是不会重新绑定$(this)指向,会一直是最开始内容

  • 普通JS:

    <script>
            $(".btn-danger").click(function () {
                console.log($(this));
                $.ajax({
                    url:"/delpub/"+$(this).attr("del_id")+'/',
                    {#箭头函数,es6有,好处是不会重新绑定$(this)指向,#}
                    success:function(res){
                        console.log($(this));
                    }
    
                })
            })
        </script>
    

{#更改为箭头函数#}
<script>
        $(".btn-danger").click(function () {
            console.log($(this));
            console.log($(this).attr("del_id"));{# 获取标签属性值 #}
            $.ajax({
                url:"/delpub/"+$(this).attr("del_id")+'/',
                {#箭头函数,es6有,好处是不会重新绑定$(this)指向,#}
                success:(res) => {
                    console.log($(this));
                }

            })
        })
    </script>

2.Form介绍

  • form表单:在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。
  • form组件:
    • 生成页面可用HTML标签
    • 对用户信息提交的数据进行校验
    • 保留上次输入内容

3.使用form组件实现简单注册界面

1.定义form组件

from django import forms
#按照Django form组件先写一个类
class RegForm(forms.Form):
    #form字段写的名称是什么,前端生成的input标签name就是什么,通过label可以更改label值
    name = forms.CharField(label="用户名" min_length=6)
    pwd = forms.CharField(label="密码" widget=forms.PasswordInput)
    #widget=forms.PasswordInput 作用是让密码成为密文

#使用form组件实现注册
def form_login(request):
    form_obj = RegForm()#实例化form对象
    if request.method == "POST":
        #from_obj有提交的数据
        form_obj = RegForm(data=request.POST)
        #调用form_obj校验数据的方式 ,返回值True或False
        #如果校验失败,form_obj会返回给页面:此时它的除了有数据还有错误信息
        if form_obj.is_valid():
            #.....插入数据代码
            return HttpResponse("注册成功")
        
    return render(request,"form_login.html",{"form_obj":form_obj})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!--会自动生成p标签,p标签有input框和label标签-->
    {{ form_obj.as_p }}
</body>
</html>
  • 显示效果:

min_length=6 最小长度大于等于6

widget=forms.PasswordInput 让输入框的内容成为密文

{{ form_obj.errors }}返回当前所有错误

form表单添加novalidate属性表示,前端不需要验证,为了让后端进行验证并且把信息返回回来
<form action="" method="post" novalidate>
	...
</form>

2.from组件功能总结

1.生产input标签
2.对提交的数据可以进行校验
3.提交错误提示
#不用form组件也可以实现上述方法,但过于麻烦,代码冗余长

3.手动生成form表单

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <form action="" method="post" >
        {% csrf_token %}
        <div>
            <!--for="{{ form_obj.name.id_for_label }}表示for 指向 id_name-->
            <label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
            <!--form_obj.name.error.0  为name对象第一个的错误-->
            {{ form_obj.name }}{{ form_obj.name.error.0 }}
        </div>
        <div>
            <label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label>
            {{ form_obj.pwd }}{{ form_obj.pwd.errors.0 }}
        </div>
        <button>提交</button>
    </form>

</body>
</html>
{{ form_obj.name }} 生成input框
{{ form_obj.name.label }}  表示该字段中文提示
{{ form_obj.name.id_for_label }} 表示该字段input框的id
{{ form_obj.name.error}}  表示该字段所有的错误
{{ form_obj.name.error.0}}  表示该字段第一个错误 

4.Form常用字段与插件

  • ChoiceField

    • 单选框
    class RegForm(forms.Form):
        gender = forms.ChoiceField(choices=((1,"男"),(2,"女")))
    
    • html源码效果:

  • initial

    • 提供默认值
    class RegForm(forms.Form):
        name = forms.CharField(
            label="用户名",
            min_length=6,
            initial="刘大牛",
        )
    
    • 网页效果

  • error_messages

    • 错误提示信息设置

      class RegForm(forms.Form):
          name = forms.CharField(
              label="用户名",
              min_length=6,
              required= True,
              error_messages={
                  "required":"用户名是必须填写的",
                  "min_length":"用户名长度不能小于6位"
      
              },
          )
      
    • 网页效果

  • required

    • 默认值为True,表示当前字段的值必须填写,

      class RegForm(forms.Form):
          name = forms.CharField(
              label="用户名",
              required = True,
          )
      
    • 网页效果:未填写,点击提交

  • radioselect

    • 把select 框改变样式变成按钮选项框

          class RegForm(forms.Form):
              gender = forms.ChoiceField(
                  choices=((1,"男"),(2,"女")),
                  widget = forms.RadioSelect,
              )
      
    • 网页效果:

  • MultipleChoiceField

    • 多选选择框

      hobby = forms.MultipleChoiceField(
              choices=((1,"篮球"),(2,"足球"),(3,"双色球"))
          )
      
    • 网页效果:

      多选框关联数据库
      choices = models.Hobby.objects.values_list("id","name")
      

      存在问题:

      如果在数据库中增加记录,会发现页面没有显示增加的字段。
      原因分析:
      	数据库更新数据。页面不能时时更新,原因py文件从上到下执行,数据通过实例化拿取数据,但views.py文件只加载一次,不会重新定义类,还会用之前数据,新增页面不显示,想要让数据实时更新方法如下
      
      #方式1
      hobby = forms.MultipleChoiceField()
      class RegForm(forms.Form):
          hobby = forms.MultipleChoiceField()
      	def __init__(self,*args,**kwargs):
      		super(RegForm,self).__init__(*args,**kwargs)
      		#self.fields能拿到所有字段的对象,是个字典
      		self.fields["hobby"].choices = models.Hobby.objects.values_list("id","name")
      #方式2:
      models.py的Myhobby设置__str__
      hobby = forms.ModelMultipleChoiceField(
              queryset=models.Myhobby.objects.all()
          )
      
      
      
    
  • 常用字段

CharField
ChoiceField
MultipleChoiceField
  • Django 所有内置字段汇总
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,              最小值
 
FloatField(IntegerField)
    ...
 
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)      
    ...
 
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.ModelMultipleChoiceField
 
 
     
TypedChoiceField(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类型

5.校验

1.内置校验

required = True
min_length
max_length
form_obj.cleaned_data
--->清洗数据,数据校验完毕才会有这条数据。   

2.自定义校验器validators

  • 遵循规律:通过校验规则 不做任何操作,不通过校验 抛出异常

  • 示例1

from django import forms
from django.core.validators import RegexValidator#用于正则匹配
class RegForm(forms.Form):
  
    phone = forms.CharField(
        validators = [
            RegexValidator(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$',"手机格式不正确"),
        ]
    )
  • 示例2
#views.py端
from django import forms
from django.core.exceptions import ValidationError
def testuserinfo(value):
    ret = "成功"
    if "xxx" == value:
        ret = "用户名存在"
    raise ValidationError(ret)
class RegForm(forms.Form):
    userinfo = forms.CharField(
        required = True,
        error_messages = {
            "required":"不能为空",
            "min_length":"小于8位"
        },
        min_length=8,
        validators=[testuserinfo,

                    ],
    )
    #required,min_length为内置校验
    #testuserinfo自定义校验器
    #自定义校验,如果不满足要求,就引发ValidationError错误
    
#html端
<div>
	<label for="{{ form_obj.userinfo.id_for_label }}">{{ form_obj.userinfo.label }}</label>
	{{ form_obj.userinfo }}
	{{ form_obj.userinfo.errors.0 }}
</div>

3.钩子函数

  • 通过钩子方法实现Form类中定义钩子函数,来实现自定义的验证功能
3.1局部钩子
  • 我们在form类中定义:clean_字段名()方法,就能够实现对特定字段进行校验。
    • 局部钩子,通过校验规则 必须返回当前字段值,不通过抛出异常
from django import forms
from django.core.exceptions import ValidationError
class RegForm(forms.Form):
	username = forms.CharField(
		label = "用户名",
		min_length = 6,
		initial = "Bug",
        widget=forms.widgets.TextInput(attrs={"class": "form-control"})
        
	)
    #定义局部钩子,用来验证username字段,之前的校验还在,只是提供一个添加一些校验功能钩子
    def clean_username(self):
        v = self.cleaned_data.get("username")
        if "xxxxxxxx" in v:
            raise ValidationError("不正确")
        else:
            return v
3.2全局钩子
  • 我们在Fom类中定义 clean() 方法,就能够实现对字段进行全局校验,字段全部验证完,局部钩子也全部执行完之后,执行这个全局钩子校验。
    • 通过校验规则,必须返回当前所有字段的值。不通过校验,抛出异常,调用全局出现__all__(全局的错误)
  • 验证2次密码是否输入一致
from django.core.exceptions import ValidationError
from django import forms
class RegForm(forms.Form):
	pwd = forms.CharField(label="密码",widget=forms.PasswordInput)
    ret_pwd = forms.CharField(label="密码",widget=forms.PasswordInput)
    def clean(self):
        pwd = self.cleaned_data.get("pwd")#获取第一次输入密码
        ret_pwd = self.cleaned_data.get("ret_pwd")
        if pwd == ret_pwd:
            return self.cleaned_data#如果一致则返回
        else:
            self.add_error("ret_pwd","两次输入不一致")
            raise ValidationError("两次输入不一致")

6.is_valid的流程:

1.执行full_clean()的方法:

定义错误字典

定义存放清洗数据的字典

2.执行_clean_fields方法:

	循环所有的字段

	获取当前的值

 	对进行校验 ( 内置校验规则   自定义校验器)

1. 通过校验

   self.cleaned_data[name] = value 

   如果有局部钩子,执行进行校验:

   通过校验——》 self.cleaned_data[name] = value 

   不通过校验——》     self._errors  添加当前字段的错误 并且 self.cleaned_data中当前字段的值删除掉

   2. 没有通过校验

   self._errors  添加当前字段的错误

3.执行全局钩子clean方法

7.is_valid()流程详细(源码解析):

当我们定义一个类
from django import forms
class RegForm(forms.Form):#继承Form
	pass
def form_login(request):
    form_obj = RegForm()#未传入参数,此时没有data,返回False
    if request.method == "POST":
        form_obj = RegForm(data=request.POST)#data传入参数,此时data不为None is_bound返回True

        # print(form_obj)
        if form_obj.is_valid():#返回值True或False
            print(form_obj.clean())
            return HttpResponse("注册成功")
    return render(request,"form_login.html",{"form_obj":form_obj})

posted @ 2019-11-13 14:31  是阿凯啊  阅读(158)  评论(0编辑  收藏  举报