推导分页的原理
1.分页:当数据量过多的时候,一页的数据展示不完,我们分好多页来展示
2. 需要几个参数:(例如博客)
1.当前第几页(get请求获取)
2.总数据量有多少(从数据库中查询出来)
3.每页展示20条(自己规定的)
4.总数据量 / 每页展示的条数 = 总页数(divmode())
"""前期准备:django项目--创建表--测试环境,创建1000条数据--写分页"""
3.需求:每页展示10条数据
1.queryset支持切片
user_list = models.User.objects.all()[0:10] # 第一页数据
user_list = models.User.objects.all()[10:20] # 第二页数据
2.需求查看第10页的数据
1.要知道当前页是第几页
current_page = request.GET.get('page', 1) # 当接收不到当前页码的时候,就默认是第一页
2.参数关系:
start = (current_page-1)*per_page_num
end = current_page*per_page_num
4.需求:添加分页按钮
1.分页按钮---bootstrap
<li><a href="?page=1">1</a></li>
2.计算总共多少页
1.divmode(总数量,每页展示数量)--->(m,n)-->(商,余数)
user_queryset = models.User.objects.all()
user_count = user_queryset.count()
page_count,yushu = divmod(user_count,per_page_num)
if yushu:
page_count += 1
2.问题:后端循环(前端无法range)---解决:后端拼接标签,前段使用过滤器safe
page_html = ''
for i in range(1,page_count+1):
page_html += '<li><a href="?page=%s">%s</a></li>'%(i,i)
5.需求:分页器只展示11页数据,并添加高亮
1.后端循环11页的数据
page_html = ''
for i in range(current_page-5,current_page+6):
page_html += '<li><a href="?page=%s">%s</a></li>'%(i,i)
2.问题:点击第5页及以前的标签,出现负数的页码-->if判断
page_html = ''
if current_page<6:
current_page =6
for i in range(current_page-5,current_page+6):
page_html += '<li><a href="?page=%s">%s</a></li>'%(i,i)
2.添加高亮:active
page_html = ''
tag = current_page
if current_page<6:
current_page =6
for i in range(current_page-5,current_page+6):
if i == tag: # 如果i == current_page,造成分页器上的前5页都不能标记
page_html += '<li class="active"><a href="?page=%s" >%s</a></li>'%(i,i)
else:
page_html += '<li ><a href="?page=%s" >%s</a></li>' % (i, i)
需求:每页展示10条数据
def show_data(request):
# 当前页
current_page = request.GET.get('page',1)
current_page = int(current_page)
# 每页展示条数
per_page_num = 10
# 起始页,结尾页
start = (current_page-1) * per_page_num
end = current_page*per_page_num
user_list = models.User.objects.all()[start:end]
return render(request,'show_data.html',locals())
<body>
{% for user_obj in user_list %}
<div>{{ user_obj.name }}</div>
{% endfor %}
</body>
需求:添加分页按钮
def show_data(request):
# user_list = models.User.objects.all()
# queryset支持切片
# user_list = models.User.objects.all()[0:20]
# user_list = models.User.objects.all()[20:40]
# 当前页
current_page = request.GET.get('page',1)
current_page = int(current_page)
# 每页展示条数
per_page_num = 10
# 起始页,结尾页
start = (current_page-1) * per_page_num
end = current_page*per_page_num
user_list = models.User.objects.all()[start:end]
# 计算展示多少页
user_queryset = models.User.objects.all()
user_count = user_queryset.count()
page_count,yushu = divmod(user_count,per_page_num)
if yushu:
page_count += 1
# 解决前端无法实现的range循环的问题
page_html = ''
for i in range(1,page_count+1):
page_html += '<li><a href="?page=%s">%s</a></li>'%(i,i)
return render(request,'show_data.html',locals())
<body>
{% for user_obj in user_list %}
<div>{{ user_obj.name }}</div>
{% endfor %}
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{{ page_html|safe }}
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</body>
需求:分页器只展示11页数据,并添加高亮
def show_data(request):
# 当前页
current_page = request.GET.get('page',1)
current_page = int(current_page)
# 每页展示条数
per_page_num = 10
# 起始页,结尾页
start = (current_page-1) * per_page_num
end = current_page*per_page_num
user_list = models.User.objects.all()[start:end]
# 计算展示多少页
user_queryset = models.User.objects.all()
user_count = user_queryset.count()
page_count,yushu = divmod(user_count,per_page_num)
if yushu:
page_count += 1
# 解决前端无法实现的range循环的问题
# if判断当前页数,解决负数问题
# 添加高亮
page_html = ''
tag = current_page
if current_page<6:
current_page =6
for i in range(current_page-5,current_page+6):
if i == tag: # 如果i == current_page,造成分页器上的前5页都不能标记
page_html += '<li class="active"><a href="?page=%s" >%s</a></li>'%(i,i)
else:
page_html += '<li ><a href="?page=%s" >%s</a></li>' % (i, i)
return render(request,'show_data.html',locals())
分页类的使用
1.cv文档:https://www.yuque.com/liyangqit/lb35ya/cgc6sz#ipd26
2.以后在新建项目中,会经常用到非django的第三方工具,对于这种第三方工具,我们一般会在django中新建一个文件夹:utils包,以后很多封装的代码都使用用面向对象的写法
3.分析代码
4.使用
ps:一般吧每页展示多少条数据,一般写在配置文件中。
如何从配置文件中读取
方式一:from django.conf import settings (找两次)
方式二:from day58 import settings # 这种方式只能获取settings里面的配置(找一次)
分析代码
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, 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)
使用
current_page = request.GET.get('page',1)
all_count = models.User.objects.all().count()
from django.conf import settings
per_page_num = settings.PER_PAGE_NUM
# 实例化对象
page_obj = Pagination(current_page,all_count,per_page_num=per_page_num)
user_list = models.User.objects.all()[page_obj.start:page_obj.end]
page_html = page_obj.page_html()
return render(request,'show_data.html',locals())
cook和session的简单介绍
背景
1.早期,网站是不用保存用户信息的,那时候都是静态网站
eg:新闻网站 博客
2.随着技术发展,诞生了许多需要登录的网站,其可以识别用户是谁
eg:淘宝 京东 支付宝
出现的问题及解决
以登录为例
1.问题1:当用户第一次登录网站的时候,用户输入用户名和密码,如果不保存下来,那么用户就每次都需要登录,意味着每次都要输入用户名和密码
2.问题1解决措施:利用cookie,当用户第一次登录成功网站的时候,服务端告诉浏览器把用户名和密码都给用户的浏览器,然后,用户浏览器把用户名和密码保存在本地,之后,用户在每次访问网站的时候,会自动把之前保存的用户名和密码都传给服务端,服务端接收浏览器传过来的用户名和密码在进行验证.
3.问题2:针对以上方案有没有问题?
有问题,把用户名和密码保存在浏览器本地是不安全的
4.问题2的解决措施:如何优化一下上述不安全问题?
使用到了session,session是把数据保存在服务端的,生成一个随机字符串,在服务端做随机字符串和用户信息的对应关系
也就是把用户信息保存在数据表中了,它是如何保存的?
"""
随机字符串1:用户信息1
随机字符串2:用户信息2
随机字符串3:用户信息3
"""
ps:session把数据保存在数据表里了,但是,当数据量特备大的时候,查询效率肯定会降低
5.session做的事情:
1. 生成一个随机字符串
2. 把随机字符串和用户信息的对应关系保存在数据表中
3. 把随机字符串返回给浏览器,让浏览器把随机字符串保存下来
之后,用户在访问网站的时候,会把随机字符串一块提交过来,服务端拿着随机字符串去数据表中查询,如果查到了,说明登录了,如果查不到,就说明还没有登录
cookie与session
1.cookie:就是把数据保存在浏览器上的数据都可以称之为cookie;cookie一般保存的形式是:k:v键值对
2.cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。
3.session:把数据保存到服务端
4.关于cookie和session的面试题
1.区别:
1.cookie把数据保存在浏览器;session把数据保存在服务端
2.session保存的数据更加安全
3.cookie设置数据是有大小限制的(最大支持4096字节);session没有数据限制大小的
2.session是基于cookie工作的
3.如果浏览器把保存cookie的开关关掉(就是不能保存用户信息了),问:session一定不能用了,对不对?
不对,可以将随机字符串以参数的形式保存
eg:back_dic = {'cook':200,'msg':'',random_str:''}
5.token:就是一个算法
6.jwt:是三段式信息,是加密得到的
7.为什么要用cookie和session保存用户信息?
因为HTTP协议是无状态的,意思是不能保存用户信息的,但是,我们网站有需要保存用户信息,所以就诞生了一些新的技术,如:cookie,session,token,jwt
ps:在浏览器中查看cookie:浏览器中按F12,点network—cookies就能看到(Application-cookies)
cookie的操作
1.使用django操作cookie
1.django三板斧(HttpResponse,redirect,render)
2.在views里的函数返回值必须有:HttpResponse、redirect、render之一
如下:
obj = HttpResponse
return obj
obj = redirect
return obj
obj = render
return obj
含义是一样的
3.如果想操作cookie,必须使用obj对象
获取cookie
request.COOKIES['key'] or request.COOKIES.get('key')
1.获取
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
2.参数:
● default: 默认值
● salt: 加密盐
● max_age: 后台控制过期时间
3.前端也可以获取cookie和session
localstorage
sessionstorage
设置cookie
obj.set_cookie('key', 'value')
1.设置
rep = HttpResponse(...)
rep = render(request, ...)
rep.set_cookie(key,value)
rep.set_signed_cookie(key,value,salt='加密盐')
2.参数
● key, 键
● value=’’, 值
● max_age=None, 超时时间 cookie需要延续的时间(以秒为单位)如果参数是\ None`` ,这个cookie会延续到浏览器关闭为止(设置过期时间)
● expires=None, 超时时间(IE requires expires, so set it if hasn’t been already.)
● path=’/‘, Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。(生效的路径)
● domain=None, Cookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=”.example.com”所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取(生效的域名)
● secure=False, 浏览器将通过HTTPS来回传cookie
● httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
删除Cookie
rep.delete_cookie
def logout(request):
rep = redirect("/login/")
rep.delete_cookie("user") # 删除用户浏览器上之前设置的usercookie值
return rep
登录案例
普通登陆
def login(request):
# 1.获取数据
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 2.从数据库中比对数据
user_obj = models.UserInfo.objects.filter(username=username).first()
if not user_obj:
return HttpResponse('用户名不存在')
if user_obj.password == password:
print('登陆成功')
# 使用cookie保存用户信息
obj = redirect('/home/')
obj.set_cookie('username',username)
return obj
return render(request, 'login.html')
def home(request):
# 需求:登陆之后才能访问home页面
# 1.获取cookie
if request.COOKIES.get('username'):
return HttpResponse('这是home页面')
else:
return redirect('/login/')
登陆认证
def login(request):
# 1.获取数据
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 2.从数据库中比对数据
user_obj = models.UserInfo.objects.filter(username=username).first()
if not user_obj:
return HttpResponse('用户名不存在')
if user_obj.password == password:
print('登陆成功')
# 使用cookie保存用户信息
obj = redirect('/home/')
obj.set_cookie('username',username)
return obj
return render(request, 'login.html')
def login_auth(fun_name):
def inner(request,*args,**kwargs):
if request.COOKIES.get('username'):
res = fun_name(request,*args,**kwargs)
return res
else:
return redirect('/login/')
return inner
@login_auth # home = login_auth(home)
def home(request):
return HttpResponse('这是home页面')
@login_auth
def transfer(request):
return HttpResponse('转账页面')
@login_auth
def withdraw(request):
return HttpResponse('提现页面')
@login_auth
def add_shop(request):
return HttpResponse('购物页面')