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表单校验组件

因为校验的安全性,前端能够没有校验,可是后端必须校验html

自定义组件

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,能够取消上面的功能django

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

后端app

能够经过这个简便的实现数据提交,form的内容还在,由于obj记录下来数据了函数

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

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

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()
    # )
相关文章
相关标签/搜索