• Django-分页-form数据校验


    分页

    view层

    def fenye(request):
        all_data = models.AuthorDetail.objects.all()
        current_page = request.GET.get('page',1)
        count = all_data.count()
        page_obj = Pagination(current_page=current_page,all_count=count)
        data = all_data[page_obj.start:page_obj.end]
        return render(request,'fenye.html',locals())
    

    模板层

    {% for a in data %}
        <p>{{ a.addr }}</p>
    {% endfor %}
    {{ page_obj.page_html|safe }}
    

    自定义分页器

    class Pagination(object):
        def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
            """
            封装分页相关数据
            :param current_page: 当前页
            :param all_count:    数据库中的数据总条数
            :param per_page_num: 每页显示的数据条数
            :param pager_count:  最多显示的页码个数
    
            用法:
            queryset = model.objects.all()
            page_obj = Pagination(current_page,all_count)
            page_data = queryset[page_obj.start:page_obj.end]
            获取数据用page_data而不再使用原始的queryset
            获取前端分页样式用page_obj.page_html
            """
            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)
    

    form表单校验组件

    由于校验的安全性,前端可以没有校验,但是后端必须校验

    自定义组件

    from django import forms
    from app01 import models
    
    class myzx(forms.Form):
        username = forms.CharField(min_length=3, max_length=8, label='用户名',
                                   error_messages={
                                       'min_length': '用户名不能少于三位',
                                       'max_length': '用户名不能大于八位',
                                       'required': '用户名不能为空',
                                   }, widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
                                   )
        password = forms.CharField(min_length=3, max_length=8, label='密码',
                                   error_messages={
                                       'min_length': '密码不能少于三位',
                                       'max_length': '密码不能大于八位',
                                       'required': '密码不能为空',
                                   }, widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
                                   )
        confirm_password = forms.CharField(min_length=3, max_length=8, label='确认密码',
                                           error_messages={
                                               'min_length': '确认密码不能少于三位',
                                               'max_length': '确认密码不能大于八位',
                                               'required': '确认密码不能为空',
                                           }, widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
                                           )
        email = forms.EmailField(
            label='邮箱',
            error_messages={
                'required': '邮箱不能为空',
                'invalid': '邮箱格式不正确'
            }, widget=forms.widgets.EmailInput(attrs={"class": 'form-control'})
        )
    

    前端表单可以自己写,也可以使用组件提供的

    obj为自己定义的组件对象

    <form method="POST">
        <p>{{ obj.username }}</p>
        <p>{{ obj.confirm_password }}</p>
        <p>{{ obj.confirm_password }}</p>
        <p>{{ obj.email }}</p>
        <input type="submit" value="提交">
    </form>
    

    但是这样渲染,会默认加上前端的校验,但是加在前端是不安全的,所以不采用

    <form method="POST">
        <p><input type="text" name="username" class="form-control" maxlength="8" minlength="3" required="" id="id_username"></p>
        <p><input type="password" name="confirm_password" class="form-control" maxlength="8" minlength="3" required="" id="id_confirm_password"></p>
        <p><input type="password" name="confirm_password" class="form-control" maxlength="8" minlength="3" required="" id="id_confirm_password"></p>
        <p><input type="email" name="email" class="form-control" required="" id="id_email"></p>
        <input type="submit" value="提交">
    </form>
    

    要在form表单上加上novalidate,可以取消上面的功能

    <form method="POST" novalidate>
    

    校验上传的数据

    #验证数据,获取验证结果对象
    form_obj = myforms.MyRegForm(request.POST)
    
    #判断是否全部验证成功
    if form_obj.is_valid():
    	#用户提交的数据(字典形式的字段和字段值)
    	print(form_obj.cleaned_data)
    else:
    	#用户提交错误的数据(错误信息为带前端样式的错误字段和错误描述信息)
    	#是一个字典,上面的打印信息时重写了str的原因
    	print(form_obj.errors)
    

    半完成版小案例

    前端

    obj.errors.username.0,才能拿到不带前端标签,完整的错误描述

    <form method="POST" novalidate>
        <p>{{ obj.username.label }}{{ obj.username }}{{ obj.errors.username.0 }}</p>
        <p>{{ obj.password.label }}{{ obj.password }}{{ obj.errors.password.0 }}</p>
        <p>{{ obj.confirm_password.label }}{{ obj.confirm_password }}{{ obj.errors.confirm_password.0 }}</p>
        <p>{{ obj.email.label }}{{ obj.email }}{{ obj.errors.email.0 }}</p>
        <input type="submit" value="提交">
    </form>
    

    后端

    可以通过这个简便的实现数据提交,form的内容还在,因为obj记录下来数据了

    其实后端返回的都是obj对象,只是最开始的那个是没有数据的,所以主要起渲染界面用

    将数据传上去后,生成的那个对象是带数据的,所以会携带错误信息并渲染到前端,两个obj是不同的

    def text(request):
        if request.method == 'GET':
            obj = myforms.myzx()
            return render(request,'zx.html',locals())
        else:
            obj = myforms.myzx(request.POST)
            if obj.is_valid():
            	#打印验证成功的数据,即使数据有错也能打印,只打印验证通过的
                print("验证成功",obj.cleaned_data)
                return HttpResponse("数据校验成功")
            else:
                print("验证失败",obj.errors)
                print(type(obj.errors))
                return render(request,'zx.html',locals())
    

    forms数据添加

    在写forms约束的时候我们可以把字段名和models里面涉及的一样,这样的话,clean_data的数据字典就完全符合提交数据的要求了

    #这样的话,数据就不用一条一条的取出来了,可以一次性提交数据
    if obj.is_valid():
    	models.books.objects.create(**obj.cleaned_data)
    

    forms数据编辑

    编辑界面需要元数据的内容,那么就可以使用校验功能,然后把数据返回前台,而且数据是从数据库拿去的数据是不会有问题的,前端的样式和add差不多

    def edit_user(request,nid):
    	if request.method == 'GET':
    		#获取数据
    		data = models.UserInfo.objects.filter(pk=nid).first()
    		#传给forms,因为它可以自动生成html
    		obj = UserForm({'username':data.username,'email':data.email})
    		return render(request,'edit_user.html',locals())
    	#else为修改数据的请求
    	else:
    		obj = UserForm(request.POST)
    		if obj.is_valid():
    			models.UserInfo.objects.filter(pk=id).update(**obj.cleaned_data)
    			return redirect('/users/')
    		else:
    			return render(request,'edit_user.html')
    

    form字段大全

    https://www.cnblogs.com/xiaoyuanqujing/articles/11753466.html

    其中weight是生成前端代码的重要插件

    返回参数为数字的单选框

    zx = fields.ChoiceField(
    	choices = [(1,'钱'),(2,'权')]
    )
    

    动态获取元数据

    刷新界面会执行这些代码,相当于重新去数据库取值

        hobby = forms.IntegerField(
            label='爱好',
            widget=widgets.Select()
        )
        def __init__(self,*args,**kwargs):
            super(myzx,self).__init__(*args,**kwargs)
            print(models.Blog.objects.values_list('id','site_title'))
            #注意这句要写后面,否则会被super覆盖掉值
            self.fields['hobby'].widget.choices = models.Blog.objects.values_list('id','site_title')
    

        from django.forms.models import ModelChoiceField
        hobby2 = ModelChoiceField(
            label='爱好2',
            #是显示的数据部分,想要指定显示内容需要去重写models的str方法
            queryset=models.Blog.objects.all(),
            #只是显示value值,上传是使用的
            to_field_name='id'
        )
    

    from前端html生成简写-高级

    但是不推荐,自定制比较弱

    {{obj.as_p}}
    
    <ul>
    	{{obj.as_ul}}
    </ul>
    
    <table>
    	{{obj.as_table}}
    </table>
    

    前端字符串转标签

    from django.utils safestring import mark_safe
    txt = mark_safe(txt)
    

    钩子函数

    注意这些函数都要写在form类中

    局部钩子函数

    给部分字段加强校验

        def clean_username(self):
            username = self.cleaned_data.get('username')
            if '88' in username:
                self.add_error('username','名字不能包含88')
            return username
    

    全局钩子函数

    针对多个字段校验使用全局钩子函数

        def clean(self):
            password = self.cleaned_data.get('password')
            re_password = self.cleaned_data.get('re_password')
            if not password == re_password:
                self.add_error('re_password','密码不相等啊')
            return self.cleaned_data
    

    正则和其他表单数据提交

    #
        # phone = forms.CharField(label='手机号',validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')])
        #
        #
        #
        # """下面的是了解知识点 你只需要整理到你的博客中 到时候需要用 直接来拷贝即可"""
        #
        # gender = forms.ChoiceField(
        #     choices=((1, "男"), (2, "女"), (3, "保密")),
        #     label="性别",
        #     initial=3,
        #     widget=widgets.RadioSelect()
        # )
        #
        #
        #
        #
        # hobby = forms.ChoiceField(
        #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        #     label="爱好",
        #     initial=3,
        #     widget=widgets.Select()
        # )
        #
        # hobby1 = forms.MultipleChoiceField(
        #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        #     label="爱好",
        #     initial=[1, 3],
        #     widget=widgets.SelectMultiple()
        # )
        #
        # keep = forms.ChoiceField(
        #     label="是否记住密码",
        #     initial="checked",
        #     widget=forms.widgets.CheckboxInput()
        # )
        #
        # hobby2 = forms.MultipleChoiceField(
        #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        #     label="爱好",
        #     initial=[1, 3],
        #     widget=forms.widgets.CheckboxSelectMultiple()
        # )
    
    

  • 相关阅读:
    【转载】SG定理
    P3235 [HNOI2014]江南乐(Multi-Nim)
    斐波那契数列
    WC2021游记
    线性基性质证明和应用
    「NOI2018」屠龙勇士
    XiaoMi Interview Log
    打靶训练
    八皇后问题
    Docker的一些常用
  • 原文地址:https://www.cnblogs.com/zx125/p/11762296.html
Copyright © 2020-2023  润新知