56、推导分页原理、使用分页类、cookie与session简介、django操作cookie、session、token原理、jwt签发tiken

49 阅读9分钟

推导分页的原理

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">&laquo;</span>
      </a>
    </li> 
    {{ page_html|safe }}
    <li>
      <a href="#" aria-label="Next">
        <span aria-hidden="true">&raquo;</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('购物页面')