Django_分页/From表单
分页
1 初试分页
def index(req): #自己定义的实现分页的方法一 per_page_count=10 //设置每页显示的数目 current_page=1 //默认是第一页 if req.GET.get('p',None): current_page=req.GET.get('p',None) current_page=int(current_page) //得到的当前页 star=(current_page-1)*per_page_count end=current_page*per_page_count//用来做切片求star和end data=USER_LIST[star:end]//切片 up_page=current_page-1 //上一页 if current_page<=1: up_page=1 //如果当前页小于等于1,设置为1 next_page=current_page+1//下一页 return render(req,"index.html",{"user_list":data,'up_page':up_page, 'next_page':next_page})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for user in user_list %} <li>{{ user.name }} ——{{ user.age }} </li> {% endfor %} <a href="/index/?p={{ up_page }}">上一页</a><a href="/index/?p={{ next_page }}">下一页</a> </ul> </body> </html>
2 django内置分页
from django.shortcuts import render from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger L = [] for i in range(999): L.append(i) def index(request): current_page = request.GET.get('p') paginator = Paginator(L, 10) # per_page: 每页显示条目数量 # count: 数据总个数 # num_pages:总页数 # page_range:总页数的索引范围,如: (1,10),(1,200) # page: page对象 try: posts = paginator.page(current_page) # has_next 是否有下一页 # next_page_number 下一页页码 # has_previous 是否有上一页 # previous_page_number 上一页页码 # object_list 分页之后的数据列表 # number 当前页 # paginator paginator对象 except PageNotAnInteger: posts = paginator.page(1) except EmptyPage: posts = paginator.page(paginator.num_pages) return render(request, 'index.html', {'posts': posts}) views.py
USER_LIST=[] for i in range(1,500): temp={"name":"root"+str(i), 'age':i } USER_LIST.append(temp) class CustomPaginator(Paginator): def __init__(self, current_page, per_pager_num, *args, **kwargs): try: self.current_page=int(current_page) self.per_page_num=int(per_pager_num) except TypeError as e: self.current_page=1 self.per_page_num = int(per_pager_num) super(CustomPaginator, self).__init__(*args, **kwargs) //继承父类属性方法 def page_num_range(self)://自定义返回分页的方式 if self.num_pages<self.per_page_num: return range(1,self.num_pages+1) //如果总页数小于当前需要显示的页数,返回1,到总页数的页码 part=int(self.per_page_num/2) //获取当前显示总页数的二分之一,目的为了左移 if self.current_page<=part: return range(1,self.per_page_num+1) //如果当前点击的页码小于part(1-6),那么返回1到当前页的页码,+1是因为切片不顾尾 if self.current_page+part>self.num_pages: return range(self.num_pages-self.per_page_num,self.num_pages) //如果当前点击的页码+part大于最后一页,说明会多显示无用的页码,返回最后的页码减去要分页的数码, 到最后一页 return range(self.current_page-part,self.current_page+part+1) //返回当前页码-part, 到当前页码+part+1 def index1(request): current_page = request.GET.get('p') paginator=CustomPaginator(current_page,7,USER_LIST,10) #Paginnator对象 try: posts=paginator.page(current_page) except PageNotAnInteger: posts = paginator.page(1) except EmptyPage: posts = paginator.page(paginator.num_pages) return render(request, "index1.html",{'posts':posts})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for user in posts.object_list %} <li>{{ user.name }} ——{{ user.age }} </li> {% endfor %} </ul>//posts对象.object_list方法里面所有的数据 {# {% include 'include/include.html' %}#} {% if posts.has_previous %} <a href="/index1/?p={{ posts.previous_page_number }}">上一页</a> {% else %} <a href="#">上一页</a> {% endif %} //如果有上一页显示,否则等于# {% for i in posts.paginator.page_num_range %} {% if i == posts.number %} <a style="color: burlywood; font-size: 20px" href="/index1/?p={{ i }}">{{ i }}</a> {% else %} <a href="/index1/?p={{ i }}">{{ i }}</a> {% endif %} //显示自己扩展的page_num_range方法,return的是一个ruage,如果是当前页加属性 {% endfor %} {% if posts.has_next %} <a href="/index1/?p={{ posts.next_page_number }}">下一页</a> {% else %} <a href="#">下一页</a> {% endif %} <span> {{ posts.number }}/{{ posts.paginator.num_pages }} </span> //当前页/最后一页 </body> </html>
3、自定义分页
分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入计算出应该在数据库表中的起始位置。
1、设定每页显示数据条数
2、用户输入页码(第一页、第二页...)
3、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置
4、在数据表中根据起始位置取值,页面上输出数据
class Pagination(object): def __init__(self, totalCount, currentPage,perPageNum=10,maxPageNum=7): #//总数目, 当前页,每页显示的数目,每页显示的页码 self.totalCout=int(totalCount) try: v=int(currentPage) if v <=0: v=1 self.currentPage=int(v) #//如果当前页<0,设置为1 except Exception as e: self.currentPage=1 #//出错或为空也设置为1 self.perPageNum = perPageNum self.maxPageNum = maxPageNum print(self.totalCout,self.currentPage) def star(self): return (self.currentPage-1)*self.perPageNum #//设置开始切片页码,当前页-1 * 每页需要显示的数据 def end(self): return self.currentPage*self.perPageNum # //设置结束切片页码,当前页 * 每页需要显示的数据 @property def num_pages(self): a,b=divmod(self.totalCout,self.perPageNum) if b==0: return a else: return a+1 #//设置最大页数 def page_num_range(self): #//设置分页方式,见拓展 if self.num_pages<self.maxPageNum: return range(1,self.num_pages+1) part=int(self.maxPageNum/2) if self.currentPage<=part: return range(1,self.maxPageNum+1) if self.currentPage+part>self.num_pages: return range(self.num_pages-self.maxPageNum+1,self.num_pages+1) return range(self.currentPage-part,self.currentPage+part+1) def page_str(self): first="<li><a href=/index2/?p=1>首页</a></li>" list=[] list.append(first) #//设置首页 if self.currentPage==1: prev = "<li><a href=#>上一页</a</li>>" else: prev = "<li><a href=/index2/?p=%s>上一页</a></li>"%(self.currentPage-1) list.append(prev) #//设置上一页 for i in self.page_num_range(): if i ==self.currentPage: temp = "<li class='active'><a href=/index2/?p=%s>%s</a></li>" % (i, i) else: temp="<li><a href=/index2/?p=%s>%s</a></li>"%(i,i) list.append(temp) # 循环自身的分页方法, if self.currentPage==self.num_pages: prev2 = "<li><a href=#>下一页</a>" else: prev2 = "<li><a href=/index2/?p=%s>下一页</a></li>"%(self.currentPage+1) list.append(prev2) lase="<li><a href=/index2/?p=%s>末页</a></li>"%(self.num_pages) list.append(lase) # //设置尾页 return ''.join(list)
USER_LIST=[] for i in range(1,500): temp={"name":"root"+str(i), 'age':i } USER_LIST.append(temp) def index2(req): currentPage=req.GET.get("p") posts_obj=Pagination(500,currentPage,) //传入最大数,和当前页 data_list = USER_LIST[posts_obj.star():posts_obj.end()] //用于切片 return render(req,"index2.html",{'data_list':data_list,"posts_obj":posts_obj})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css"/> <style> </style> </head> <body> <ul> {% for user in data_list %} <li>{{ user.name }} ——{{ user.age }} </li> {% endfor %} </ul> <ul class="pagination"> <li><a href="#" aria-label="Previous"><span aria-hidden="true">«</span></a></li> {{ posts_obj.page_str|safe }} <li><a href="#">1</a></li> <li><a href="#" aria-label="Next"><span aria-hidden="true">»</span></a></li> </ul> </body> </html>
FROM
Django的Form主要具有一下几大功能
1 生成HTML标签 2 验证用户数据(显示错误信息) 3 HTML Form提交保留上次提交数据 4 初始化页面显示内容
FROM牛刀小试
1创建from类
from django.forms import Form //引入form from django import forms from django.forms import widgets// 定制生成的html插件 from django.forms import fields //引入fiedls class MyForm(forms.Form): //创建一个myform类,继承 forms.Form user = fields.CharField( widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'}) )//设置为普通input框, 设置属性id=i1 class=c1 gender = fields.ChoiceField( choices=((1, '男'), (2, '女'),), initial=2, widget=widgets.RadioSelect )//设置radio单选 #//gender2 = fields.CharField( initial=2, widget=widgets.RadioSelect(choices=((1, '男'), (2, '女'),),) )//设置radio单选,第二种方式 city = fields.CharField( initial=2, widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) )//单选select city = fields.ChoiceField( choices=[(1,'上海'),(2,'北京'),] initial=2, widget=widgets.Select() )//单选select第二种方式 pwd = fields.CharField( widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) )
2、View函数处理
from django.shortcuts import render, redirect from .forms import MyForm def index(request): if request.method == "GET": obj = MyForm() return render(request, 'index.html', {'form': obj}) //第一次提交结果肯定是get返回index.html,同时返回空对象,生成input界面 elif request.method == "POST": obj = MyForm(request.POST, request.FILES)//把from提交的结果和上传的文件验证myfrom if obj.is_valid(): values = obj.clean() print(values) //如果成功,打印一下值 else: errors = obj.errors print(errors) return render(request, 'index.html', {'form': obj}) //否则把obj返回, 前台调用obj.errors显示错误类型 else: return redirect('http://www.google.com')
3生成HTML
<form novalidate action="/" method="POST" enctype="multipart/form-data"> //novalidate阻止游览器,enctype="multipart/form-data上传文件必加 <p>{{ form.user }} {{ form.user.errors }}</p> <p>{{ form.gender }} {{ form.gender.errors }}</p> <p>{{ form.city }} {{ form.city.errors }}</p> <p>{{ form.pwd }} {{ form.pwd.errors }}</p> <input type="submit"/> </form> // 第一个变量生成input框, 第二个生成错误信息
Form类
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
1、Django内置字段如下
1.1 field方法
Field required=True, //是否允许为空 widget=None, //HTML插件 initial=None, //初始值 label=None, // 用于生成Label标签或显示内容 help_text='', //帮助信息(在标签旁边显示),在后面加 error_messages=None, //错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False //是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) validators=[], //自定义验证规则 localize=False, //是否支持本地化 disabled=False, //是否可以编辑,等于ture不可编辑状态 label_suffix=None/':' // Label内容后缀 和 input输入框之间,一般用冒号
1.2CharField(Field)
max_length=None, // 最大长度 min_length=None, //最小长度 strip=True //是否移除用户输入空白
1.3IntegerField(Field)
IntegerField(Field) max_value=None, // 最大值 min_value=None, //最小值 //多用于年龄验证
1.5ChoiceField(Field) //默认单选select,封装了choices方法
choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) required=True, 是否必填 widget=None, 插件,默认select插件 label=None, Label内容 initial=None, 初始值 help_text='', 帮助提示 city=fields.ChoiceField( choices=[(1,'济南'),(2,'青岛'),(3,'商河')], initial=2 #默认显示第二个,可以在函数里面设置类(【c】) //obj = TestFrom({'city':3)--函数里面设置 )
TypedChoiceField(ChoiceField)//对选中的值进行一次转化
city=fields.ChoiceField( # coerce=lambda x: int(x), #把提交过来的数据转化为int类型 choices=[(1,'济南'),(2,'青岛'),(3,'商河')], initial=2 #默认显示第二个,可以在函数里面设置类(【c】) )
1.6MultipleChoiceField(ChoiceField)//多选select,其余与1.5相同, 还有TypedMultipleChoiceField(MultipleChoiceField)
hobby=fields.MultipleChoiceField( choices=[(1,'LoL'),(2,'王者荣耀'),(3,'问道'),(4,'qq飞车')], initial=[1,2,3] )
1.7ComboField(Field) //使用多个验证
fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
其余内置字段
loatField(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类型 ...
常用选择插件
//# 单选slect select = fields.CharField( initial=2, widget=widgets.Select(choices=[(1, 'LoL'), (2, '王者荣耀'), (3, '问道'), (4, 'qq飞车')]) ) select2 = fields.ChoiceField( choices=[(1, '济南'), (2, '青岛'), (3, '商河')], initial=2, widget=widgets.Select() ) // 多选select hobby2 = fields.MultipleChoiceField( choices=[(1, 'LoL'), (2, '王者荣耀'), (3, '问道'), (4, 'qq飞车')], widget=widgets.SelectMultiple(attrs={'class':'c1'}) ) //# 多选checkbox,值为列表 xdb = fields.MultipleChoiceField( initial=[], choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxSelectMultiple ) //#单选checkbocx xdb2=fields.CharField( widget=widgets.CheckboxInput() ) //# 单radio,值为字符串 usera = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), initial=2, widget=widgets.RadioSelect ) // usera2 = fields.CharField( initial=2, widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) )
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
方式一 推荐用法:
from django.shortcuts import render,redirect from app01 import models # Create your views here. from django import forms from django.forms import fields from django.forms import widgets class LoveFrom(forms.Form): price=fields.IntegerField() user_id =fields.IntegerField( # widget=widgets.Select(choices=[(0,'alex'),(1,'佩奇'),(3,'小红')])//默认显示下面是数据库显示方法 # widget=widgets.Select(choices=models.UserInfo.objects.all().values_list('id','username')) #//因为下面写了init方法 所以每次都会执行下面的sql语句,这一句就多余了 ) def __init__(self, *args,**kwargs): super(LoveFrom,self).__init__() self.fields['user_id'].widget.choices=models.UserInfo.objects.all().values_list('id','username')
方式二 使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现,不推荐
class LoveFrom(forms.Form): price=fields.IntegerField() from django.forms.models import ModelChoiceField user_id2=ModelChoiceField(queryset=models.UserInfo.objects.all()) //这个方法在html显示都是字符,必须在类里面定义一个方法,
初始化数据
在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。
1 类部分
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField() city = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), widget=widgets.Select ) //也可以用默认值显示
2 Views
from django.shortcuts import render, redirect from .forms import MyForm def index(request): if request.method == "GET": values = {'user': 'root', 'city': 2} obj = MyForm(values) //obj = TestFrom({'city':3,'hobby':[1,2,3]}) return render(request, 'index.html', {'form': obj}) elif request.method == "POST": return redirect('http://www.google.com') else: return redirect('http://www.google.com')
3 html
<form method="POST" enctype="multipart/form-data"> {% csrf_token %} <p>{{ form.user }} {{ form.user.errors }}</p> <p>{{ form.city }} {{ form.city.errors }}</p> <input type="submit"/> </form>
自定义验证规则
方式一:
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], ) //定义两了个验证规则, 第一个是正则表达式,第二个是错误信息
方式二
RegexField(CharField) regex, 自定制正则表达式 max_length=None, 最大长度 min_length=None, 最小长度 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fieldsRegexField(r'^[0-9]+$', error_messages={'invalid': '...'} //错误信息 )
方式三Form扩展(钩子函数)
如果对username做扩展 先做正则表达式判断然 后自定义方法验证:也就是clean_xx, 称为钩子函数
class AjaxFrom(forms.Form): username=fields.CharField( required=True, max_length=10, min_length=2 ) user_id =fields.IntegerField( widget=widgets.Select(choices=[(0,'alex'),(1,'佩奇'),(3,'小红')]) # widget=widgets.Select(choices=models.UserInfo.objects.all().values_list('id','username')) ) //def clean_username(self): 定义clean_+字段名 // v=self.cleaned_data['username'] 先拿username // if UserInfo.objects.filter(username=v).count()://如果数量不是0 // raise ValidationError('用户名已存在') 抛出异常 // return v //如果是0返回
联合唯一
def clean_username(self): v = self.cleaned_data['username'] if models.UserInfo.objects.filter(username=v).count(): # 整体错了 # 自己详细错误信息 raise ValidationError('用户名已存在') return v def clean_user_id(self): return self.cleaned_data['user_id'] def clean(self): value_dict = self.cleaned_data v1 = value_dict.get('username') v2 = value_dict.get('user_id') if v1 == 'root' and v2==1: raise ValidationError('整体错误信息') return self.cleaned_data
验证码
1 准备好 check_code.py 和 Monaco.ttf
2
from utils.check_code import create_validate_code from io import BytesIO def check_code(request): """ 验证码 :param request: :return: """ stream = BytesIO() //内存里面打开一个文件 img,code = create_validate_code() //获取一个img对象,一个code字符串 img.save(stream,'Png') //生成图片 request.session['CheckCode'] = code //设置来访问游览器的session 里面的随机字符串为K 值为一个字典 把session['CheckCode'] = code return HttpResponse(stream.getvalue()) 返回
4
#!/usr/bin/env python # -*- coding:utf-8 -*- //创建base.py文件 建一个类 class BaseForm(object): def __init__(self, request, *args, **kwargs): self.request = request super(BaseForm, self).__init__(*args, **kwargs)
5
from .base import BaseForm class LoginFrom(BaseForm ,djan_froms.Form,): # //创建一个myform类,继承 forms.Form code = fields.CharField( error_messages={ 'required':'请输入验证码' } ) def clean_code(self): v = self.cleaned_data['code'].upper() a = self.request.session.get('CheckCode').upper() print(v,a) if v != a: raise ValidationError('验证码错误') //把类传进来, 函数里面需要传requst参数 //self.request.session.get('CheckCode').upper() 获取session里面设置的值 // 定义一个钩子函数,如果不相等抛出错误信息
6
<div class="row"> <div class="col-xs-7"> <input type="password" class="form-control" name="code" placeholder="请输入验证码"> </div> <div class="col-xs-5"> <img src="/creat_code.html" onclick="change_code(this)"> </div> <div> {{ obj.code.errors.0 }} </div> </div> <input type="submit" class="btn btn-default"> </form> </div> <script> function change_code(self) { self.src = self.src + "?" } </script> //定义好验证码, 绑定函数,执行, 点击自动切换验证码, 等于src再次发了一个请求

浙公网安备 33010602011771号