day68 form组件

一、自定义分页器的拷贝和使用

在django中一些第三方的组件我们可以单独建一个文件夹utils去存放,分页器就是这样的组件

utils>fenyeqi.py

class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=15, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)

分页器代码不需要完全会写,只要知道这个类如何使用以及几个关键性参数就行了

app01>views.py

from app01 import models
# 导入分页器模块
from utils import fenyeqi
def fenye(request):
    # 获取需要分页的所有数据对象
    all_queryset =  models.Author.objects.all()
    # 1 统计数据对象个数
    all_count =all_queryset.count()
    # 2 实例化分页器对象
    # 3 同时获取前端传来的get请求中page页数
    # 4 这里还有一个参数per_page_num,虽然是默认参数,但是我们通常要自己制定每页展示的数据,要知道是实例化的时候这个参数的修改
    fenye_obj = fenyeqi.Pagination(current_page=request.GET.get('page',1) ,all_count=all_count)
    # 5 获取每页展示的数据对象
    page_queryset = all_queryset[fenye_obj.start:fenye_obj.end]
    return render(request,'fenyeqi.html',locals())

template>fenyeqi.html

<body>
# 通过for循环展示要显示的内容
{% for obj in page_queryset %}
    <p>{{ obj.name }}</p>
{% endfor %}

# 展示分页器,记得加safe
{{ fenye_obj.page_html |safe}}
</body>

二、Forms组件

1 前戏

用我们现在所学的知识完成,判断用户名密码是否符合规范:

用户名不能含有金梅

密码不能少于三位

后端

def ab_form(request):
    back_dic = {'username':'','password':''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if '金梅' in username:
            back_dic['username'] = '不符合社会主义核心价值观'
        if len(password) < 3:
            back_dic['password'] = '不能太短 不好!'
     return render(request,'ab_form.html',locals())
            
# get请求来的时候,返回的是一个没有报错信息的字典,post请求来的时候对数据进行判断返回的是一个可能携带报错信息的字典

前端

// 通过一个span标签展示报错信息
<form action="" method="post">
    <p>username:
        <input type="text" name="username">
        <span style="color: red">{{ back_dic.username }}</span>
    </p>
    <p>password:
        <input type="text" name="password">
        <span style="color: red">{{ back_dic.password }}</span>
    </p>
    <input type="submit" class="btn btn-info">
</form>

2 form组件的基本功能

  • 渲染html代码
  • 效验数据
  • 展示提示信息

提示:前端的数据校验可有可无,后端必须要有数据校验

因为在爬虫程序可以绕过前端直接向服务端发起请求

3 基本使用

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()

4 基本方法

# 在pycharm中又一个自带的测试环境:python console

# 1 实例化form对象,这里的数据传多了不报错,但是不能少了类中有的,因为默认每个字段都是非空的
form_obj = views.MyForm({'username':'hz','password':'1111','email':'1@qq.com'})

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

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

# 4 查看所有不符合校验规则及其原因
form_obj.errors
{'usrename': ['Ensure this value has at least 3 characters (it has 2).']}

5 渲染标签

forms组件只会自动帮你渲染获取用户输入的标签(input select radio checkbox),不会帮我们渲染提交按钮

后端

def index(request):
    # 1 先产生一个空对象
    form_obj = MyForm()
    # 2 直接将该空对象传递给html页面
    return render(request,'index.html',locals())

前端

# 前端利用空对象做操作
    <p>第一种渲染方式:代码书写极少,封装程度太高 不便于后续的扩展 一般情况下只在本地测试使用</p>
    {{ form_obj.as_p }}
    {{ form_obj.as_ul }}
    {{ form_obj.as_table }}
    <p>第二种渲染方式:可扩展性很强 但是需要书写的代码太多  一般情况下不用</p>
    <p>{{ form_obj.username.label }}:{{ form_obj.username }}</p>
    <p>{{ form_obj.password.label }}:{{ form_obj.password }}</p>
    <p>{{ form_obj.email.label }}:{{ form_obj.email }}</p>
    <p>第三种渲染方式(推荐使用):代码书写简单 并且扩展性也高</p>
    {% for form in form_obj %}
        <p>{{ form.label }}:{{ form }}</p>
    {% endfor %}
    
"""
label属性默认展示的是类中定义的字段首字母大写的形式
也可以自己修改 直接给字段对象加label属性即可
	 username = forms.CharField(min_length=3,max_length=8,label='用户名')
"""

6 展示提示信息

'''
在我们在前端写需要校验的一些标签的时候,浏览器会自动帮我们以我们的要求校验,且会提示中文的报错信息
虽然这样的提示很智能,但是封装程度很高,不好修改样式
而且浏览器的校验非常的容易绕过可有可无,必须要在后端进行数据校验
如何让浏览器不做校验
	<form action="" method="post" novalidate>
'''

后端

'''
1.必备的条件 get请求和post传给html页面对象变量名必须一样
2.forms组件当你的数据不合法的情况下 会保存你上次的数据 让你基于之前的结果进行修改
更加的人性化
'''
def index(request):
    # 1 先产生一个空对象
    form_obj = MyForm()
    if request.method == 'POST':
        # 获取用户数据并且校验
        """
        1.数据获取繁琐
        2.校验数据需要构造成字典的格式传入才行
        ps:但是request.POST可以看成就是一个字典
        """
        # 3.校验数据
        form_obj = MyForm(request.POST)
        # 4.判断数据是否合法
        if form_obj.is_valid():
            # 5.如果合法 操作数据库存储数据
            return HttpResponse('OK')
        # 5.不合法 有错误
    # 2 直接将该空对象传递给html页面
    return render(request,'index.html',locals())

前端

{% for form in form_obj %}
        <p>
            {{ form.label }}:{{ form }}
            <span style="color: red">{{ form.errors.0 }}</span>
        </p>
{% endfor %}

我们也可以自己定制错误信息,针对的是我们限定的条件和一些自带的条件

# 针对错误的提示信息还可以自己自定制
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':"用户名不能为空"
                               }
                               )
    # password字符串类型最小3位最大8位
    password = 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': "邮箱不能为空"
                             }
                             )

7 钩子函数(HOOK)

'''
钩子函数会在特定情况触发,无需调用
钩子函数想到与forms组件的第二道关卡,在最基本的检测通过后,就会经历钩子函数的检测,是我们自定义的检测规则

在forms组件中有两种钩子
	1 局部钩子:对单个字段增加校验规则的时候使用
	2 全局钩子:对多个字段增加校验规则的时候使用

'''

# 局部钩子
def clean_username(self):
    # 获取到用户名
    username = self.cleaned_data.get('username')
    if '666' in username:
        # 提示前端展示错误信息
        self.add_error('username','光喊666是不行滴~')
        # 将钩子函数钩去出来数据再放回去
        return username

# 全局钩子
def clean(self):
        password = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data.get('confirm_password')
        if not confirm_password == password:
            self.add_error('confirm_password','两次密码不一致')
        # 将钩子函数钩出来数据再放回去
        return self.cleaned_data

8 forms组件其他参数及补充

'''
label 字段名,标签前显示
error_messages  自定义报错信息,以字段存放对应每个报错信息
initial  默认值
required  是否可以为空
'''

# 字段的标签样式也可以修改,添加参数widget
# forms.widgets是固定写法,后面自定义
# 不同的样式用空格隔开
widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'})

其他类型的渲染

# 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()
    )
原文地址:https://www.cnblogs.com/hz2lxt/p/13047118.html