day68——使用自定义分页器、Forms组件功能与应用

自定义分页器的拷贝与使用

自定义分页器

当我们需要用到非django内置的第三方功能或组件的时候,由于拷过来的东西不能瞎放,我们一般会创建一个名为utils的文件夹来存放。该文件夹可以在django根目录下创建;在分组开发每个应用需要不同组件的时候,也可以在每个应用下分别创建utils的文件夹,具体根据实际情况来定。

注意:

上述自定义的分页器是基于bootstrap样式来了,使用时页面需要导入bootstrap。

后端

代码很简单,将分页器代码拷贝到utils文件夹下需要使用时导入Pagination即可;给类传值生成对象,利用分区器对象的start和end属性对总数据进行切片获取当前页需要展示的数据,然后将数据传入html页面展示。

book_queryset = models.Book.objects.all()
current_page = request.GET.get('page',1)
all_count = book_queryset.count()
# 1 传值生成对象
page_obj = Pagination(current_page=current_page,all_count=all_count)
# 2 直接对总数据进行切片操作
page_queryset = book_queryset[page_obj.start:page_obj.end]
# 3 将page_queryset传递到页面 替换之前的book_queryset

前端

自定义分页器代码中已经封装好了bootstrap分页器的样式,分页器用模版语法直接.page_html使用即可,无需自己渲染分页器,需加上|safe进行转义,当然也可以在后端完成转义。

# 前端
{% for book_obj in page_queryset %}
    <p>{{ book_obj.title }}</p>
    <nav aria-label="Page navigation">
</nav>
{% endfor %}
{#利用自定义分页器直接显示分页器样式#}
{{ page_obj.page_html|safe }}
  

Forms组件

我们先写一个简单的注册功能来一步步引出Forms组件。

注册功能需求:

前端页面获取用户名和密码,利用form表单提交数据,后端接收到数据进行数据的校验,看是否满足用户名不能含有字符'666'以及密码不能小于三位的条件,如果不满足条件,则在前端页面展示错误信息。

后端:

def id_check(request):
    msg_dict = {'username': '', 'password': ''}  # 定义一个值是空字符串的字典
    if request.method == 'POST':
        username = request.POST.get('username')  # 获取用户的输入的信息
        password = request.POST.get('password')
        if '666' in username:  # 对数据进行校验,有错则给字典对应的字段添加错误信息
            msg_dict['username'] = '不能做一条只会喊6的咸鱼'
        if len(password) < 3:
            msg_dict['password'] = '密码还是长点安全'
    return render(request, 'id_check.html', locals())
    """无论是get请求还是post请求都能给页面返回错误信息字典,post请求来的时候字段可能有值。"""

前端:

<form action="" method="post">
    <p>用户名:
        <input type="text" name="username">
	   {#span便签的大小取决于文本,只有在字典有错误信息时才能取到值进行展示,否则span标签隐藏不展示#}
        <span style="color: red">{{ msg_dict.username }}</span></p>
    <p>密码:
        <input type="text" name="password">
        <span style="color: red">{{ msg_dict.password }}</span></p>
    <p><input type="submit"></p>
</form>

实现该注册功能我们的总共干了三件事:

  • 手动书写前端获取用户数据的html代码——>渲染html代码
  • 后端对用户数据进行校验——>校验数据
  • 对不符合要求的数据进行前端提示——>展示提示信息

渲染html、代码校验数据、展示提示信息Forms组件都可以实现,无需我们书写繁杂的代码。

后端校验的重要性

数据的校验可分为前端数据校验和后端的数据校验;前端的数据校验可有可无,因为可以在浏览器内进行修改,而后端校验才是必须要有了,保护服务端数据的安全。

Forms组件基本使用

需要先充django中导入forms模块,书写的时候跟写模型表类似。

from django import forms
class MyForm(forms.Form):
    # username字符串类型最小3位最大8位
    username = forms.CharField(min_length=3,max_length=8)
    # password字符串类型最小3位最大8位
    password = forms.CharField(min_length=3,max_length=8)
    # email字段必须符合邮箱格式  xxx@xx.com
    email = forms.EmailField()

校验数据:

之前我们学习了测试脚本的创建,但其实pycharm已经准备好一个测试环境python console,在charm页面的正下方打开就可以测试代码,在创建数据时,可以帮我们每个字段是否合法。

from app01 import views

# 1 将带校验的数据组织成字典的形式传入即可
form_obj = views.MyForm({'username':'jason',
                         'password':'123',
                         'email':'123'})

# 2 判断数据是否合法,注意该方法只有在所有的数据全部合法的情况下才会返回True
form_obj.is_valid()
# False

# 3 查看所有校验通过的数据
form_obj.cleaned_data
# {'username': 'jason', 'password': '123'}

# 4 查看所有不符合校验规则以及不符合的原因
form_obj.errors
"""{
  'email': ['Enter a valid email address.']
}"""

# 5 校验数据只校验类中出现的字段 多传不影响 多传的字段直接忽略
form_obj = views.MyForm({'username':'jason',
                         'password':'123',
                         'email':'123@qq.com',
                         'hobby':'study'})
form_obj.is_valid()
# True
res4.errors
# {}

# 6 校验数据 默认情况下 类里面所有的字段都必须传值
form_obj = views.MyForms({'username':'jason',
                          'password':'111'})
print(res4.is_valid())
# True
form_obj.errors
# {'email': ['This field is required.']}
"""
也就意味着校验数据的时候 默认情况下数据可以多传但是绝不可能少传
"""

渲染标签

form组件会自动渲染获取用户输入的标签(input radio checkbox select textarea ...)

但是它不会渲染提交标签,需要我们自己书写代码渲染。

后端:

def randering_label(request):
    form_obj = MyForms()  # 先生成空对象
	return render(request,'ren_label.html')  # 直接将空对象传给页面    

前端渲染:

  • 方式一:

    <body>
        {{ form_obj.as_p }}  <!--每个标签单独占一行-->
        {{ form_obj.as_ul }}  <!--每个标签单独占一行,竖向无序排列-->
        {{ form_obj.as_table }}  <!--标签横向按表格的形式排列-->
    </body>
    

    第一种方式书写的代码最少,最为方便,但是封装程度太高,不利于后期的扩展,一般只在本地测试使用。

  • 方式二:

    逐个标签进行渲染,扩展性强,但是需要书写的代码太多。如果标签过多逐个书写太麻烦,一般情况不使用这种渲染方式。

    <body>
    {{ form_obj.username.label }}:{{ form_obj.username }}
    {{ form_obj.password.label }};{{ form_obj.password }} 
    </body>
    
    

    label属性默认展示的是类中定义的字段首字母大写的形式,还可以自己修改,在对应字段对象指定label参数即可...max_length=8,label='用户名')

  • 方式三:

    利用循环的方式减少代码的书写,并且有一定的扩展性,推荐使用

    <body>
    {% for form in form_obj %}
    	<p>{{ form.label }}:{{ form }}</p>  <!--用p标签包裹将每个标签单行展示-->
    {% endfor %}
    </body>
    

展示提示信息

取消浏览器校验数据novalidate

我们给页面传一个空的form对象,后端不进行数据的校验时;在输入框中输入不合法的数据提交数据后,输入框向下弹出样式很好看的提示信息,这其实是浏览器帮我做的校验,但这种校验弱不禁风,浏览器内修改前端代码就会失效。

def ren_label(request):
    form_obj = MyForms()
    return render(request, 'ren_label.html', locals())

浏览器对该字段的校验代码:

<input type="text" name="username" maxlength="6" minlength="3" required="" id="id_username">

我们右键检查在浏览器内将该校验条件修改:

<input type="text" name="username" maxlength="6" minlength="2" required="" id="id_username">

再次提交用户名'66'校验就失效了

这也验证了前端校验的不可靠性和后端校验的重要性

可在前端form便签内设置浏览器不进行校验

<form action="" method="post" novalidate>

form组件展示提示信息

后端:

def check_data(request):
    # 1 先产生一个空的form对象
    form_obj = MyForm()
    # 2 定义一个空字字典,如果数据通过校验,给字典添加“注册成功”提示信息
    back_msg = {'success': ''}
    if request.method == 'POST':
        # 3 可将request.POST看是一个字典当参数传入
         form_obj = MyForm(request.POST)
        # 4.校验数据
        if form_obj.is_valid():
            back_msg['success'] = '注册成功'
    return render(request, 'ren_label.html', locals())  
"""无论是GET请求还是POST请求都能返回一个form对象给页面,
   POST请求来的时候对象可能不为空"""

注意:为了保证html页面不管什么情况下都能有一个form对象来渲染标签,get请求和post传给html页面对象变量名必须一样。

前端:

<body>
<form action="" method="post" novalidate>
    <h1 class="text-center">注册页面</h1>
    {% for form in form_obj %}
    <p>{{ form.label }}:{{ form }}<span style="color:red">{{ form.errors.0 }}</span></p>
    {% endfor %}
    <input type="submit" class="btn-success btn-lg">
    <span style="font-size: large;color:green" class="span">{{ back_msg.success }}</span>
</form>
<script>function clean_span() {
       $('.span') .remove()
    }
    setInterval(clean_span, 2000)
</script>
</body>

form.errors.0 :错误信息可能为多个所以是一个列表,在展示的时候会被渲染成无序列表的形式,按索引取值取消这种渲染。

数据校验不通过展示提示信息:

通过校验提示注册成功:

forms组件会保存你上次的数据,让你基于之前的结果进行修改,更加的人性化。

自定义错误信息:

class MyForm(forms.Form):
    # username字符串类型最小3位最大8位
    username = forms.CharField(min_length=3,max_length=8,label='用户名',
                               error_messages={
                                   'min_length':'用户名最少3位',
                                   'max_length':'用户名最大8位',
                                   'required':"用户名不能为空"
                               }
    # email字段必须符合邮箱格式  xxx@xx.com
    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 'invalid':'邮箱格式不正确',
                                 'required': "邮箱不能为空"
                             }
                             )                       

钩子函数(HOOK):

在特定的节点自动触发完成响应操作,钩子函数相当于forms组件的第二道数据校验关卡,能够支持我们自定义校验规则

forms组件两类钩子函数:

  • 局部钩子函数

    当你需要给单个字段增加校验规则的时候可以使用

    eg:对用户名进行校验,里面不能含有字符'6';单个字段username的校验用局部钩子函数

  • 全局钩子函数

    当你需要给多个字段增加校验规则的时候可以使用

    eg:核对密码和确认密码是否一致;两个字段校验数据用全局钩子函数

# 钩子函数在类里面书写方法即可
class MyForm(forms.Form):
	# 1 局部钩子函数
    def clean_username(self):
        # 获取到用户名
        username = self.cleaned_data.get('username')
        if '6' in username:
            self.add_error('username', '光喊6是不行滴!')
        # 将钩子函数钩去出来数据再放回去    
        return username
    
    
	# 2 全部钩子函数
    def clean(self):
        password = self.cleaned_data.get('password')
        conform_password = self.cleaned_data.get('conform_password')
        if conform_password != password:
            self.add_error('conform_password', '密码不一致')
        # 全局钩子函数将所有数据再放回去      
        return self.cleaned_data

对数据内容进行二次校验:

forms组件其他参数及补充知识点:

  • label(汉字/英文)——>字段名(英文则首字母大写)

  • error_messages({'字段校验条件':'错误信息',})——>自定义报错信息

  • required(True/False)——>控制字段是否必填

  • widget——>为标签修改样式

    widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'})  # 具有bootstrap样式的密文输入框
    
    widget=forms.widgets.Select() # 下拉选择框
    
  • 第一道关卡里面还支持正则校验:

    validators=[
                RegexValidator(r'^[0-9]+$', '请输入数字'),
                RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
            ]
    

其他类型渲染

	# radio
    gender = forms.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "保密")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )
    
    # select
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )
    
    # 多选
    hobby1 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )
    
    # 单选checkbox
    keep = forms.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )
    
    # 多选checkbox
    hobby2 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )

创建字段时forms跟有Choice表示该字段为可选类型,MultipleChoice表示多选,widgets后面点什么就是什么类型的输入标签,...select:表示单选,...SelectMultiple:表示多选;标签类型的单选和多选必须跟字段匹配。

原文地址:https://www.cnblogs.com/zhangtieshan/p/13054318.html