Django进阶二

一. Session:https://www.cnblogs.com/liwenzhou/p/8343243.htm

1. 什么要有session?Cookied的缺点:
    1. 存储的最大数据量只有4096字节
    2. 数据都保存在客户端(浏览器)上,不安全
2. Session的简单流程(需要插入图片)
    保存在服务端的键值对
    1. 请求来了之后,还是生成随机字符串
    2. 以随机字符串为key,在服务端生成一个大字典,真正保存数据是value
    3. 把随机字符串以cookie的形式回复给浏览器
    4. 下一次请求再来的时候,会携带上一步的随机字符串
    5. 从请求中拿到随机字符串,
    6. 去后端以 该随机字符串为key找对应的value
    7. value里面存的就是真正有用的数据

3. Django中如何使用Session
    1. 无论设置Session还是获取Session都是针对request对象来操作

    2. 设置Session
        request.session["key"] = "value"
        request.session.setdefault('k1', 'v1') # 存在则不设置

    3. 获取session
        request.session.get("key") # 如果没有key则返回None 推荐使用
        request.session["key"]  # 如果没有key则会报错

    4. 删除session
        1. 将所有Session失效日期小于当前日期的数据删除
           request.session.clear_expired()
        2. 删除当前的会话数据并删除会话的Cookie
           request.session.flush()
        3. 删除session
           del request.session['k1']
        4. 删除当前会话的所有session
           request.session.delete()

    5. 其他常用命令
        1. 设置超时时间
           request.session.set_expiry(7)
                * 如果value是个整数,session会在些秒数后失效。
                * 如果value是个datatime或timedelta,session就会在这个时间后失效。
                * 如果value是0,用户关闭浏览器session就会失效。
                * 如果value是None,session会依赖全局session失效策略。
        2. 获取会话session的key
           request.session.session_key
        3. 检查会话session的key在数据库中是否存在
           request.session.exists("session_key")

    6. Django中的Session配置(settings)
        1. 数据库Session
        SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

        2. 缓存Session
        SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
        SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

        3. 文件Session
        SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
        SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()

        4. 缓存+数据库
        SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

        5. 加密Cookie Session
        SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

        其他公用设置项:
        SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
        SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
        SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
        SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
        SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
        SESSION_COOKIE_AGE = 60 * 60 * 24 * 2                    # Session的cookie失效日期(2周)(默认)
        SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
        SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)

二. 分页:https://www.cnblogs.com/liwenzhou/p/8343243.html最下面

1. 如何在单独的一个脚本文件中使用Django的一些变量或方法
    #!/usr/bin/env python3
    import os
    if __name__ == '__main__':
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_session.settings")
        import django
        django.setup()
        from app01 import models

        # 批量导入500条数据
        data = [models.User(username="user{}".format(i), password="123") for i in
                range(1, 501)]
        models.User.objects.bulk_create(data)

    注释:bulk_create()   Django ORM批量创建的一个方法
2. 分页:
    每页显示10条
    1             0->10
    2             10->20
    3             20->30
    n             (n-1)*10->n*10
#!/usr/bin/env python3
"""
分页功能
"""


class Paging(object):

    def __init__(self, current_page, total_count, url_prefix, per_page=10, max_show=11):
        """
        初始化一个自定义的分页实例
        :param current_page: 当前页码
        :param total_count: 总的数据量
        :param url_prefix: 分页中a标签的url前缀
        :param per_page: 每一个显示多少条数据
        :param max_show: 页面上最多显示多少个页码
        """
        self.total_count = total_count
        self.per_page = per_page
        self.max_show = max_show
        self.url_prefix = url_prefix

        # 最多显示页码数的一半
        half_show = max_show // 2
        # 因为URL取到的参数是字符串格式,需要转换成int类型
        try:
            current_page = int(current_page)
        except Exception as e:
            # 如果输入的页码不是正经页码,默认展示第一页
            current_page = 1
        # 求总共需要多少页显示
        total_page, more = divmod(total_count, per_page)
        if more:
            total_page += 1
        # 如果输入的当前页码数大于总数据的页码数,默认显示最后一页
        if current_page > total_page:
            current_page = total_page
        self.current_page = current_page

        # 计算一下显示页码的起点和终点
        show_page_start = current_page - half_show
        show_page_end = current_page + half_show
        # 特殊情况特殊处理
        # 1. 当前页码 - half_show <= 0
        if current_page - half_show <= 0:
            show_page_start = 1
            show_page_end = max_show
        # 2. 当前页码数 + hale_show >= total_page
        if current_page + half_show >= total_page:
            show_page_end = total_page
            show_page_start = total_page - max_show + 1
        # 3. 总共需要的页码数 < max_show
        if total_page < max_show:
            show_page_start = 1
            show_page_end = total_page

        self.show_page_start = show_page_start
        self.show_page_end = show_page_end
        self.total_page = total_page

    @property
    def start(self):
        """
        数据切片的起点
        :return:
        """
        return (self.current_page - 1) * self.per_page

    @property
    def end(self):
        """
        数据切片的终点
        :return:
        """
        return self.current_page * self.per_page

    @property
    def num(self):
        """
        分页起始序号
        :return:
        """
        return (self.current_page - 1) * self.max_show
    
    def page_html(self):
        """
        分页的html代码
        :return:
        """
        tmp = []
        page_html_start = '<nav aria-label="Page navigation" class="text-center"><ul class="pagination">'
        page_html_end = '</ul></nav>'
        tmp.append(page_html_start)
        # 添加一个首页
        tmp.append('<li><a href="/{}?page=1">首页</a></li>'.format(self.url_prefix))
        # 添加一个上一页
        # 当当前页是第一页的时候不能再点击上一页
        if self.current_page - 1 <= 0:
            tmp.append(
                '<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
        else:
            tmp.append(
                '<li><a href="/{}?page={}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(
                    self.url_prefix, self.current_page - 1))
        # for循环添加要展示的页码
        for i in range(self.show_page_start, self.show_page_end + 1):
            # 如果for循环的页码等于当前页码,给li标签加一个active的样式
            if self.current_page == i:
                tmp.append('<li class="active"><a href="/{1}?page={0}">{0}</a></li>'.format(i, self.url_prefix))
            else:
                tmp.append('<li><a href="/{1}?page={0}">{0}</a></li>'.format(i, self.url_prefix))
        # 添加一个下一页
        # 当前 当前页已经是最后一页,应该不让下一页按钮能点击
        if self.current_page + 1 > self.total_page:
            tmp.append(
                '<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>')
        else:
            tmp.append(
                '<li><a href="/{}?page={}" aria-label="Previous"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                    self.url_prefix, self.current_page + 1))
        # 添加一个尾页
        tmp.append('<li><a href="/{}?page={}">尾页</a></li>'.format(self.url_prefix, self.total_page))
        tmp.append(page_html_end)

        page_html = "".join(tmp)
        return page_html
后端代码-封装模块
from django.shortcuts import render
from app01 import models
from public.paging import Paging


# Create your views here.


def user_list(request):
    """
    显示用户列表
    :param request:
    :return:
    """
    if request.method == "GET":
        # 查找到所有的用户
        books = models.User.objects.all()
        # 拿到总数据量
        total_count = books.count()
        # 从url拿到page参数
        current_page = request.GET.get("page", None)
        page_obj = Paging(current_page, total_count, url_prefix="user_list", max_show=7)
        # 对总数据进行切片,拿到页面显示需要的数据
        data = books[page_obj.start:page_obj.end]
        page_html = page_obj.page_html()
        return render(request, "user_list.html", {"data": data, "num": page_obj.num, "page_html": page_html})
后端代码-使用模块
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户列表</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <table class="table table-bordered">
                <thead>
                <tr>
                    <th>#</th>
                    <th>用户名</th>
                </tr>
                </thead>
                <tbody>
                {% for user in data %}
                    <tr>
                        <td>{{ forloop.counter|add:num }}</td>
                        <td>{{ user.name }}</td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        {{ page_html|safe }}
        </div>
    </div>
</div>
</body>
</html>
前端代码-显示分页

三. 中间件:https://www.cnblogs.com/liwenzhou/p/8761803.html

1. 什么时候使用中间件?
    当需要在全局改变Django框架的输入输出时
2. 中间件不宜添加过多,功能过于复杂
    否则会增加请求的响应时间
3. Django如何使用
    1. 五个方法(三个常用)
        主要记忆:执行时间、执行顺序、参数和返回值
        1. process_request(self,request)
            1. 执行时间
                在执行视图函数之前执行
            2. 执行顺序
                按照注册的顺序执行
            3. 参数和返回值
                1. request参数和视图函数中是同一个对象
                2. 返回值:
                    1. 返回None:请求继续往后执行
                    2. 返回响应对象:请求就结束了,要返回响应了

        2. process_response(self, request, response)
            1. 执行时间
                视图函数执行之后(拿到响应对象之后)
            2. 执行顺序
                按照注册的倒序执行
            3. 参数和返回值
                1. 参数:request请求对象
                         response响应对象
                2. 返回值:
                        只能返回响应对象
                        1. 返回默认的
                        2. 自己生成一个响应对象返回

        3. process_view(self, request, view_func, view_args, view_kwargs)
            1. 执行时间
                视图函数之前,在urls.py找到将要执行的视图函数之后
            2. 执行顺序
                注册的顺序执行
            3. 参数和返回值
                1. 参数:
                    1. request: 请求对象
                    2. view_func:将要执行的视图函数
                2. 返回值:
                    1. 返回None:继续往后执行
                    2. 返回响应对象,直接跳出,按照process_response方法的顺序执行

        有条件触发:
        4. process_template_response(self,request,response)
            1. 执行时间:
                视图函数之后,并且返回的响应对象是要有render方法
            2. 执行顺序:
                按照注册的倒序执行
            3. 返回值:
                对传递过来的响应对象,调用其render方法,把返回值向后继续传递
        5. process_exception(self, request, exception)
            1. 执行时间:
                当视图函数中抛出异常的时候才执行
            2. 执行顺序:
                注册的倒序
            3. 参数和返回值
                exception:视图函数中抛出的异常
                返回响应对象,就跳出按照process_response方法的顺序执行
代码示例:
------------------------------------------------------------------------------
        在项目目录下创建py文件
            from django.utils.deprecation import MiddlewareMixin
            class MD1(MiddlewareMixin):
                def process_request(self, request):
                    print("MD1里面的 process_request")

            class MD2(MiddlewareMixin):
                def process_request(self, request):
                    print("MD2里面的 process_request")
                    pass
-------------------------------------------------------------------------------
        在settings.py的MIDDLEWARE配置项中注册上述两个自定义中间件
            MIDDLEWARE = [
            'django.middleware.security.SecurityMiddleware',
            'django.contrib.sessions.middleware.SessionMiddleware',
            'django.middleware.common.CommonMiddleware',
            'django.middleware.csrf.CsrfViewMiddleware',
            'django.contrib.auth.middleware.AuthenticationMiddleware',
            'django.contrib.messages.middleware.MessageMiddleware',
            'django.middleware.clickjacking.XFrameOptionsMiddleware',
            'middlewares.MD1',  # 自定义中间件MD1
            'middlewares.MD2'  # 自定义中间件MD2
            ]
代码示例

四. ORM(多对多) https://www.cnblogs.com/liwenzhou/p/8660826.html

1. ORM多对多字段
    class Book(models.Model):
        """
        图书表
        """
        name = models.CharField(max_length=24)
        publisher = models.ForeignKey(to="Publisher")

    class Author(models.Model):
        """
        作者表
        多对多,自动帮我们在数据库建立第三张关系表
        """
        name = models.CharField(max_length=24)
        books = models.ManyToManyField(to="Book", related_name="authors")
    参数:
        - to:表示和哪张表建立多对多的关系
        - related_name:表示返乡查询时使用的那个字段名,默认反向查询时使用表名_set的方式
2. 多对多字段的方法
    1. 查询
        # 多对多  基于对象的查询
        # 正向查询
        print(models.Author.objects.first().books.all())
        # 反向查询
        print(models.Book.objects.first().authors.all())

        # 多对多 基于QuerySet 的查询
        # 正向查询
        print(models.Author.objects.all().values("books__name"))
        # 反向查询
        print(models.Book.objects.all().values("authors__name"))

    2. 删除
        1. 从关联对象集中移除执行的model对象
            models.Book.objects.first().authors.remove(3)
        2. 从关联对象集中移除一切对象。
            models.Book.objects.first().authors.clear()
    3. 添加
        1. 当form表单提交的数据是列表(多选的select\多选的checkbox)取值?
            request.POST.getlist("hobby")

        2. .set([id1,id2,...])  参数是一个列表  --> 删除原来的设置新的
            models.Author.objects.first().books.set([9])
            print(models.Author.objects.first().books.all())
        3. .add(id值)                           --> 在原来的基础上增加新的纪录
            models.Author.objects.first().books.add(9)
            print(models.Author.objects.first().books.all())

五. ajax:https://www.cnblogs.com/liwenzhou/p/8718861.html

1. stringify与parse方法
    JavaScript中关于JSON对象和字符串转换的两个方法:
    1. JSON.parse(): 用于将一个 JSON 字符串转换为 JavaScript 对象 
        JSON.parse('{"name":"Q1mi"}');
        JSON.parse('{name:"Q1mi"}') ;   // 错误
        JSON.parse('[18,undefined]') ;   // 错误
    2. JSON.stringify(): 用于将 JavaScript 值转换为 JSON 字符串。 
        JSON.stringify({"name":"Q1mi"})
2. 目前已知浏览器和服务端发请求的方式
    1. 浏览器地址栏 输入url直接回车   GET
    2. a标签                          GET
    3. form表单                       GET/POST
    4. ajax                           GET/POST
3. ajax的特点:
    优点:
        1. 偷偷发请求,用户无感知
        2. 局部刷新  相对于其他请求方式而言,返回的数据量小
        3. 同一个页面可以发送多次请求     异步
    缺点:
        1. 如果滥用,对服务端的压力比较大
4. ajax的使用:
    jQuery版:
        导入jQuery
        $.ajax({
            url: "往哪里发请求",
            type: "发送请求的类型",
            data: {
                "k1": "v1",
                "k2": "v2",
            },
            success:function(res){

            }
        })
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    <link rel="stylesheet" href="/static/plugins/bs/css/bootstrap.min.css">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-2">
            <form action="" method="post" class="form-signin">
                {% csrf_token %}
                <h2 class="form-signin-heading">请注册</h2>
                <div class="username">
                    <label for="username" class="sr-only">用户名</label>
                    <input type="text" id="username" name="username" class="form-control" placeholder="Username"
                           required
                           autofocus>
                </div>
                <label for="password" class="sr-only">密码</label>
                <input type="password" id="password" name="password" class="form-control" placeholder="Password"
                       required>
                <button class="btn btn-lg btn-primary btn-block" type="submit">注册</button>
            </form>
        </div>
    </div>
</div> <!-- /container -->
<script src="/static/plugins/jquery-3.3.1.min.js"></script>
<script>
    $(function () {
        $("#username").blur(function () {
            $.ajax({
                url: "/check/",
                type: "post",
                data: {
                    "username": $("#username").val(),
                    "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()
                },
                success: function (res) {
                    if (res == 0) {
                        $(".username").addClass("has-error")
                    }
                    else{
                        $(".username").addClass("has-success").removeClass("has-error")
                    }
                }
            })
        })
    })
</script>
</body>
</html>
ajax-前端代码
from django.shortcuts import render, HttpResponse
from app01 import models


# Create your views here.
def reg(request):
    if request.method == "GET":
        return render(request, "reg.html")
    
    
def check(request):
    username = request.POST.get("username")
    obj = models.User.objects.filter(username=username)
    if not obj.exists():
        return HttpResponse("1")
    else:
        return HttpResponse("0")
    
ajax-后端代码
5. AJAX请求如何设置csrf_token
   $.ajax({
      url: "/cookie_ajax/",
      type: "POST",
      data: {
        "username": "Q1mi",
        "password": 123456,
        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
      },
      success: function (data) {
        console.log(data);
      }
    })

 



posted @ 2018-08-21 14:33  S.Curry  阅读(260)  评论(0编辑  收藏  举报