django分页器、form组件
一、django的分页器
1、django的分页器简介
在页面显示分页的数据时,需要用到django的分页器组件
# django中的分页器组件,把分页常用的东西,封装到一个类中、
# 实例化得到一个对象,对象里有属性和方法
- Paginator对象:
from django.core.paginator import Paginator
user_list = models.User.objects.all()
# 实例化得到对象:
# 第一个参数:要分页的数据——user_list
# 第二个数据:每页条数
paginator = Paginator(user_list, 10)
# paginator对象的属性和方法:
- paginator.per_page :每页显示的条目数量
- paginator.count: 数据总数量
- paginator.num_pages:总页数
- paginator.page_range:总页数的索引范围,如:(1,11) 是一个生成器
page对象:page= paginator.page(2)
# page对象的属性和方法:
- page.has_next(): 是否有下一页
- page.next_page_number():下一页的页码
- page.has_previous():是否哟上一页
- page.previous_page_number():上一页的页码
- page.object_list: 当前页面的数据列表
- page.number: 当前页码
# 表模型中默认以id排序
class Mata:
ordering = ('id',) # 值必须是一个元组或者列表
2、模板示例:
- 视图层函数 :
from django.core.paginator import Paginator
def index(request):
page_num_int = int(request.GET.get('page', 1))
per_page_int = int(request.GET.get('size', 10)) # 从post请求中获取每页的数据条数,默认是10
book_list = models.Book.objects.all()
paginator = Paginator(book_list, per_page_int)
# page_range = paginator.page_range
# 解决页码过多的问题(最多只显示10页)
if paginator.num_pages > 11:
if page_num_int - 5 < 1:
page_range = range(1, 11)
elif page_num_int + 5 > paginator.num_pages:
page_range = range(paginator.num_pages - 9, paginator.num_pages + 1)
else:
page_range = range(page_num_int - 5, page_num_int + 5)
else:
page_range = paginator.page_range
page = paginator.page(page_num_int)
# 最好不要写locals(), 会影响效率,需要几个值就传几个值
return render(request, 'index.html', {'page_range': page_range, 'page': page, 'page_num_int': page_num_int})
- 模板 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<title>Title</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<table class="table">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>price</th>
</tr>
</thead>
<tbody>
{% for book in page.object_list %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
<td>{{ book.price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
--------------------------------------------
// 分页器
<div class="text-center">
<nav aria-label="Page navigation">
<ul class="pagination">
{% if page.has_previous %}
<li>
<a href="/?page={{ page.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{% for page_num in page_range %}
{% if page_num_int == page_num %}
<li class="active">
<a href="/?page={{ page_num }}" aria-label="Previous">
<span aria-hidden="true">{{ page_num }}</span>
</a>
</li>
{% else %}
<li>
<a href="/?page={{ page_num }}" aria-label="Previous">
<span aria-hidden="true">{{ page_num }}</span>
</a>
</li>
{% endif %}
{% endfor %}
{% if page.has_next %}
<li>
<a href="/?page={{ page.next_page_number }}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
----------------------------------------------------------
</div>
</div>
</div>
</body>
</html>
3、分页器的进阶使用
# 分页器最多显示前5页和后5页,总共11页;如果页数小于11,全部显示出来
# 逻辑分析
- 1. 如果总页码大于11
1.1 if 当前页码减5小于1,则生成1到12的列表(顾头不顾尾,共11个页码)
page_range=range(1,12)
1.2 elif 当前页码+5大于总页码数,则生成当前页码减10,到当前页码加1的列表(顾头不顾尾,共11个页码)
page_range=range(paginator.num_pages-10,paginator.num_pages+1)
1.3 else 生成当前页码-5,到当前页码+6的列表
page_range=range(current_page_num-5,current_page_num+6)
- 2 其它情况(总页码不大于11),生成的列表就是pageinator的page_range
page_range=paginator.page_range
二、form表单组件介绍
1、form表单组件的介绍
1、很多场景下,前端需要校验用户输入的字段长度、邮箱等是否合法,如:注册功能、登录功能等,如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。
2、前端应该有校验但不是必须的,但是后端校验是必须的,(使用传统方式,需要写很多if判断)
3、借助form表单组件,可以简单实现字段的校验
from django.forms import Form
# 总结:form组件的主要功能
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入的内容
2、forms校验字段功能
### 1、新建一个myforms.py文件,写一个类,类里写要校验的字段
from django import forms
class MyForm(forms.Form):
# 字段的required属性默认是True
name = forms.CharField(required=True, max_length=32, min_length=2, label='用户名')
email = forms.EmailField(required=False, label='邮箱')
age = forms.IntegerField(max_value=200, min_value=0, label='年龄')
views.py 视图函数
def register(request):
data = {'name': "cc", 'age': 208}
# 实例化得到form对象,把要校验的数据传入
form = myforms.MyForm(data)
if form.is_valid(): # is_valid()返回一个布尔值
print('校验通过')
print(form.cleaned_data) # 不一定是上面传入的数据,(只返回校验过的数据)
else:
print(form.cleaned_data)
print('校验失败')
print(form.cleaned_data)
print(form.errors) # __str__被重写了,返回errors.as_ul()
# print(form.errors.as_ul()) # 结果与上面的form_errors相同
print(form.errors.as_data())
print(form.errors.as_json())
print(form.errors.as_text())
return HttpResponse('ok')
输出结果:
- 注意点:
1、调用is_valid()后,才有form.errors和form.cleaned_data
2、form.cleaned_data不管成功与否,都会有值,(被校验过的值),不一定是传入的值
3、form.errors源码中有as_ul()、as_data()、as_text()、as_json()方法
3、forms渲染模板功能
- 视图函数:
def register(request):
if request.method == 'GET':
form = myforms.MyForm()
return render(request, 'register.html', {'form': form})
elif request.method == 'POST':
form = myforms.MyForm(request.POST)
if form.is_valid():
print('校验通过')
else:
print('校验失败,返回错误原因')
print(form.errors.as_data())
return HttpResponse('ok')
- 模板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>手动创建模板</h1>
<form action="" method="post">
<p>用户名:<input type="text" name="name"></p>
<p>邮箱:<input type="text" name="email"></p>
<p>年龄:<input type="text" name="age"></p>
<p><input type="submit" value="提交"></p>
</form>
<h1>半自动渲染模板1</h1>
<form action="" method="post">
<p>用户名:{{ form.name }}</p>
<p>邮箱:{{ form.email }}</p>
<p>年龄:{{ form.age }}</p>
<p><input type="submit" value="提交"></p>
</form>
<h1>半自动渲染模板2(常用)</h1>
<form action="" method="post">
<p>{{ form.name.label }}:{{ form.name }}</p>
<p>{{ form.email.label }}:{{ form.email }}</p>
<p>{{ form.age.label }}:{{ form.age }}</p>
<p><input type="submit" value="提交"></p>
</form>
<h1>半自动渲染模板3(常用)</h1>
<form action="" method="post">
{% for foo in form %}
<p>{{ foo.label }}:{{ foo }}</p>
{% endfor %}
<p><input type="submit" value="提交"></p>
</form>
<h1>全自动渲染模板(不经常使用,不好修改样式)</h1>
<form action="">
<table>
{{ form.as_table }} //{{ form.as_p }} //{{ form.as_ul }}
</table>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
三、form组件渲染错误信息
#1、form对象.errors (是个字典)
#2、name对象.errors
- 视图函数
from django.shortcuts import render,redirect,HttpResponse
from app01 import myforms
def register(request):
if request.method == 'GET':
form = myforms.MyForm()
return render(request, 'register.html', {'form': form})
elif request.method == 'POST':
form = myforms.MyForm(request.POST)
if form.is_valid():
print('校验通过')
return redirect('http://www.baidu.com')
else:
print('校验失败')
err = form.errors.get('__all__')
ptint(err)
return render(request, 'register.html', {'form': form, 'err': err})
- 模板register.html
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form action="" method="post" novalidate>
{% for foo in form %}
<div class="form-group">
<label for="">{{ foo.label }}</label>
{{ foo }}
<span class="text-danger pull-right">{{ foo.errors }}</span>
</div>
{% endfor %}
<div class="text-center">
<input type="submit" value="提交" class="btn btn-success">
<span class="text-warning">{{ err }}</span>
</div>
</form>
</div>
</div>
</div>
注意:
# 1、novalidate是针对{{ foo }}的,模板会自动提示输入不合法,在form表单上加上novalidate可以关闭此效果
# 2、foo.errors区别于form.errors,form.error是所有的错误信息,foo.errors是某一个字段的错误信息
四、forms组件参数配置
# 定制模板中的显示样式即配置类
widget = widgets.TextInput(attrs={'class':'form-control'})
# 错误信息中文显示
error_messages = {'min_length':'密码长度不足', 'required':'必填项'}
1、forms组件参数配置
from django import forms
from django.forms import widgets, ValidationError
class MyForm(forms.Form):
name = forms.CharField(required=True, max_length=16, min_length=2, label='用户名', widget=widgets.TextInput(attrs={'class': 'form-control'}), error_messages={'min-length': '名字长度不足'})
password = forms.CharField(required=True, max_length=32, min_length=6, label='密码', widget=widgets.PasswordInput(attrs={'class': 'form-control'}), error_messages={'min_length': '密码长度不足'})
re_password = forms.CharField(required=True, max_length=32, min_length=6, label='密码', widget=widgets.PasswordInput(attrs={'class': 'form-control'}), error_messages={'min_length': '密码长度不足'})
email = forms.EmailField(required=True, label='邮箱', widget=widgets.TextInput(attrs={'class': 'form-control'}), error_messages={'required': '必填项'})
age = forms.IntegerField(required=False, max_value=150, min_value=0, label='年龄', widget=widgets.TextInput(attrs={'class': 'form-control'}))
birth = forms.CharField(label='出生日期', widget=widgets.TextInput(attrs={'class': 'form-control'}))
text = forms.CharField(label='个人简介', widget=widgets.TextInput(attrs={'class': 'form-control'}))
2、局部钩子和全局钩子
局部钩子要写在全局钩子之前(依次执行)
# 局部钩子的使用:
- 1.在自定义的Form类中写 clean_字段名 (如:clean_name)
- 2.取出字段的真正值,name = self.clean_data.get.get('name')
- 3.判断自定义的规则,如果判断不符合规则,抛出ValidationError
- 4.如果通过了,return 字段名
# 局部钩子
def clean_name(self):
name = self.cleaned_data.get('name')
if name.startswith('sb'):
raise ValidationError('名字不能以sb开头')
else:
# 校验通过必须返回name,views函数中name就是这里返回的值
return name
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if password == re_password:
# 这里返回的非None值也会被传到前面的views函数中,如果返回None,默认会被改成self.cleaned_data
return self.cleaned_data
else:
raise ValidationError('两次密码不一致')
五、form组件校验源码
1 读的入口是:
form.is_valid()--->self.errors(BaseForm类)---》self.full_clean()(BaseForm类)--》
-self._clean_fields(局部数据校验)和self._clean_form(全局数据校验)
2 self._clean_fields(BaseForm类)
for name, field in self.fields.items():
try:
# 字段自己的校验(最大值,最小值,是不是邮箱格式)
value = field.clean(value)
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name): # 反射判断有没有clean_字段名
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e)
3 self._clean_form(BaseForm类) 全局钩子
try:
cleaned_data = self.clean() # self.clean执行的是自己类的clean方法
except ValidationError as e:
self.add_error(None, e)
钩子(hook)是面向切面编程的一种思想