• Django之 Cookie,Session


    复习

    1,分页

    # 利用URL携带参数page,views.py中通过request.GET来获取page参数

    # utils: 放常用工具

     

    1. 在工具包utils中自定义mypage,并用其完成分页显示(推荐使用)

    book_list.html

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Book_list</title>

        <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">

    </head>

    <body>

    <h1>Book List</h1>

    <div class="container">

        <table class="table table-bordered">

            <thead>

            <tr>

                <th>#</th>

                <th>Title</th>

                <th>Publish Date</th>

            </tr>

            </thead>

            <tbody>

            {% for book in book_list %}

                <tr>

                    <td>{{ forloop.counter }}</td>

                    <td>{{ book.title }}</td>

                    <td>{{ book.publish_date|date:'Y-m-d' }}</td>  # 格式化输出日期

                </tr>

            {% endfor %}

            </tbody>

        </table>

    </div>

     

    <nav aria-label="Page navigation" class="text-center">

        <ul class="pagination">

            {{ page_html|safe }}  # 取消浏览器自动转译功能

        </ul>

    </nav>

    <script src="/static/jquery-3.3.1.min.js"></script>

    <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script>

    </body>

    </html>

     

    views.py

    from django.shortcuts import render

    from app01 import models

    from utils import mypage

     

    # 用工具包utils中的自定义mypage,完成分页显示

    def book_list(request):

        data = models.Book.objects.all()

        total_num = data.count()

        current_page = request.GET.get('page')

        page_obj=mypage.Page(total_num, current_page, 'book_list',per_page=20)

        book_list = data[page_obj.data_start:page_obj.data_end]

        page_html = page_obj.page_html()

        return render(request, "book_List.html", {'book_list':book_list, 'page_html':page_html})

     

    utils -> mypage.py. (小工具,可反复使用)

    class Page(object):

        """

        这是一个自定义分页类

        可以实现Django ORM数据的分页展示

     

        使用说明:

            from utils import mypage

            page_obj = mypage.Page(total_num, current_page, 'publisher_list')

            publisher_list = data[page_obj.data_start:page_obj.data_end]

            page_html = page_obj.page_html()

            为了显示效果,show_page_num最好使用奇数  #当前页面highlight,左右对称排列

        """

     

        def __init__(self, total_num, current_page, url_prefix, per_page=10, show_page_num=11):

            """

            :param total_num: 数据的总条数

            :param current_page: 当前访问的页码

            :param url_prefix: 分页代码里a标签的前缀

            :param per_page: 每一页显示多少条数据

            :param show_page_num: 页面上最多显示多少个页码

            """

            self.total_num = total_num

            self.url_prefix = url_prefix

            self.per_page = per_page

            self.show_page_num = show_page_num

     

            self.half_show_page_num = self.show_page_num // 2

     

            total_page, more = divmod(self.total_num, self.per_page)  # divmod()得到商和余数的小元组

            if more:

                total_page += 1

            self.total_page = total_page

     

            try:

                current_page = int(current_page)  # GET得到的current_page是字符串,必须要转成int才能进行后续运算操作

            except Exception as e:

                current_page = 1

            if current_page > self.total_page:

                current_page = self.total_page

            if current_page < 1:

                current_page = 1

            self.current_page = current_page

     

            if self.current_page - self.half_show_page_num <= 1:

                page_start = 1

                page_end = self.show_page_num

            elif self.current_page + self.half_show_page_num >= self.total_page:

                page_end = self.total_page

                page_start = self.total_page - self.show_page_num + 1

            else:

                page_start = self.current_page - self.half_show_page_num

                page_end = self.current_page + self.half_show_page_num

            self.page_start = page_start

            self.page_end = page_end

     

        @property  # 将方法装饰成数据属性

        def data_start(self):

            return (self.current_page-1)*self.per_page

     

        @property

        def data_end(self):

            return (self.current_page)*self.per_page

     

        def page_html(self):

            li_list = []

            li_list.append('<li><a href="/{}/?page=1">首页</a></li>'.format(self.url_prefix))

            if self.current_page <= 1:

                prev_html = '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'  # disabled,当在第一页时不能选前一页

            else:

                prev_html = '<li><a href="/{}/?page={}" aria_label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(

                    self.url_prefix,self.current_page - 1)

            li_list.append(prev_html)

            for i in range(self.page_start, self.page_end + 1):

                if i == self.current_page:

                    tmp = '<li class="active"><a href="/{0}/?page={1}">{1}</a></li>'.format(self.url_prefix,i) # active,当前页highlight

                else:

                    tmp = '<li><a href="/{0}/?page={1}">{1}</a></li>'.format(self.url_prefix,i)

                li_list.append(tmp)

            if self.current_page >= self.total_page:

                next_html = '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>'

            else:

                next_html = '<li><a href="/{}/?page={}" aria_label="Previous"><span aria-hidden="true">&raquo;</span></a></li>'.format(

                    self.url_prefix, self.current_page + 1)

            li_list.append(next_html)

            li_list.append('<li><a href="/{}/?page={}">尾页</a></li>'.format(self.url_prefix,self.total_page))

     

            page_html = "".join(li_list)  # 连成一个大的字符串,传至前段,方便后续操作

            return page_html

     

    1. 用Django中的分页,完成显示

    views.py

    # 用django中的工具,完成分页显示;功能单薄,一般不用

    from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger

    def publisher_list(request):

        data = models.Publisher.objects.all()

        current_page = request.GET.get('page')

        page_obj = Paginator(data,10)

        try:

            publisher_list = page_obj.page(current_page)

            # has_next              是否有下一页

            # next_page_number      下一页页码

            # has_previous          是否有上一页

            # previous_page_number  上一页页码

            # object_list           分页之后的数据列表

            # number                当前页

            # paginator             paginator对象

        except PageNotAnInteger:

            publisher_list= page_obj.page(1)

        except EmptyPage:

            publisher_list = page_obj.page(page_obj.num_pages)

    return render(request,'publisher_list.html',{'publisher_list':publisher_list})

     

    publisher_list.html

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Publisher_list</title>

        <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">

    </head>

    <body>

    <h1>Publisher List</h1>

    <div class="container">

        <table class="table table-bordered">

            <thead>

            <tr>

                <th>#</th>

                <th>Publish Name</th>

            </tr>

            </thead>

            <tbody>

            {% for publisher in publisher_list %}

                <tr>

                    <td>{{ forloop.counter }}</td>

                    <td>{{ publisher.name }}</td>

                </tr>

            {% endfor %}

            </tbody>

        </table>

     

        <nav aria-label="Page navigation">

            <ul class="pagination">

                {% if publisher_list.has_previous %}

                    <li>

                        <a href="/publisher_list/?page={{ publisher_list.previous_page_number }}" aria-label="Previous">

                            <span aria-hidden="true">&laquo;</span>

                        </a>

                    </li>

                {% else %}

                    <li class="disabled">

                        <a aria-label="Previous">

                            <span aria-hidden="true">&laquo;</span>

                        </a>

                    </li>

                {% endif %}

                <li class="active"><a href="#">{{ publisher_list.number }}</a></li>

                {% if publisher_list.has_next %}

                    <li>

                        <a href="/publisher_list/?page={{ publisher_list.next_page_number }}" aria-label="Next">

                            <span aria-hidden="true">&raquo;</span>

                        </a>

                    </li>

                {% else %}

                    <li class="disabled">

                        <a aria-label="Next">

                            <span aria-hidden="true">&raquo;</span>

                        </a>

                    </li>

                {% endif %}

            </ul>

        </nav>

    </div>

    <script src="/static/jquery-3.3.1.min.js"></script>

    <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script>

    </body>

     

    1. Cookie

    HTTP无状态 -> Cookie

    Cookie:服务端在返回响应的时候设置的,保存在浏览器上的键值对。

     

    复习: 

    print(request.path_info)  # 获得路径

    print(request.get_full_path())  # 获得路径+参数

     

     

    login.html

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Title</title>

    </head>

    <body>

    <form action="{{ request.get_full_path }}" method="post">

        {% csrf_token %}

        <p>Username: <input type="text" name = "username"></p>

        <p>Password: <input type="password" name="pwd"></p>

        <p><input type="submit" value="Login"></p>

    </form>

    </body>

    </html>

     

    views.py

    from django.shortcuts import render

    from django.shortcuts import HttpResponse,render,redirect

    from app01 import models

    from utils import mypage

     

    # 定义一个检测是否登陆的装饰器

    def check_login(func):

        def wrapper(request,*args,**kwargs):

            login_flag = request.get_signed_cookie("login",default="",salt="shanghais1hao")  # 获取Cookie,加盐版的盐要一致

            # login_flag = request.COOKIES.get("login", "") 

    #获取Cookie,非加盐版

     

            # default: 默认值

            # salt: 加密盐

            # max_age: 后台控制过期时间

     

            if login_flag.upper() == 'OK':

                return func(request,*args,**kwargs)

            else:

                url = request.path_info  # 获取跳转源路径

                return redirect("/login/?next={}".format(url))  # 如果是从其他页面跳转来,记录下该页面,完成验证后跳转回去

        return wrapper

     

    # 用工具包utils中的自定义mypage,完成分页显示

    @check_login

    def book_list(request):

        data = models.Book.objects.all()

        total_num = data.count()

        current_page = request.GET.get('page')

        page_obj=mypage.Page(total_num, current_page, 'book_list',per_page=20)

        book_list = data[page_obj.data_start:page_obj.data_end]

        page_html = page_obj.page_html()

        return render(request, "book_List.html", {'book_list':book_list, 'page_html':page_html})

     

    def login(request):

        if request.method == 'POST':

            username = request.POST.get('username')

            pwd = request.POST.get('pwd')

            if username == 'alex' and pwd =='alex':

                url = request.GET.get('next')  # 如果是从其他页面跳转来,跳转至该页面

                if not url:

                    url = '/publisher_list/'  # 若非从其他页面跳转来的,跳转到默认页面

                rep = redirect(url)

                rep.set_signed_cookie("login","ok",salt="shanghais1hao") 

    # 给响应设置Cookie,加密盐版

                # rep.set_cookie("login", "ok") 

    # 给响应设置Cookie

     

                # key, 键

                # value = '', 值

                # max_age = None, 超时时间

                # expires = None, 超时时间(IE requires expires, so set it if hasn't been already.)

                # path = '/', Cookie生效的路径, / 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问

                # domain = None, Cookie生效的域名

                # secure = False, https传输

                # httponly = False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

     

                return rep

        return render(request,"login.html")

     

    def logout(request):

        rep = redirect('/login/')

        rep.delete_cookie('login')  # 删除用户浏览器上之前设置的cookie值

        return rep

     Session

     

    Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。依赖cookie

    # 用session前要现有一个数据库

    1. 浏览器请求来了之后,服务端给你分配一个序号(口令)(程序级别完成)

    2. 浏览器收到响应之后,把得到的口令保存在 cookie (浏览器自带)

    3. 浏览器携带着刚才得到的口令,再次发送请求 (浏览器自带)

    4. 服务端拿到口令,去后端根据口令找对应的数据(大字典)(程序级别完成)

     

    views.py

    def login(request):

        err_msg = ""

        if request.method == 'POST':

            username = request.POST.get('username')

            pwd = request.POST.get('pwd')

            is_exist = models.User.objects.filter(name=username,pwd=pwd)

            if is_exist:

                # 登陆成功

                # 1. 生成随机字符串(口令),给浏览器返回

                # 2. 在服务端开辟一块空间,用来保存对应的session数据(大字典)

                # 3. 在服务端开辟的空间中保存需要保存的键值对数据

                request.session['login'] = 'OK'

                request.session['user'] = username  # 可以插入不只一条数据

                request.session.set_expiry(60*60*24*14)  # 两周,python支持上述写法

                return redirect('/index/')

            else:

                err_msg = 'invalid username or password'

        return render(request,'login.html',{'err_msg':err_msg})

     

    def index(request):

        # 1. 判断请求中是否携带了我下发的随机字符串(口令)

        # 2. 拿着口令去后端找对应的session数据

        # 3. 根据固定的key去取固定的值

        login_flag = request.session.get('login')

        if login_flag == 'OK':

            return render(request,'index.html')

        else:

            return redirect('/login/')

     

    def logout(request):

        request.session.flush()  # 删除当前的会话数据并删除会话的Cookie

        return redirect('/login/')

     

    总结:

    # 获取、设置、删除Session中数据

    request.session['k1']

    request.session.get('k1',None)

    request.session['k1'] = 123

    request.session.setdefault('k1',123) # 存在则不设置

    del request.session['k1']

     

    # 所有 键、值、键值对

    request.session.keys()

    request.session.values()

    request.session.items()

    request.session.iterkeys()

    request.session.itervalues()

    request.session.iteritems()

     

    # 会话session的key

    request.session.session_key

     

    # 将所有Session失效日期小于当前日期的数据删除

    request.session.clear_expired()  # session数据库中的数据不会自动清除,需手动删除

     

    # 检查会话session的key在数据库中是否存在

    request.session.exists("session_key")

     

    # 删除当前会话的所有Session数据

    request.session.delete()

    # 删除当前的会话数据并删除会话的Cookie。

    request.session.flush()   # 推荐使用

    这用于确保前面的会话数据不可以再次被用户的浏览器访问

    例如,django.contrib.auth.logout() 函数中就会调用它。

     

    # 设置会话Session和Cookie的超时时间

    request.session.set_expiry(value)

    * 如果value是个整数,session会在些秒数后失效。

    * 如果value是个datatime或timedelta,session就会在这个时间后失效。

    * 如果value是0,用户关闭浏览器session就会失效。

    * 如果value是None,session会依赖全局session失效策略。

     

    1. CBV中加装饰器(Django内置的把函数装饰器转换成方法装饰器)

    方式一:加载CBV视图的get或post方法上

    views.py

    from django import views

    from django.utils.decorators import method_decorator

     

    class Home(views.View):

        @method_decorator(check_login)

        def get(self, request):

            return render(request, "home.html")

        def post(self):

            pass

     

    urls.py

    urlpatterns = [

        url(r'^home/',views.Home.as_view()),

    ]

     

    方式二:加载dispatch方法上

    # 因为CBV中首先执行的就是dispatch方法,所以这么写相当于给get和post方法都加上了登录校验。

    from django.utils.decorators import method_decorator

    class HomeView(View):

        @method_decorator(check_login)

        def dispatch(self, request, *args, **kwargs):

            return super(HomeView, self).dispatch(request, *args, **kwargs)

     

        def get(self, request):

            return render(request, "home.html")

     

        def post(self, request):

            print("Home View POST method...")

            return redirect("/index/")

     

    式三:直接加在视图类上,但method_decorator必须传 name 关键字参数

    from django.utils.decorators import method_decorator

     

    @method_decorator(check_login, name="get")

    @method_decorator(check_login, name="post")

    class HomeView(View):

        def dispatch(self, request, *args, **kwargs):

            return super(HomeView, self).dispatch(request, *args, **kwargs)

     

        def get(self, request):

            return render(request, "home.html")

     

        def post(self, request):

            print("Home View POST method...")

            return redirect("/index/")

     

    CSRF Token相关装饰器(csrf_exempt,csrf_protect)

    CSRF Token相关装饰器在CBV只能加到dispatch方法上

    csrf_protect:为当前函数强制设置防跨站请求伪造功能,即便settings没设置全局中间件。

    csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

     

    views.py

    from django.views.decorators.csrf import csrf_exempt, csrf_protect

    @csrf_exempt

    def login(request):

        err_msg = ""

        if request.method == 'POST':

            username = request.POST.get('username')

            pwd = request.POST.get('pwd')

            is_exist = models.User.objects.filter(name=username,pwd=pwd)

            if is_exist:

                # 登陆成功

                # 1. 生成随机字符串(口令),给浏览器返回

                # 2. 在服务端开辟一块空间,用来保存对应的session数据(大字典)

                # 3. 在服务端开辟的空间中保存需要保存的键值对数据

                request.session['login'] = 'OK'

                request.session['user'] = username  # 可以插入不只一条数据

                request.session.set_expiry(60*60*24*14)  # 两周,python支持上述写法

                return redirect('/index/')

            else:

                err_msg = 'invalid username or password'

    return render(request,'login.html',{'err_msg':err_msg})

     

    logins.html

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Title</title>

    </head>

    <body>

    <form action="{{ request.get_full_path }}" method="post">

    {#    {% csrf_token %}#}

        <p>Username: <input type="text" name = "username"></p>

        <p>Password: <input type="password" name="pwd"></p>

        <p><input type="submit" value="Login"></p>

        <p style="color:red">{{ err_msg }}</p>

    </form>

    </body>

    </html>

     其他

    有时间这片重新写  有点乱

    [链接]2018年不容错过的Django全栈项目YaDjangoBlog

    https://zhuanlan.zhihu.com/p/33903527

     

     https://www.cnblogs.com/liwenzhou/p/8343243.html

    1. 补充

     

     

  • 相关阅读:
    完成卸载vs2010后再安装
    图片集合,可用作商品列表
    无可奈何花落去
    Uncaught TypeError: Cannot read property 'msie' of undefined
    CodeGenerator.cs
    年月日控件
    SQL GETDATE()日期格式化函数
    股票操作要点
    Rust 错误处理, 包裹错误
    使用 Rust 实现并查集
  • 原文地址:https://www.cnblogs.com/maojiang/p/9229347.html
Copyright © 2020-2023  润新知