Django 框架

Django

HTTP协议:

Socket和Tcp协议关系:
  • socket本身并不是协议而是一个调用的接口,socket的出现使我们更方便的使用tcp协议,如socket的基本接口,listen send recv

HTTP协议概述:
  • HTTP存在应用层的超文本传输协议,协议规定了客户端和服务器之间的通信标准,采用的是请求和响应模型,客户端发送一个请求报文,服务端进行响应

  • 请求格式和响应格式:

HTTP工作原理(重要):

  • 第一步:客户端连接web服务端

    • 客户端连接服务端,会建立一个TCP套接字连接,(套接字就是socket,客户端通常是浏览器)

  • 第二步:发送http请求

    • 通过TCP套接字,客户端向web服务端发送一个文本的请求报文,一个请求报文由请求行,请求头部,和请求数据组成

  • 第三步:服务器接收请求并返回HTTP响应

    • 响应就是服务端分析发送过来的请求,通过解析请求定位到资源,服务器将资源写到tcp套接字中,返回给客户端

  • 第四步:释放tcp连接

    • 如果connection模式为close时,服务端主动关闭tcp连接,客户端被动关闭连接,释放tcp,若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求

  • 第五步:客户端浏览器解析HTML内容

    • 客户端浏览器首先解释状态行,查看请求的状态码是否成功,然后解析每一个响应头,客户端浏览器读取响应数据的HTML,在根据HTML语法在浏览器窗口中进行展示

浏览器地址栏输入URL,流程:
  1. 浏览器向DNS服务器请求解析该URL中的域名对应的IP地址

  2. 解析出IP地址,根据IP地址和默认端口,和服务器建立TCP连接

  3. 浏览器发送HTTP请求(包含url后面对应的文件路径),该请求报文由TCP第三次握手发送给服务器

  4. 服务器对浏览器做出相应,将对应的html返回给浏览器

  5. 释放TCP连接

  6. 浏览器将该html文本进行展示

HTTP 请求方法
  • HTTP/1.1协议中共定义了八种方法(也叫“动作”)来以不同方式操作指定的资源:

  • GET:

    • 向指定资源发出显示请求,使用GET方法只用在读取数据,
  • POST:

    • 向指定资源提交数据,请求服务器进行处理(如提交表单或者上传文件),数据被包含在请求文本中
  • HEAD:

    • 与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据

  • 其余请求:

    • put 向指定资源位置上传其最新内容

    • delete 请求服务器删除Request-URI所标识的资源。

    • trace 回显服务器收到的请求,主要用于测试或诊断

    • options 使服务器传回该资源所有http请求方法

    • connect HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器

  • 请求注意事项:

    • 方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed),当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)

HTTP 状态码
  • 1xx消息——请求已被服务器接收,继续处理

  • 2xx成功——请求已成功被服务器接收、理解、并接受

  • 3xx重定向——需要后续操作才能完成这一请求

  • 4xx请求错误——请求含有词法错误或者无法被执行 404:没有内容 403:没有权限

  • 5xx服务器错误——服务器在处理某个正确请求时发生错误

URL
  • 超文本传输协议,统一资源定位获取五个基本元素,

    https://www.sogou.com/web?query=新闻&_asf=www.sogou.com&_ast=&w=0
    
    # http      传送协议
    # //        层级URL表示符号,固定格式
    # www.sogou.com   域名,服务器和:端口号
    #/                  区分每个路径的的目录
    #/web           页面路径
    #?query=新闻       GET模式查询的窗口参数(?字符为起点,每个参数以&隔开,再以=分开参数名称和数据)
    #               锚点
    
    请求(浏览器发给服务器的数据 request0
        请求方法,路径,协议版本\r\n  #请求行
        k1:v1\r\n
        k2:v2\r\n                 #请求头
        \r\n
        请求数据                   #get请求美哦与请求体
    
    响应(服务端返回给浏览器的数据 respons)
      格式:
          "协议版本" 状态码 状态描述 \r\n
             k1:v1\r\n
             k2:v2\r\n  
             \r\n
             响应数据(响应体)"html文本"

web框架

  • web框架的本质都是一个socket服务端,而用户浏览器就是一个socket客户端部,这样就实现了web框架

web框架的功能:
  1. 使用socket收发消息 (wsgiref wsgi模块也可以收发消息,uwsgi线上使用)

  2. 根据不同路径返回不同的内容

  3. 返回动态的数据(字符串的替换 模板的渲染 jinja2)

分类:
  • Django 2 3

  • Flask 2

  • tornado 1 2 3

自己写web框架:
  • 框架示例:

    import socket
    import time
    
    sk = socket.socket()            # 创建一个socket对象
    sk.bind(('127.0.0.1',667))     # 绑定ip和端口
    sk.listen()                     # 监听
    
    def index(url):
        with open('index.html', 'rb') as f:    #读取index标签
            return f.read()
    
    def home(url):
        ret = '欢迎回家! - {}'.format(url)
        return ret.encode('utf-8')
    
    def help_me(url):
        ret = '再等30年,你又是条好汉! - {}'.format(url)
        return ret.encode('utf-8')
    
    def timer(url):
        now = time.time()
        with open('time.html','r',encoding='utf-8') as f:
            ret = f.read()
            ret = ret.replace('@@time@@',str(now))  #将获取的时间在time中替换展示在页面上
            return ret.encode('utf-8')
    
    list1 = [
        ('/index', index),
        ('/home', home),
        ('/help_me', help_me),
        ('/time', timer),]
    
    while True:
        conn, addr = sk.accept()  # 等待连接
        data = conn.recv(1024)
        url = data.decode('utf-8').split()[1]
    
        func = None
        for i in list1:
            if url == i[0]:
                func = i[1]
                break
    
        if func:
            ret = func(url)
        else:
            ret = '被查办了!'.encode('utf-8')
    
        conn.send(b'HTTP/1.1 200 OK\r\ncontent-type: text/html; charset=utf-8\r\n\r\n')
        conn.send(ret)
        conn.close()
    
    # index页面:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1 style="color: forestgreen"> 欢迎光临! </h1>
    </body>
    </html>
    
    # time页面:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>当前时间是:@@time@@ </h1>
    </body>
    </html>

Django安装简单使用

安装Django:
  • 命令行方法:

    • 使用django必须是专业版

    • pip3 install django==1.11.23 -i https://pypi.tuna.tsinghua.edu.cn/simple (清华源速度快)

  • 查看是否安装成功:

    • 查看此目录是否有django-admin.exe

    • D:\Program Files (x86)\python3.6.8\Scripts

创建项目:
  • 命令行创建:

    • django-admin startproject 项目名称
  • pycharm创建:

    • flie _ new_project _ django _ 项目路径 选解释器

  • 创建后的项目目录:

    xiangmu/
    ├── idea         # pycharm环境
    ├── manage.py    # 管理文件
    ├─— templates    # html css样式目录
    └── xiangmu      # 项目目录
        ├── __init__.py
        ├── settings.py  # 配置
        ├── urls.py      # 路由 --> URL和函数的对应关系
        └── wsgi.py      # runserver命令就使用wsgiref模块做简单的web server
    
    settings.py配置文件:
    ALLOWED_HOSTS = ['*']   #设置可以访问的主机,*=所有
    TEMPLATES-->DIRS      #关联html css文件位置
    
    urls.py URL路径地址:
    

启动项目:
  • 命令行:

    #进入python目录进行启动
    
    python manage.py runserver  127.0.0.1:8000
    python manage.py runserver  127.0.0.1:80
    python manage.py runserver  0.0.0.0:80
  • pycharm:

    • pycharm 点绿三角, 不要右键运行文件

Django项目创建步骤:
  1. 下载

    命令行:

    ​ pip install django==1.11.23

    ​ pip install django==1.11.23 -i 源的地址

    pycharm

    ​ file——》 settings ——》解释器 ——》 点+号 ——》 输入django ——》 选择版本 ——》下载

  2. 创建项目

    命令行:

    ​ 切换到存项目的目录下

    ​ django-admin startproject 项目名

    pycharm:

    ​ file ——》 new project ——》 django ——》 输入项目的路径 ——》 选择解释器 ——》 写一个APP的名字 ——》 create

  3. 启动项目

    命令行:

    ​ 切换进去到项目的根目录 manage.py

    ​ python manage.py runserver # 127.0.0.1:8000

    ​ python manage.py runserver 80 # 127.0.0.1:80

    ​ python manage.py runserver 0.0.0.0:80 # 0.0.0.0:80

    pycharm:

    ​ 点绿三角 启动 确定是django项目

    ​ 可配置ip和端口

  4. 配置settings

    静态文件

    ​ STATIC_URL = '/static/'

    ​ STATICFILES_DIRS = [

    ​ os.path.join(BASE_DIR,’static‘),

    ​ os.path.join(BASE_DIR,’static1‘),

    ​ ]

    中间件

    ​ csrf 中间件注释掉 可以提交POST请求

    INSTALLED_APPS app相关

    数据库

    模板 DIRS

  5. APP

    创建app

    命令行:

    ​ python manage.py startapp app名称

    pycharm:

    ​ tools ——》 run manage.py task ——》 startapp app名称

    注册app

    INSTALLED_APPS = [
    
        # 'app01',
        'app01.apps.App01Config',
    ]

  6. urls.py

    urlpatterns = [
        url(r'^index/', views.index),
        url(r'^home/', views.home),
        url(r'^login/', views.login),
    ]

  7. views.py

    from django.shortcuts import HttpResponse, render, redirect
    
    def index(request,):
        return HttpResponse()
    

    HttpResponse('字符串') 返回字符串

    render(request,'html文件名') 返回一个html页面

    redirect(’重定向的地址‘) 重定向

  8. form表单

    1. form标签的属性 action ='' 提交的地址 method=’post‘ 请求方式 novalidate 不校验

    2. input标签要有name 有些需要value

    3. 有一个类型为submit的input或者 button

  9. get 和post的分别

    get 获取一个页面

    没有请求体

    ?k1=v1&k2=v2

    django 中获取 request.GET request.GET['k1'] request.GET.get('k1','xxxx')

    post 提交数据 数据隐藏

    django 中获取 request.POST request.POST['k1'] request.POST .get('k1','xxxx')

orm-增删改查:

  • __str__和__repr__区别

    有时候我们想让屏幕打印的结果不是对象的内存地址,而是它的值或者其他可以自定义的东西,以便更直观地显示对象内容,可以通过在该对象的类中创建或修改__str__()或__repr__()方法来实现(显示对应方法的返回值)
    
    # 两种触发方式:
        使用print()时
        使用%s和f'{}'拼接对象时
        使用str(x)转换对象x时
    
    在上述三种场景中,会优先调用对象的__str__()方法;若没有,就调用__repr__()方法;若再没有,则显示其内存地址。
    
    # 特别地,对于下面两种场景:
        用%r进行字符串拼接时
        用repr(x)转换对象x时
      则会调用这个对象的__repr__()方法;若没有,则不再看其是否有__str__()方法,而是显示其内存地址
  • 创建表数据

    from django.db import models
    
    class Publisher(models.Model):                # Publisher:表名,继承models.Model类
        pid = models.AutoField(primary_key=True)  # 自增、主键
        name = models.CharField(max_length=32,unique=True)
    
        def __str__(self):
            return "{}{}".format(self.pid,self.name)  # 返回给调用者内容

  • 查询展示数据:

    #查询所有数据
    all_publisher = models.Publisher.objects.all()  
    
    #将数据返回到前端
    return render(request,'publisher_list.html',{'all_publisher':all_publisher})
    
    #展示数据
    from django.shortcuts import render,HttpResponse,redirect
    from app import models
    
    
    #从数据库中展示数据
    #从数据库中查询出所有的出版社
    #从数据库中查询的数据展示到前端页面
    def publisher_list(request):
        all_publisher = models.Publisher.objects.all().order_by('pid') 
        return render(request,'publisher_list.html',{'all_publisher':all_publisher})

  • 模板的语法:

    {{  all_publishers }}    变量
    
    {% for  i in  all_publishers  %}  #for循环
    
      {{ forloop.counter }}         #循环打印的内容
      {{  i }}
    
     {% endfor %}                       #闭合

  • 新增数据:

    # 方式一:通过create将数据插入到数据库中(推荐)
    ret = models.Publisher.objects.create(name=pub_name)
    return redirect("/publisher_list")  
    
    #方式二:先创建对象在save进行保存
    obj = models.Publisher(name=pub_name)
    obj.save()
    
    
    #新增出版社 
    def publisher_add(request):                           
        pub_name,error='',''
        if request.method == "POST":                    #判断前端是POST类型还是GET类型
            pub_name = request.POST.get('pub_name')     #通过POST获取前端的出版社名称
    
            if not pub_name:
                error = "输入不能为空"
            elif models.Publisher.objects.filter(name=pub_name):
                error = "数据已经存在"
            else:                                     #通过create将数据插入到数据库中(推荐)
                models.Publisher.objects.create(name=pub_name)  
                return redirect("/publisher_list")
    
        return render(request, 'publisher_add.html', {'pub_name': pub_name, 'error': error})

  • 删除数据

    pk = request.GET.get('pk')
    query = models.Publisher.objects.filter(pk=pk)  # 对象列表
    query.delete()     # 通过queryset 删除
    query[0].delete()  # 通过单独的对象 删除
    
    
    # 删除数据
    def publisher_del(request):       
        pk_id = request.GET.get('pk')        # 获取前端URL数据
        query = models.Publisher.objects.filter(pk=pk_id)
        if not query:
            return HttpResponse("要删除的数据不存在")
        query.delete()                     # 参数有多个删除多个,query[0].delete()#只删除一个
        return redirect("/publisher_list")

  • 编辑数据

    obj = models.Publisher.objects.filter(pk=pk).first()  # 对象列表中第一个对象
    obj.name = pub_name  # 内存中修改
    obj.save()             # 提交
    
    #编辑数据
    def publisher_edit(request):   
        error=""
        pk_id = request.GET.get("pk")   #url地址携带的参数,first()查出多个只取一个,没有不报错
        obj = models.Publisher.objects.filter(pk=pk_id).first()
    
        if not obj:
            return HttpResponse("编辑的对象不存在")
    
        if request.method == "POST": # 获取新提交的数据,编辑原始的对象
            pub_name = request.POST.get('pub_name')
    
            if not pub_name:
                error = "输入不能为空"
            elif models.Publisher.objects.filter(name=pub_name):
                error = "数据已经存在"
            else:
                obj.name = pub_name
                obj.save()
                return redirect("/publisher_list")
        return render(request,'publisher_edit.html',{'obj':obj,'error':error})

ORM一对多关系:

外键的设计

# ForeignKey 添加的外键('Publisher' 添加字符串通过反射进行查找)
    on_delete=models.set(1)   唯一
    default=11,on_delete=models.DEFERRED   默认值
    null=True on_delete=models.SET_NULL     设置字段可有为空
    on_delete=models.DO_NOTHING             什么操作都不做

# on_delete  2.0 必填 ,关联删除后的选项
class Book(models.Model):
    id = models.AutoField(primary_key=True)  # 自增、主键
    title = models.CharField(max_length=32)
    pid = models.ForeignKey('Publisher', on_delete=models.CASCADE) #默认关联主键 

增删改查

  • 查询:

    all_books = models.Book.objects.all()
    print(all_books)
    
    for book in all_books:
        print(book)
        print(book.pk)
        print(book.title)
        print(book.pub,type(book.pub))      # 一对多关联的是对象 
        print(book.pub_id,type(book.pub_id))  # 所关联的对象的pk
        print('*' * 32)

  • 新增

    #pub=models.Publisher.objects.get(pk=pub_id)  只能等于一个对象
    models.Book.objects.create(title=title,pub=models.Publisher.objects.get(pk=pub_id))
    
    #第二种方法,pub外键必须等于一个对象,但是都要转换成id,所以第二种方法直接添加ID也可以
    models.Book.objects.create(title=title, pub_id=pub_id)  

  • 删除

    pk = request.GET.get('pk')
    models.Book.objects.filter(pk=pk).delete()

  • 编辑

    book_obj.title = title
    book_obj.pub_id = pub_id
    
    #第一种方法
    book_obj.pub = models.Publisher.objects.get(pk=pub_id)
    book_obj.save()
    
    #第二种方法
    book_obj.pub_id = new_pub_id     #将数据保存
    book_obj.save()

ORM 多对多关系:

多对多关系创建

  • 第一种方法:django通过ManyToManyField自动创建第三张表

    # 在上下两个表中添加都可以,只是顺序不一样
    class Book(models.Model):
       title = models.CharField(max_length=32)
       pub = models.ForeignKey('Publisher', on_delete=models.CASCADE)
       # authors = models.ManyToManyField('Author')  # 描述多对多的关系   不生成字段  生成关系表
    
        def __repr__(self):
            return self.title
    
        __str__ = __repr__
    
    class Author(models.Model):
        id = models.AutoField(primary_key=True)  # 自增、主键
        name = models.CharField(max_length=32)
        books = models.ManyToManyField('Book')  # 描述多对多的关系   不生成字段  生成关系表

  • 第二种方法:自己手动创建

    class Book(models.Model):
        title = models.CharField(max_length=32)
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)
    
    
    class Book_Author(models.Model):
        book = models.ForeignKey(Book, on_delete=models.CASCADE)
        author = models.ForeignKey(Author, on_delete=models.CASCADE)
        date = models.DateField()     #自己创建表可以添加多个字段(时间字段)

  • 第三种方法:自己创建 + ManyToManyField

    # 第三张表包含了第一张和第二张表的使用方法
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)
      #第三种方法:to book表字段,through=指定多对多关系
        books = models.ManyToManyField(Book, through='Book_Author')  
    
    
    class Book_Author(models.Model):   #创建关系表
        book = models.ForeignKey(Book, on_delete=models.CASCADE)
        author = models.ForeignKey(Author, on_delete=models.CASCADE)
        date = models.DateField()

多对多-增删改查

  • 查询:

    def author_list(request):
        all_author = models.Author.objects.all().order_by("id")
    
        for author in all_author:
            print(author)
            print(author.name)
            print(author.pk)
            print(author.books)         #多对多拿到books是关系对照表
            print(author.books.all())   #通过all拿到关联的所有对象
            print("*"*32)
        return render(request,'author_list.html',{'all_author':all_author})
    
    # 从book表中查询author作者数据,下面是获取到author对象
      book.author_set.all  
    
    
    # 前端展示示例:
      <tbody>
             {% for author in all_author %}
                    <tr>
                        <td>{{ forloop.counter }}</td>
                        <td>{{ author.pk }}</td>
                        <td>{{ author.name }}</td>
                        <td>
                            {% for book in author.books.all %}
                                    《{{ book.title }}》
                            {% endfor %}
                        </td>
                        <td>
                            <a href="">删除</a>
                            <a href="">编辑</a>
                        </td>
                     </tr>
            {% endfor %}
     </tbody>

  • 新增:

    books = request.POST.getlist('books')               # 获取多个元素
    author_obj = models.Author.objects.create(name=name)  # 新建作者
    author_obj.books.set(books)                         #【id,id】,给作者和书籍绑定关系
    
    
    #新增示例:
    def author_add(request):
        if request.method == "POST":
            name = request.POST.get('name')
            books = request.POST.getlist('books')  #列表方式获取书籍ID ['1']
    
            author_obj = models.Author.objects.create(name=name)   #作者名写入数据库
            author_obj.books.set(books)                              #写入关联方式
    
            return redirect("/author_list/")
        all_books = models.Book.objects.all()
        return render(request,'author_add.html',{'all_books':all_books})
    

  • 删除:

    def author_del(request):
        pk_id = request.GET.get("pk")
        models.Author.objects.filter(pk=pk_id).delete()
    
        return redirect('/author_list/')

  • 编辑:

    def author_edit(request):
        pk_id = request.GET.get("pk")
        obj = models.Author.objects.filter(pk=pk_id).first()
    
        if request.method == "POST":
            name = request.POST.get('name')
            books = request.POST.getlist('books')  #列表方式获取书籍
    
            obj.name = name                #修改作者名称
            obj.save()
            obj.books.set(books)           #添加作者关联的书籍地址,每次都是删除后在添加
            return redirect('/author_list//')
    
        all_books = models.Book.objects.all()
        return render(request,'author_edit.html',{'obj':obj,'all_books':all_books})
    
    # 前端示例
        <form action="" method="post">
            <p>
                作者:<input type="text" name="name" value="{{ obj.name }}">
            </p>
    
            <p>
                著作:<select name="books" id="" multiple>
                    {% for book in all_books %}
                        {% if book in obj.books.all %}
                                <option selected value="{{ book.pk }}">{{ book.title }}</option>
                            {% else %}
                                <option value="{{ book.pk }}">{{ book.title }}</option>
                        {% endif %}
    
                    {% endfor %}
    
                </select>
            </p>
            <button>提交</button>
        </form>

模板使用:

MVC 和 MTV

  • MVC:

  • mvc是软件中一种软件架构模式,把软件系统分为三个基本部分,具有耦合性低,重用性高,生命周期成本低

    • M: model 模型 和数据库交互
    • V:view 视图 展示页面 HTML
    • C: controller 控制器 调度 业务逻辑
  • MTV:

    • M : model 模型 操作数据库 orm、

    • T : template 模板 HTML

    • V: view 视图 业务逻辑

常用语法:

  • {{ 变量 }} :两个大括号 表示变量,在模板渲染的时候替换成值

  • {{% 逻辑%}} : 括号中带有两个百分号表示逻辑相关操作

变量

  • {{ 变量名. }}

    • ​ .索引 .key .属性 .方法
    • 列表.索引 字典.key 类.属性 类.方法
  • 示例:

    # 模板中的变量是通过views render后端返回到前端,前端通过{{}}显示到页面
    # 注意事项:在字典中创建跟字典方法相同key 会优先调用字典中的key > 对象的属性或方法 > 列表的索引  
    
    {{ name_list.1 }}  拿到列表第一个索引
    {{ dic.name }}     通过字典的key取值
    {{ dic.keys }}     字典的数据类型,keys
    {{ dic.values }}   字典的数据类型,values
    
    {{ p1 }}         类中方法,在类中定义str返回值,返回所有值
    {{ p1.name }}    返回name属性
    {{ p1.talk }}    调用类中方法,不用加括号
    
    
    #views
    def index(request):
        class Person():
            def __init__(self,name,age):
                self.name = name
                self.age  = age
    
            def talk(self):
                return "我太难了"
    
            def __str__(self):
                return f"Person obj:{self.name}{self.age}"
    
        booll = True
        num = 666
        string = "小窝窝"
        name_list = ["海洋","俊丽","小宝宝","大宝宝"]
        dic = {
            "name":"小明",
            "age":"18",
            "sex":"18",
            'hobby':["台球","篮球","足球",'羽毛球']}
    
        p1 = Person("胡海洋",18)
    
        context = {
            'booll':booll,
            'num':num,
            'string':string,
            'name_list':name_list,
            'dic':dic,
            'p1':p1}
        return render(request,'index.html',context)
    
    # templates
    <body>
        {{ booll }}<br>
        {{ num }}<br>
    
        {{ string }}<br>
    
        {{ name_list.1 }}<br>
    
        {{ dic.values }}<br>
    
        {{ p1}}
        {{ p1.name }}
        {{ p1.talk }}
    </body>

过滤器-Filters

  • 修改变量的显示结果

  • 语法

    • {{ value|filter_name }} {{ value|filter_name:参数 }}

  • default: 提供默认值

    # 传过来的变量不存在或者为空 使用默认值,:后面不可以有空格!!!
    {{ qq|default:'默认值自己设置' }}
    
    # 后端变量如果无效可以增加提示
    注:TEMPLATES的OPTIONS可以增加一个选项:string_if_invalid:'找不到',可以替代default的的作用。

  • 列表切片

    {{ name_list|slice:'::-1' }}   从后往前切片
    {{ name_list|slice:'0:2' }}    切两个值

  • filesizeformat 文件大小格式化

    • 将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)

      {{ filesize|filesizeformat }}
      
      后端:'filesize':1*1024*1024

  • add

    数字的加法   字符串的拼接  列表的合并
    
    {{ num|add:2 }}            # 给数字进行加2,如果num为字符串类型也会进行相加
    {{ string|add:"haha"}}       # 通过add给string字符串进行拼接
    {{ name_list|add:name_list}} # 列表拼接
    
    {{ num|add:-2 }}           减法
    {% widthratio num 1 4%}  乘法
    {% widthratio num 2 1%}  除法 # 1放在中间时乘法,放在后面是除法

  • 其他内置过滤器方法

    • 字符串

      • 变小:{{ string|lower}}
      • 变大:{{ string|upper}}
      • 计数:{{ string|length}}
    • 列表

      • 取第一个元素: {{ name_list|first}}

      • 取最后一个元素 : {{ name_list|last}}

      • 字符串拼接列表: {{ name_list|join:'**'}}

      • 按字符串长度取值: {{ string|truncatechars:'10'}} ,truncatewords按照单词区分

  • 日期格式化:

    {{ now|date:'Y-m-d H:i:s' }}  年月日时分秒
    {{ now|date}}               设置settings后使用方法
    {{ now|time}}
    # 后端格式
      'now':datetime.datetime.now()
    
    # settings 配置
    USE_L10N = False
    DATETIME_FORMAT = 'Y-m-d H:i:s'
    DATE_FORMAT = 'Y-m-d'
    TIME_FORMAT = 'H:i:s'

  • safe 告诉django不需要“”转义:

    {{ js|safe }}
    {{ a|safe }}
    
    # 后端js,字符串在前端还是普通字符串,想要展示效果,添加safe不需要转义
    'js':'''
            <script>
                for (var i = 0; i < 5; i++) {
                    alert('11111')
                }
            </script>
       '''
    'a':'<a href="http://www.baidu.com">跳转</a>'

  • PY文件中不转义方法

    # 第二种方法mark_safe 在后端进行处理,前端直接调用:
    
    {{ a }}   # 调用
    
    from django.utils.safestring import  mark_safe
    'a':mark_safe('<a href="http://www.baidu.com">跳转</a>')

自定义过滤器
  1. 在app下创建一个名为templatetags的python包(文件夹)

  2. 在包内创建py文件 —— 》 自定义 my_yags.py

  3. 在py文件中写入:

    from django import template
    register = template.Library()  # register不能变
  4. 定义函数 + 加装饰

    @register.filter
    # new_upper(|后面的方法)  value(原本的变量) arg最多有一个(冒号就后面的参数,可默认)
    
    def new_upper(value, arg=None):  # arg 最多有一个
        print(arg)
        return value.upper()         # 不返回 前端结果为None
  5. 在模板中使用:

    {% load my_yags %}          # 加载写函数定义的文件
    
    {{ string|new_upper:dic }}  # string变量,new_upper函数方法,dic后端数据,可以返回到函数arg中

for 循环

  • forloop

    # for 循环格式
    {% for name in name_list %}
        <li> {{ forloop.counter0 }} - {{ name }}</li>
    {% endfor %}
    
    {{ forloop.counter0 }}     字典中都已经定义好了一下参数
    {{ forloop.counter }}      当前循环的从1开始的计数
    {{ forloop.counter0 }}     当前循环的从0开始的计数
    {{ forloop.revcounter }}   当前循环的倒叙计数(到1结束)
    {{ forloop.revcounter0 }}  当前循环的倒叙计数(到0结束)
    {{ forloop.first}}         当前循环是否是第一次循环  布尔值
    {{ forloop.last}}        当前循环是否是最后一次循环  布尔值
    {{ forloop.parentloop }}   当前循环父级循环的forloop(多层循环中才可以查看到)
  • empty

    {% for name in name_list %}
        {{ name }}
    
    {% empty %}
        空的数据,循环过程没有数据返回此提示
    {% endfor %}

if 判断

  • if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。

    # 判断列表中为偶数的变颜色,if里面可以使用过滤器
    <table border="1">
        <tbody>
            {% for name in name_list2 %}
                <tr>
                    {% for new in name %}
                        {% if forloop.counter|divisibleby:2 and forloop.parentloop.counter|divisibleby:2%}
                                <td style="background-color: yellow">{{ new }}</td>
                            {% else %}
                                <td>{{ new }}</td>
                        {% endif %}
                    {% endfor %}
                </tr>
            {% endfor %}
        </tbody>
    </table>
    
    
    # 多层判断
        {% if p1.age < 18 %}
            宝宝
        {% elif p1.age == 18 %}
            刚成年
        {% else %}
            上班
        {% endif %}
    
    # 不支持算数运算 
      {% if 1 + 2 == 3 %}
        {% if 1|add:2 == 3 %}  可以这样写
    
    # 不支持连续判断
      {% if 10 >5 > 1 %}

with和csrf_token

  • with示例:

    # 取别名两种方式
    {% with alex=person_list.1.name age=person_list.1.age   %}
        {{ alex }} {{ age }}
        {{ alex }}
    {% endwith %}
    
    {% with person_list.1.name as junli %}
        {{ junli }}
    {% endwith %}

  • {% csrf_token %}

    # {% csrf_token %}标签放在form标签中,不安全报错403
    # form表单中有一个隐藏的input标签(name  ='csrfmiddlewaretoken'),后端通过这个值进行验证
    
    <form action="" method="post">
        {% csrf_token %}   
    </form>

母板和继承

  • 母板就是将公共部分提取出来,不重复的定义block,之后再进行集成填充block块,减少重复代码,提高效率

  • 母板

    • html页面 提取多个页面的公共部分

    • 定义多个block块,需要让子页面覆盖填写

      # 找到重复的页面定义bolck块
      
      # 第一种block块,设置母版
      <div class="col-sm-3 col-md-2 sidebar">
      <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
              {% block content %}
              <h1>母版</h1>
              {% endblock %}
       </div>
      
      # 第二种block可以设置为参数
          <ul class="nav nav-sidebar">
              <li class="{% block pub_active%}{% endblock %}">
              <li class="{% block book_active%}{% endblock %}">
              <li class="{% block author_active%}{% endblock %}">
          </ul>
      
      
      # 每个页面的css样式,在继承中填写link
          {% block  css%}
      
          {% endblock %}

  • 继承母版:

    • {% extends ‘母板文件名’ %}

    • 重写block块 ,写在block内部

      {% extends 'base.html' %}     # 引用母版文件
      
      {% block content %}
                  <h3 class="sub-header"> 出版社列表 </h3>   #填写block块,将代码覆盖添加到块中
              <div class="table-responsive">
      {% endblock %}
      
      
      {% block book_active %}      # 重写block会覆盖block内的内容
          active  
      {% endblock %}
      
      
      {% block css %}              # 重写每个页面文件的样式,这样避免重复,公共样式写在母版中
          <link rel="stylesheet" href="">
      {% endblock %}

  • 注意:

    • 注意事项

      • {% extends 'base.html' %} 带上引号 不带的话会当做变量

      • {% extends 'base.html' %} 上面不要写其他内容,否则会显示出来

      • 要显示的内容写在block块中

      • 母板可以定义多个block 定义 css js,每个子页面继承自己的css和js样式

组件

  • 组件就是将一段常用HTML代码段存放单独的html中,想调用直接引用

  • include组件示例:

    #组件其实就是将代码进行拆分
    #新建一部分代码如导航栏,存放在 nav.html中
    
    #拆分的组件可以使用include在母版中,也可以使用在单独的html中:
    {%  include  ‘nav.hmtl ’ %}

静态文件相关

  • 引入静态文件:

    # 从配置文件中自动找到static别名
    # 第一种方式
    {% load static %} 
    <link rel="stylesheet" href="{% static 'css/dashboard.css' %}">
    
    # 第二种方式
    <link rel="stylesheet" href="{% get_static_prefix %}css/dashboard.css">

定义 filter simple_tag inclusion_tag

  1. 在app下创建templatetags的python包文件

  2. 在包内创建py文件 my_tags.py

  3. 在py文件中写代码:

    from django import  template
    register = template.Library()
    
  4. 定义函数 + 加装饰器

  • filter 过滤器方法

    @register.filter   
    def add_arg(value,arg):          # 只能接受两个
       return  "{}_{}".format(value,arg) 
    
    {% load my_yags %}                 # 加载写函数定义的文件,不可以嵌套,可以在for循环if里使用
    {{ string|new_upper:"haha" }} 

  • simple_tag 方法:

    # 可以接受多个参数
    @register.simple_tag            
    def join_str(*args, **kwargs):
        return '_'.join(args) + "*".join(kwargs.values())
    
    # 调用方法
    {% load my_tags %}
    {% join_str "v1" "v2" k3="v3" k4="v4"%}

  • inclusion_tag 方法

    # my_tags中填写 inclusion_tag函数
    @register.inclusion_tag('page.html')  # 必须制定页面,而且是动态,要不和组件一样
    def page(num):
       return {'num':range(1,num+1)}        # 返回必须是字典交给模板,接受HTML返回的参数,返回给page
    
    
    # 前端页面调用 page函数
        {% load my_tags %}
        {% page  5%}
    
    
    # page.heml 页面添加一些分页功能,接受page发送刚过来的参数num
        </li>
         {% for i in num  %}
             <li><a href="#">{{ i }}</a></li>
                 {% endfor %}
         <li>    

视图

  • 视图系统,是一个简单的python函数,接受web传过来的请求,并向web进行响应,响应可以是html,重定向,错误提示啊,无论你的视图包含什么逻辑,都需要返回响应

  • FBV: function based view

  • CBV: class based view

    • cbv示例:

      from django.views import View
      
      class AddPublisher(View):
          def get(self,request):  #处理get请求
              return xx
      
          def post(self,request): #处理post请求
              return xx
      
      # urls调用
      url(r'^publisher_add/',views.AddPublisher.as_view() ),

CBV的流程

  • 看这个cbv流程主要是要知道request和get是怎么执行的

  • 项目启动时,运行urls.py

    url(r'^publisher_add/',views.AddPublisher.as_view() )  # 执行as_view() 函数
    AddPublisher.as_view() 执行  ——>   view函数    # 将此函数AddPublisher传给 as_view()
    
    # view函数
        def as_view(cls, **initkwargs):        # cls = AddPublisher   
            def view(request, *args, **kwargs):  # reqyest 前端的请求  
                self = cls(**initkwargs)       # AddPublisher实例化对象self
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                    self.request = request       # 类中使用的request就是self.request
                    self.args = args
                    self.kwargs = kwargs
                return self.dispatch(request, *args, **kwargs)  # 调用dispatch方法
            view.view_class = cls
            view.view_initkwargs = initkwargs
    
            update_wrapper(view, cls, updated=())
            update_wrapper(view, cls.dispatch, assigned=())
            return view
  1. 请求到来时,实行view函数:

    1. 实例化AddPublisher ——> self

    2. self.request = request

    3. 执行View中self.dispatch(request, *args, **kwargs)

      def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names: # 判断请求是否被准许,方法有八种
            handler = getattr(self, request.method.lower(),                                                     self.http_method_not_allowed)  # 反射,判断请求是否错误
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)             # 执行 handler=get加括号
      
      # 判断请求方式是否被允许
          允许
            通过反射获取请求方式对应的方法     ——> handler  
          不允许
            self.http_method_not_allowed  ——> handler   # 返回的是错误代码信息
      
      执行handler 获取到响应

视图加装饰器

  • 装饰器函数:

    # 视图添加装饰器,先创建好装饰器
    def timer(func):
        def inner(*args,**kwargs):
            start = time.time()
            ret = func(*args,*kwargs)
    
            print(f"{time.time()-start}")
            return ret
        return inner

  • FBV 函数添加装饰器直接加:

    @timer
    def publisher_edit(request):   #编辑数据
        pass

  • CBV 类中方法添加装饰器:

    # 第一种在方法中添加:
        from django.utils.decorators import method_decorator
    
        class AddPublisher(View):
            @method_decorator(timer)
            def get(self, request):  # 处理get请求
                print('get')
                return  render(request, 'publisher_add.html')
    
    
    # 第二种在dispatch上添加,这样所有请求都带有装饰器功能:
        @method_decorator(timer)
        def dispatch(self, request, *args, **kwargs):
              print("dispatch 前")
                ret = super().dispatch(request,*args,**kwargs)
                print("dispatch 后")
                return ret
    
    
    # 第三种加在类上面:
        @method_decorator(timer,name='post')
        @method_decorator(timer,name='get')
      @method_decorator(timer,name='dispatch')
        class AddPublisher(View):
          pass

request:

  • 获取请求参数:

    request.method      # 请求方式  GET POST 
    request.GET         # url上携带的参数  {}   get()
    request.POST        # post请求提交的参数  {}   get()
    
    request.META          # http头部的信息,具体信息取决于客户端和服务器,获取页面request headers
    request.body          # 请求体的数据  post也是在body里面取值的
    
    request.path_info     # 获取路径信息  不包含IP和端口 也不包含查询参数
    request.FILES     # 获取上传的文件 
    request.scheme        # 获取前端是http或者是https字符串
    
    request.COOKIES       # cookie 记录状态 键值对存储在浏览器
    request.session   # session 记录状态,键存储在浏览器,值存储在数据库中,更安全些
    
    request.get_full_path()   # 完整的路径信息 包含查询参数 不包含IP和端口 
    request.is_ajax()           # 判断是否是ajax请求

  • 上传文件注意事项:

    # form表单的属性  enctype="multipart/form-data"
    # input   type = 'file'
    # request.FILES.get('f1')      
    
    class File_upload(View):
        def get(self,request):
            return render(request,'file_upload.html')
    
        def post(self,request):
            file_obj = request.FILES.get('f1')
            with open(file_obj.name,'wb') as f:
                for i in file_obj.chunks():
                    f.write(i)
            return HttpRespose("ok")
    
    # 前端页面:
    <form action="" method="post" enctype="multipart/form-data">
        <p>
               上传文件:<input type="file" name="f1" multiple>
        </p>
        <button>上传</button>
    </form>

response

  • HttpResponse('字符串') # 返回字符串

  • render(request,'模板的文件名',{}) # 返回一个完整的页面

    def render(request, template_name, context=None, content_type=None, status=None, using=None):
      # content loader.render_to_string 这里已经将模板页面进行渲染完成
        content = loader.render_to_string(template_name, context, request, using=using)
      # 将content内容封装成httpresponse对象
        return HttpResponse(content, content_type, status)  

  • redirect('要跳转的地址') # 重定向 响应头 Location: url

  • 301: url后面不添加/ 302:添加/会直接永久重定向

    def redirect(to, *args, **kwargs):
        if kwargs.pop('permanent', False):
            redirect_class = HttpResponsePermanentRedirect  # 301 暂时重定向
        else:
            redirect_class = HttpResponseRedirect           # 302 永久重定向
    
        return redirect_class(resolve_url(to, *args, **kwargs))

  • 前后端分离-JSON:

    # 第一种json传参方法:
    import json
    def get_data(request):
        data = {'status':0,'data':{'k1':'v1'}}
      return HttpResponse(json.dumps(data),content_type='application/json')
    
    # 第二种简单方法(常用,前端自动进行反序列化):
    def get_data(request):
        data = {'status':0,'data':{'k1':'v1'}}
      return JsonResponse(data)
    
    # 不是字典,列表的话需要添加safe=False
    from django.http.response import JsonResponse
    def get_data(request):
        l1 = ["1",'2','3']
      return JsonResponse(l1,safe=False)
    

路由

  • 路由的本质就是,url与调用的视图函数之间的映射表,也就是说你访问我哪个url路由执行相对应的函数

  • 路由匹配的只是路径,获取的参数永远只是字符串

  • urls的基本配置

    from django.conf.urls import url
    from django.contrib import admin
    from app import views
    urlpatterns = [
         url(正则表达式, views视图,参数,别名),
    ]

正则表达式

  • 正则规则

    • 以什么开头^ 结尾$ [0-9] [a-zA-Z]{4}

    • . 数字:\d 数字和字母:\w 0个或者多个:? 至少一个:+ 0和或者无数个:*

  • 从上到下的进行匹配 匹配到一个不再往下进行匹配,开头不加 / ,游览器后面/自动添加

    urlpatterns = [
        url(r'^admin/', admin.site.urls),              # r转义,^以什么开头,前面不加/
        url(r'^blog/$', views.bolg),                   # 如果上下第一层目录相同,添加$表示结尾
        url(r'^blog/[0-9]{4}/[0-9]{2}/$', views.bolgs),  # 简单的动态路由可以匹配多个
    ]

分组和命名分组

  • 从URL上获取到的参数的类型都是字符串,获取的参数按照位置参数传递给视图函数

    # urls
        urlpatterns = [
            url(r'^blog/([0-9]{4})/([0-9]{2})/$', views.bolgs),]
    
    # views
        def bolgs(request,year,month):   #year和month按照顺序接受分组前端返回信息
            print(year)
            print(month)
            return HttpResponse("首页")

  • 命名分组:获取的参数按照关键字参数传递给视图函数,视图中实参可以指定默认值

    urlpatterns = [
        url(r'^blog/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.bolgs),
    ]
    
    
    def bolgs(request,year,month):   #year和month按照顺序接受分组前端返回信息,前后端命名相同
        print(year)
        print(month)
        return HttpResponse("首页")

路由分发include:

  • 分发示例:

    # luyou
    from django.conf.urls import url,include
    from django.contrib import admin
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        # url(r'^',include('ap.urls')),        # 匹配时为空,跟之前相同都是一级目录
        url(r'^app/',include('app.urls')),         # 匹配app中的目录
        url(r'^app01/',include('app01.urls'))    # 可以匹配多个app
    ]
    
    # app
    from django.conf.urls import url
    from app import views
    
    urlpatterns = [
        url(r'^blog/$', views.bolg),     #$表示结尾
        url(r'^blog/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.bolgs),
    ]

命名URL和URL反向解析

  • 命令url和反向解析,解决了路径写死问题,让路径修改更加灵活

  • 命名url

    urlpatterns = [
        url(r'^admin/', admin.site.urls,),
        url(r'^publisher_list/',views.publisher_list.as_view(),name='publisher'),]

  • 反向解析:

    • 模板

      # 模板解释方法,通过命令url中的设置的name,获取到真实路径
      
      <a href="{% url 'publisher' %}">出版社列表 </a>  
    • py文件

      # py文件中使用,也是通过name获取到路径
      from  django.urls  import reverse
      
      def AddPublisher(request):
          return redirect(reverse('publisher'))  #redirect('publisher') 相同    

  • 分组命名URL

    • 分组命令url

      # url命名动态路由
      urlpatterns = [
          url(r'^blogs/([0-9]{4})/([0-9]{2})/$', views.bolgs,name='blogs'),
          url(r'^home/$', views.home), ]

    • 反向解析

      • 模板

        # 模板中反向解析
        <a href="{% url 'blogs' '2019' '12' %}">xxx</a>
        
        # views
        def home(request):
            return render(request,'home.html')
      • py文件

        def home(request):
            return redirect('blogs','2020','10')  

  • 命名分组URL(了解)

    • 分组命名

      url(r'^blogs/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.blogs,name='blogs'),
    • 反向解析

      • 模板

        <a href="{% url 'blogs' year='2019' month='13' %}">xxx</a>
      • py文件

        from  django.urls  import reverse
        reverse('blogs',args=('2019','09'))   ——> /app01/blogs/2019/09/
        
        reverse('blogs', kwargs={'year': '2019', 'month': '12'})     ——> /app01/blogs/2019/12/

namesapce 名称空间

  • 名称空间示例,解决命名分组相同问题

    # app,app01
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
    
        url(r'^app/',include('app.urls',namespace='app')),
        url(r'^app01/',include('app01.urls',namespace='app01')),
    ]
    
    # app
    urlpatterns = [
        url(r'^home/$', views.home,name='home'),]
    
    def home(request):
        return render(request,'home.html')
    
    # app01 
    urlpatterns = [
        url(r'^home/$', views.home,name='home'),]
    
    def home(request):
        return render(request,'home1.html')
  • 反向解析

    # app:blogs 起了相同的name时,根据名称空间来区分
    
    <a href="{% url 'app:blogs' year='2019' month='13' %}">xxx</a>

示例:

  • html模板

    <a href="{% url 'delete' 'author' author.pk %}">删除</a>
  • urls

    url(r'(\w+)_del/(\d+)/$',views.delete,name='delete')
  • views

    def delete(request, table, pk):
        print(table, pk)
        # 查询到对应的数据进行删除
        model_cls = getattr(models, table.capitalize(),)
        model_cls.objects.filter(pk=pk).delete()
        # 重定向
        return redirect(reverse(table))

ORM

  • orm对象关系映射,面向对象和关系型数据库的对应关系,将程序中的对象保存到数据库中

  • 注意不同的django和不同版本的mysql存在兼容性

    • 类 表
    • 属性 字段
    • 对象 数据行

  • str
    # str返回的只是显示出来信息
    def __str__(self):
    return f"{self.pid}{self.name}{self.age}{self.bith}"

admin

  • 创建一个超级用户

  • python36 manage.py createsuperuser

  • 输入用户名和密码

    • 注册

    • 在app下的admin.py中写

      from django.contrib import admin
      from app01 import models
      
      admin.site.register(models.Person)   #注册Person
  • 登陆网页http://127.0.0.1:8000/admin/(找到对应的表做增删改查)

  • 脚本运行

    import os
    import django
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lainxi.settings")
    django.setup()
    from app01 import models  #放在最后面引入

ORM 常用字段

  • 常用字段

    • AutoField 主键

      #自增的整形字段,必填参数primary_key=True,则成为数据库的主键。无该字段时,django自动创建。
      #一个model不能有两个AutoField字段。
      
      pid  = models.AutoField(primary_key=True) 
    • CharField 字符串

      #字符类型,必须提供max_length参数。max_length表示字符的长度。
      name = models.CharField(max_length=32)
    • IntegerField 整型

      #-21亿到正21亿
      age = models.IntegerField() 
    • DecimalField 十进制小数

      max_digits=5      #总长度五位
      decimal_places=2  #小数位长度  999.99
      
      price = models.DecimalField(max_digits=5,decimal_places=2,default=0)

    • DateTimeField 日期时间

      auto_now和auto_now_add和default参数是互斥的,不能同时设置
      
      # auto_now=True 每次修改时间,都会变化
      bith = models.DateTimeField(auto_now=True) 
      
      # auto_now_add=True  #新增数据,自动添加时间
      bith = models.DateTimeField(auto_now_add=True)

  • 其他字段

    BooleanField(Field)        #布尔值
    NullBooleanField(Field)    #可以为空的布尔值
    
    BigIntegerField(IntegerField)     #存入的数字更多
    BigAutoField(AutoField)           #自增存入的数字更多
    SmallIntegerField(IntegerField)   #小整数 -32768 ~32767
    FloatField(Field)                   #浮点数
    
    TextField              #存储大字符串多内容
    EmailField(CharField)    #判断字符串是否是email格式
    IPAddressField(Field)    #判断字符是否是IPV4格式
    URLField(CharField)      #判断是否是正常的url地址
    UUIDField(Field)         #uuid格式校验
    FilePathField(Field)     #存储文件路径
    FileField(Field)         #存储文件
    ImageField(FileField)    #存储图片
    
    DateField()        #日期格式
    TimeField()            #时间格式

  • 自定义char字段

    class MyCharField(models.Field):
        # 自定义的char类型的字段类
        def __init__(self, max_length, *args, **kwargs): #init可以自定义,不写源码里面也有
            self.max_length = max_length
            super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
    
        def db_type(self, connection):
            #限定生成数据库表的字段类型为char,长度为max_length指定的值
            return 'char(%s)' % self.max_length
    
    #调用:
    phone = MyCharField(max_length=11)

字段参数

  • null=True 数据库该字段可以为空

  • blank=True 校验时可以为空,from表单可以不填

  • default 默认值

  • unique 唯一索引,有重复的添加不了

  • verbose_name admin页面显示的名称

  • choices=((True, '男'), (False, '女')) 可选择的参数

    # 单选,通过bool进行设置,true为1 false为0,默认为男1
    gender = models.BooleanField(default=True,choices=((True,'男'),(False,'女')))
    
    gender = models.BooleanField(max_length=32,choices=((1,'男'),(2,'女'),(3,'xxx')))

  • 其他参数:

    • db_column 跟数据库关联时使用,关联数据库中的列名

    • db_index=True 数据库字段创建索引

    • editable=False 不可编辑,默认为True

    • help_text admin页面显示提示信息

表的参数

class Person(models.Model):
    pid  = models.AutoField(primary_key=True)  
    name = models.CharField(max_length=32,db_column='username',db_index=True)      
    age = models.IntegerField(null=True,blank=True,editable=False)  
    bith = models.DateTimeField(auto_now=True)  
    phone = MyCharField(max_length=11,null=True,blank=True)
    gender = models.BooleanField(default=True,choices=((True,'男'),(False,'女')))


    class Meta:
        db_table = "person"  #数据库中生成表名称 默认app名称+下划线+类名

        verbose_name = '个人信息'                # admin中显示的表名称
        verbose_name_plural = '所有用户信息'      # admin中导航栏修改

        index_together = [
            ("name", "age"),                    # 联合索引,两个存在的字段
        ]

        unique_together = (("name", "age"),)    # 联合唯一索引,不可重复

必会13条查询语句:

  • 在文件中查询引入配置:

    import os
    import django
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled.settings")
    django.setup()
    from app01 import models

  • 查询语句

    # all 获取表中所有数据,结果是对象列表:QuerySet
    ret = models.Person.objects.all()
    
    
    # filter 获取所有满足条件的数据,结果也是对象列表:QuerySet
    ret = models.Person.objects.filter(pk=1)
    
    
    # first和last 获取第一个/最后一个 拿不到值为none,结果为对象
    ret = models.Person.objects.filter().first()
    
    
    # get 获取到一个满足条件的数据,存在而且唯一,查不到或者多个报错,结果是对象
    ret = models.Person.objects.get(pk=1)
    
    
    # exclude 获取不满足条件结果,取反,结果是对象列表:QuerySet
    ret = models.Person.objects.exclude(pk=1)
    
    
    #order_by  按照字段排序,默认升序,降序为'-pid',
    ret = models.Person.objects.all().order_by('pid')
    ret = models.Person.objects.all().order_by('age','-pid') #多个字段排序,先排前面
    
    
    #reverse 只能对已经排序的QuerySet进行翻转
    ret = models.Person.objects.all().order_by('pid').reverse()
    
    
    #values  获取对象的字段名和字段值,结果是对象列表:QuerySet,列表里面为字典
    ret = models.Person.objects.all().values()
    ret = models.Person.objects.all().values('pid','name')      #可以指定字段
    ret = models.Person.objects.all().values_list('pid','name') #拿取字段值
    
    
    #distinct 只能是一样的对象才可以去重,distinct中不可填写字段
    ret = models.Person.objects.all().distinct()            #只能去重相同对象
    ret = models.Person.objects.values('age').distinct()    #这样可以去重
    
    
    #count  计数
    ret = models.Person.objects.all().count()
    
    
    # exists 判断数据是否存在,结果为布尔值
    ret = models.Person.objects.filter(pk=1).exists()
    print(ret)
    
    返回queryset:all filter exculde values(字典) values_list()  order_by reverse distinct
    返回对象:get first last
    返回布尔值:exists
    返回数字:count

  • 单表的双下划线(字段__过滤条件)
    • 大于小于

      ret = models.Person.objects.filter(pk__gt=1)    #id大于1(greater than)
      ret = models.Person.objects.filter(pk__lt=5)    #id小于5(less than)
      ret = models.Person.objects.filter(pk__gte=1)   #id大于等于1 (greater than equal)
      ret = models.Person.objects.filter(pk__lte=5)   #id小于等于5 (greater than equal)

    • 查询范围

      ret = models.Person.objects.filter(pk__range=[1,3])
      ret = models.Person.objects.filter(pk__in=[1,3,5])   #in 多个

    • 模糊查询(like),查当前字段中有g的数据

      ret = models.Person.objects.filter(name__contains='g')    #查询包含g的字段
      ret = models.Person.objects.filter(name__icontains='g')   #ignore 忽略大小写
      
      ret = models.Person.objects.filter(name__startswith='h')  #查询以h开头的
      ret = models.Person.objects.filter(name__istartswith='h') #ignore 忽略大小写
      
      ret = models.Person.objects.filter(name__endswith='g')    #查询以g结尾的
      ret = models.Person.objects.filter(name__iendswith='g')   #ignore 忽略大小写

    • 查询时间

      ret = models.Person.objects.filter(bith__year='2020')        #查看年份
      ret = models.Person.objects.filter(bith__contains='09')      #查看月份
      ret = models.Person.objects.filter(bith__contains='2019-09') #查看年月

    • 查询为空数据

      ret = models.Person.objects.filter(age__isnull=True)

外键查询:

  • 对象正向查询

    #外键的正常查询可以取到书籍是哪个出版社出版的
    
    book_obj = models.Book.objects.get(pk=1)
    print(book_obj)            #关联的对象
    print(book_obj.pub_id)       #关联的对象的id
    print(book_obj.pub.name)       #关联的对象的id,通过书籍查询到出版社

  • 对象反向查询

    pub_obj = models.Publisher.objects.get(pk=1)
    
    print(pub_obj.name)               #当前出版社
    print(pub_obj.book_set)         #关系管理对象   不指定related_name
    print(pub_obj.book_set.all())   #关联的所有对象,根据出版社ID查询书籍
    
    
    # 外键 related_name
    publisher = models.ForeignKey(to="Publisher", related_name='books')
    print(pub_obj.book.all())       #在models中指定related_name,替换book_set

  • 基于字段查询

    #表中有外键的话,直接通过外键__字段
    ret = models.Book.objects.filter(pub__name="沙河出版社")       #__跨表查询,根据出版社拿书
    
    
    #表中没有外键,直接通过表名__字段
    ret = models.Publisher.objects.filter(book__title='神雕侠侣')  #根据书拿出版社
    
    
    #指定related_name 使用类名小写,related_query_name='book' 后面覆盖前面
    ret = models.Publisher.objects.filter(books__title='神雕侠')  

外键关系管理对象:
  • SET不可用,set不能通过ID添加,只能添加对象

  • add

    #外键添加数据,修改书籍的出版社
    pub_obj = models.Publisher.objects.get(pk=1)
    pub_obj.book_set.add(*models.Book.objects.filter(pk__in=[2,4,6])) 
  • remove

    #外键删除,字段要为空
    pub_obj.book_set.remove(*models.Book.objects.filter(pk__in=[2,4,6])) 
  • clear

    pub_obj.book_set.clear()    #清空
  • create

    pub_obj.book_set.create(title='小白',pub_id=2)  #添加书籍和出版社,不填默认自己1

多对多

  • 对象正向查询(跟外键使用方法一样)

    auth_obj = models.Author.objects.get(pk=1)
    
    print(auth_obj.name)
    print(auth_obj.books)          #关联管理对象
    print(auth_obj.books.all())    #关联对象      通过作者找书籍

  • 对象反向查找(跟外键使用方法一样)

    book_obj = models.Book.objects.get(pk=1)
    
    print(book_obj.title)
    print(book_obj.author_set)         #关联管理对象
    print(book_obj.author_set.all())   #关联对象   通过书籍找作者

  • 基于字段查询

    ret = models.Book.objects.filter(author__name='俊丽')         #通过作者找书名
    
    ret = models.Author.objects.filter(books__title='少有人走的路')  #通过书名找作者

多对多关系管理对象
  • set:添加之前会删除清空

    author_obj = models.Author.objects.get(pk=1)
    
    author_obj.books.set([1,2])    #set [存放的是关联的ID,可以是多个],之前的会覆盖掉
    author_obj.books.set(models.Book.objects.filter(pk__in=[1,2]))   #set [也可以是对象]

  • add :新增新的关系,之前不变,重复添加之前的也不变

    # add添加是不会覆盖删除而是新增
    author_obj = models.Author.objects.get(pk=1)
    
    author_obj.books.add(3)     #添加ID
    author_obj.books.add(models.Book.objects.get(pk=4))            #添加对象
    author_obj.books.add(*models.Book.objects.filter(pk__in=[3,4]))  #添加多个对象,*打散

  • remove 删除

    author_obj = models.Author.objects.get(pk=1)
    
    author_obj.books.remove(2)   #跟add语法一样
    author_obj.books.remove(*models.Book.objects.filter(pk__in=[3,4]))

  • clear 清空

    # 将关系全部清空
    author_obj = models.Author.objects.get(pk=1)
    
    author_obj.books.clear()

  • create 新增

    #给作者添加书籍和出版社
    author_obj = models.Author.objects.get(pk=2)
    author_obj.books.create(title='小红书',pub_id=1)  #通过作者2对象,添加一本小红书和出版社1的id
    
    #给书籍添加作者
    book_obj = models.Book.objects.get(pk=2)
    book_obj.author_set.create(name='小黑')          #先查找书籍,在通过书籍对象反向写入作者
    

聚合

  • 引入聚合函数

    from django.db.models import Max,Min,Count,Avg,Sum
    
  • aggregate

    #统计书中最高的价格,aggregate为终止方法,后面不可在添加方法了
    ret= models.Book.objects.aggregate(max=Max('price'),min=Min('price'))
    
    #先进行筛选,选择范围之后在使用聚合函数
    ret=models.Book.objects.filter(pk__range[3,7]).aggregate(max=Max('price'),min=Min('price'))
    
    print(ret['max'])   #返回的是字典,使用key来取值
    

分组

  • 统计每一本数的作者个数

    #book:按照book_id进行分组  annotate:填写聚合函数(将count结果注释到Book中)
    
    ret = models.Book.objects.annotate(count=Count('author')).values()
    for i in ret:
        print(i)
    
  • 统计出每个出版社的最便宜书的价格 分组聚合

    # 方法一
    # 先使用出版社进行分组,annotate注释 填写聚合函数,将结果写入对象中,以出版社为准
    ret= models.Publisher.objects.annotate(min=Min('book__price')).values()
    
    for i in ret:
        print(i['min'])
    
    
    # 方法二!!!
    #values分组条件    #以书籍为准
    ret = models.Book.objects.values('pub__id').annotate(min=Min('price'))
    
    for i in ret:
        print(i['min'])
    
    
  • 统计不止一个作者的图书-筛选

    # 先分组聚合,之后进行筛选
    ret = models.Book.objects.annotate(count=Count('author')).filter(count__gt=1)
    
    for i in ret:
        print(i.title)
    
  • 根据作者数量,进行-排序

    # 先分组聚合,之后排序
    ret = models.Book.objects.annotate(count=Count('author')).order_by('-count')
    
    for i in ret:
        print(i.title)
    
  • 查询各个作者书的总价格

    #第一种方法:通过作者分组,反向取price   以作者表进行左连接
    ret = models.Author.objects.annotate(sum=Sum('books__price'))
    
    for i in ret:
        print(i.name,i.sum)
    
    
    #第二种方法:通过作者分组,在通过钱来取值   以书进行左连接,所以会出现none情况
    ret = models.Book.objects.values('author').annotate(sum=Sum('price'))
    for i in ret:
        print(i
    
    #转成sql过程
    1.先把book表和authro进行left join连表
    2.连完表之后进行group by分组
    3.之后再进行函数取值
    

F和Q

  • save和update字段区别:

    • save会全部都保存一遍

    • update会只针对查询的数据进行保存,!!!

  • F 两个字段比较,跟子查询很像

    from django.db.models import F
    
    #sale字段大于(gt)num字段的有哪些
    ret = models.Book.objects.filter(sale__gt=F('num')) 
    
    #筛选book表pk字段等于1,针对这个行数据的sale进行修改
    ret = models.Book.objects.filter(pk=1).update(sale=F('sale') *2 + 10)

  • Q 或与非

    from django.db.models import F,Q
    ret = models.Book.objects.filter(Q(pk__gt=5)|Q(pk__lt=2))  #大于5或者小于2
    
    ret = models.Book.objects.filter(~Q(pk__gt=5)&Q(pk__lt=2)) #与
    
    ret = models.Book.objects.filter(~Q(pk__gt=5)|Q(pk__lt=2)) #小于等于5或者小于2(~非)
    
    ret = models.Book.objects.filter(Q(~Q(pk__gt=5)&Q(pk__lt=2))|Q(pk__lt=2)) #设置多个匹配

事务

  • 事务把一系列操作,可能是多条的,我去执行,要么成功要么都失败,当出现问题全部都要回滚回去,保证原子性

    # 验证事务
    from django.db import transaction
    try:
        with transaction.atomic():
            book1 = models.Book.objects.get(pk=1)
            book2 = models.Book.objects.get(pk=2)
    
            book1.num -=50
            book1.save()
    
            int("sssss")  #默认是有事务,try int的错误
    
            book2.num += 50
            book2.save()
    except Exception as  e:
        print(e)
    

  • cookie就是保存在浏览器上的一组键值对,这个键值对是服务器发送出来存储在浏览器上,当浏览器在访问服务器时,会自动携带这些键值对,服务器接受后可以利用处理这些键值对

  • 为什么要有cookie:HTTP协议是没有状态的,每次请求都是独立的,不会受前面的结果影响

cookies和session区别:

  • 应为http本身是无状态的,所以使用cookies和session都是用来记录客户端的状态

  • cookies是以文本键值对存储在浏览器中,session是存储在服务端
  • cookies的存储数量有限只有4kb,而session是无限量的
  • 还有就是安全问题,我们可以轻松访问到cookie值,但是我们无法直接访问会话值,因此session更安全

原理

  • 首先客户端向服务端发送一个post请求,提交账号密码

  • 服务器校验账号密码,正确向浏览器写入登录成功状态

  • 浏览器再次请求时,会自动携带cookie的状态在发送给服务器,服务器在判断状态

  • 特性:

    • 服务器让浏览器进行设置的,键值对保存在浏览器

    • 浏览器下次访问事自动携带对应的cookie

  • 应用:

    • 登陆,记录登录状态

    • 投票,投票记录状态

    • 记录网页的浏览习惯,分页多少条数据

django中操作cookies

  • 设置

    #设置cookie  Set-Cookie: is_login=1; 添加响应头
    ret.set_cookie('is_login','1000') 
    
    
    #设置加密的cookie Set-Cookie: is_login=1
    ret.set_signed_cookie('is_login', '1000', '盐') 

  • 获取

    is_login = request.COOKIES.get('is_login')  #读取cookies
      if is_login != '1000':                  #判断是否有cookies
    
    #获取加密盐的cookie    
    request.get_signed_cookie('is_login',default='',salt='盐')   #获取不到值,设置一个默认值

  • 删除

    def logout(request):            
        ret = redirect('/login/')
        ret.delete_cookie('is_login')   #删除cookie
        return ret

参数:

  • key, 键 value='', 值

  • expires=None, IE 浏览器的超时时间

  • max_age=None 浏览器超时时间,没有设置浏览器关闭就失效

    ret.set_signed_cookie('is_login', '1000', '盐',max_age=10)  #超过10秒cookei失效!!
  • path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问

    #针对某个目录设置cookie地址,没有cookie不能访问
    ret.set_signed_cookie('is_login', '1000', '盐',path='/home/')
  • domain=None, Cookie生效的域名,默认对所有域名生效

  • secure=False, https传输 为true是有为https协议时候才传输cookei

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

Cookie应用示例:

  • 网页登录:

    from django.shortcuts import render,redirect,HttpResponse
    
    def login_required(func):
        def inner(request,*args,**kwargs):
            # is_login = request.COOKIES.get('is_login')  #读取cookies
            is_login =  request.get_signed_cookie('is_login', default='', salt='盐')
    
            if is_login != '1000':                    #没cookies执行login,url上添加当前路径
                 return redirect(f'/login/?returnurl={request.path_info}')
    
            ret = func(request,*args,**kwargs)      #执行home或者index
            return ret
        return inner
    
    
    def login(request):
         if request.method == "POST":
             user = request.POST.get('user')
             pwd = request.POST.get('password')
    
             if user == 'haiyang' and pwd =='123':          #效验登录状态
                 returnurl = request.GET.get('returnurl')   #获取是否有要跳转的地址
                 if returnurl:
                     ret = redirect(returnurl)
                 else:
                     ret = redirect('/home/')               #登录成功要调转的地址
    
        # ret.set_cookie('is_login','1000')  #设置cookie  Set-Cookie: is_login=1; 添加响应头
                 ret.set_signed_cookie('is_login', '1000', '盐',path='/home/')
                 return ret
    
             return render(request,'login.html',{'error':'用户名错误'})  #密码错误返回
         return render(request,'login.html')
    
    
    @login_required                       #访问home页面调用装饰器
    def home(request):                      #home页面
        return HttpResponse("home ok")
    
    @login_required
    def index(request):                     #index页面
        return HttpResponse("index ok")
    
    def logout(request):                  #删除cookie
        ret = redirect('/login/')
        ret.delete_cookie('is_login')
        return ret
    
    
    #前端代码
    <form action="" method="post">
        {% csrf_token %}
        <p>用户名:<input type="text" name="user"></p>
    
        <p>密码:<input type="text" name="password"></p>
    
        <p style="color: red">{{ error }}</p>
        <button>登录</button>
    </form>

session

  • session会话控制,保存在服务器上一组组键值对 ,但是必须依赖于cookie。

  • 为什么session要就与cookie,没有cookie,建立连接就生成一个session_id,那么打开几个页面就是几个session,使用cookie就可以把session_id保存在cookie中,每次访问携带

  • 为什么要用session

    • session保存在服务器上,安全

    • cookie 保存在浏览器上 不安全

    • 浏览器对cookie的大小有限制

  • session原理图

  • session流程:

    • 首先客户端向服务端发送一个post请求,提交账号密码
    • 服务器校验账号密码,效验正确,向浏览器返回
    • 浏览器再次请求时,会自动携带session_id发送给服务器,服务器通过session_id拿取到值

  • 数据库中session标识

    • session表名:django_session

      • session_id

      • session_data

      • expire_date:超时时间,默认两周

django中的操作:

  • 设置session

    request.session['is_login'] ='1000'  
    request.session['is_login1'] ='1000'      #可以设置多个键值对
    
    request.session.setdefault('k1',123)      #存在则不设置
    
    del request.session['is_login']           #删除键值对
    

  • 获取

    #两种获取方式
    request.session.get('is_login')   
    
    request.session['is_login']
    
    #可以当成字典使用
    request.session.keys()
    request.session.values()
    request.session.items()     #设置多个键值对可以都取到
    

  • 删除

    request.session.delete()   # 删除session 数据 不删除cookie
    request.session.flush()    # 删除session 数据 删除cookie
    
    
    def logout(request):            #删除session
        ret = redirect('/login/')
        request.session.flush()
        return ret
    

  • session方法:

    request.session.session_key            #拿到数据库中的key
    
    request.session.clear_expired()        #将所有Session失效日期小于当前日期的数据删除
    
    request.session.exists("session_key")  # 检查会话session的key在数据库中是否存在
    
    
    request.session.set_expiry(value)      # 设置会话Session和Cookie的超时时间
        * 如果value是个整数,session会在些秒数后失效。
        * 如果value是个datatime或timedelta,session就会在这个时间后失效。
        * 如果value是0,用户关闭浏览器session就会失效。
        * 如果value是None,session会依赖全局session失效策略。
    

  • session设置:

    #查看session配置 在settings中设置 from django.conf import global_settings
    
    #每次访问保存session,默认为flase 设置后在两周内在此访问,延长两周
    SESSION_SAVE_EVERY_REQUEST = True
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False  #关闭浏览器cookie就失效了
    
    1. 数据库Session
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
    
    2. 缓存Session
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
    SESSION_CACHE_ALIAS = 'default'                            
    
    3. 文件Session
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 
    SESSION_FILE_PATH = None                                    
    
    
    4. 缓存+数据库
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
    
    5. 加密Cookie 把Session数据放在cookie里
    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 = 1209600                 # Session的cookie失效日期(2周)(默认)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False      # 是否关闭浏览器使得Session过期(默认)
    SESSION_SAVE_EVERY_REQUEST = False     # 是否每次请求都保存Session,默认修改之后才保存(默认)
    

session代码示例:

  • 网页登录

    from django.shortcuts import render,redirect,HttpResponse
    
    def login_required(func):
        def inner(request,*args,**kwargs):
    
            is_login = request.session.get('is_login') # 通过session去数据库中拿取key
            print(is_login)
            if is_login != '1000':     #没session执行login,url上添加当前路径
                 return redirect(f'/login/?returnurl={request.path_info}')
    
            ret = func(request,*args,**kwargs)          #执行home或者index
            return ret
        return inner
    
    
    def login(request):
        request.session.clear_expired()  # 设置清除已经失效的session数据
        if request.method == "POST":
             user = request.POST.get('user')
             pwd = request.POST.get('password')
    
             if user == 'haiyang' and pwd =='123':        #效验登录状态
                 returnurl = request.GET.get('returnurl')  #获取是否有要跳转的地址
                 if returnurl:
                     ret = redirect(returnurl)
                 else:
                     ret = redirect('/home/')              #登录成功要调转的地址
    
                 #设置session,使用cookie将session_id结果传给浏览器
                 request.session['is_login'] = '1000'
                 request.session.set_expiry(10)                          #设置超时时间
                 return ret
    
             return render(request,'login.html',{'error':'用户名错误'}) #密码错误返回
        return render(request,'login.html')
    
    
    
    @login_required
    def home(request):
        return HttpResponse("home ok")
    
    @login_required
    def index(request):
        return HttpResponse("index ok")
    
    
    def logout(request):                   #删除session
        ret = redirect('/login/')
        request.session.delete()
        return ret
    

中间件

  • django的中间件是用来处理django请求和响应,框架级别的钩子(类似于装饰器,添加新的内容),他是一个轻量,低级别的插件系统,也就是,我是用插件就改变,每个中间件组件都负责做一些特定的功能

  • 添加插件,影响的是全局,谨慎使用,使用不当会影响性能,注意

  • django中间件就是一个类,五个方法,四个特点

定义中间件

  • settings 中间件配置

    • 配置中注册APP

      MIDDLEWARE = [
          'django.middleware.security.SecurityMiddleware',
          'django.contrib.sessions.middleware.SessionMiddleware', #中间件没有返回值走下面
          'app01.mymiddleware.MD1',
          'app01.mymiddleware.MD2',]
          #执行urls
          #执行视图
      

process_request

  • 执行时间:

    • 在路由前面执行
  • 参数:

    • request:process_request中的request和views中接受的请求是同一个
  • 顺序:

    • 多个process_request,按照中间件的注册顺序,顺序执行
  • 返回值:

    • None:为none是正常流程

    • HttpResponse:当前中间件中有return之后,路由匹配,视图函数都不执行了,如果有response,直接执行当前中间件process_response的方法,倒序执行之前的process_response方法,最终返回给浏览器

process_response

  • 执行时间:

    • 在视图函数后面执行
  • 参数:

    • request:process_request中的request和views中接受的请求是同一个
    • response:视图返回的response对象
  • 顺序:

    • 按照中间件的注册顺序,倒序执行
  • 返回值:

    • HttpResponse:必须return返回视图的Response对象,返回值也可以自己返回

          def process_response(self,request,response):  #response响应对象
              print("MD2 process_response")
              # return response
              return HttpResponse('ok98')
      

process_view

  • 执行时间:

    • 在路由匹配之后,视图函数之前
  • 参数:request response:

    • request 请求都是同一个
    • view_func:视图函数 (function index at 0x31435345242134)
    • view_args,:位置参数,urls中的分组信息
    • vies_kwargs:关键字参数,命名分组
  • 顺序:

    • 按照中间件的注册顺序,正序执行
  • 返回值:

    • None:正常流程

    • HttpResponse:当前中间件之后的process_view不执行了,视图函数都不执行了,倒序执行中间件中的process_response方法,最终返回给浏览器

process_exception

  • 执行时间:

    • 视图层有错误异常触发条件,执行
  • 参数:request response:

    • request 请求对象都是同一个
    • exception:错误对象
  • 顺序:

    • 按照中间件的注册顺序,在视图函数之后都是倒序执行
  • 返回值:

    • None:交给下一个中间件处理异常,所有的中间件都没有处理的话,django最后进行处理

    • HttpResponse:中间件之前的process_exception不执行了,直接执行最后一个方法,倒序执行中间件中的process_response方法,最终返回给浏览器

process_template_response

  • 执行时间:

    • 视图返回的reqponse是一个template_response 模板对象
  • 参数:request response:

    • request 请求都是同一个
    • response:响应对象
  • 顺序:

    • 按照中间件的注册顺序,倒序执行
  • 返回值:

    • HttpResponse:必须返回process_template_response对象,返回的结果,以最后一个为准,倒序执行中间件中的process_response方法,最终返回给浏览器

中间件示例

  • 代码示例

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    
    class MD1(MiddlewareMixin):
        def process_request(self,request):  #处理请求,request和views请求一样
            print("MD1 process_request")
            # return HttpResponse("md1")
    
        def process_response(self,request,response):  #response响应对象
            print("MD1 process_response")
            return response
    
        def process_view(self,request,view_func,view_args,vies_kwargs):
            # print(view_func)
            # print(view_args)
            print("MD1 process_view")
            return HttpResponse("dudu")
    
        def process_exception(self,request,exception):
            print("MD1 process_exception")
            # return None
    
        def process_template_response(self,request,response):
            print("MD1 process_template_response")
            response.template_name = "index1.html"       #替换新的页面
            response.context_data['user']='newhai'  #更改返回的数据
            return response
    
    
    class MD2(MiddlewareMixin):
        def process_request(self,request):  #处理请求,request和views请求一样
            print("MD2 process_request")
    
        def process_response(self,request,response):  #response响应对象
            print("MD2 process_response")
            return response
    
        def process_view(self,request,view_func,view_args,vies_kwargs):
            print("MD2 process_view")
            return HttpResponse("xiix")
    
        def process_exception(self,request,exception):
            print("MD2 process_exception")
            # return HttpResponse("处理完了")
    
        def process_template_response(self,request,response):
            print("MD2 process_template_response")
            response.template_name = "index1.html"         #替换新的页面
            response.context_data['user']='newhaiyang1'    #更改返回的数据
            return response
    
    
    #views
    from django.shortcuts import render,HttpResponse
    from django.template.response import TemplateResponse
    
    def index(request):
        print('indexa')
        # ret = HttpResponse("OK")
        # int("aaaaaa")
        # return ret
        return TemplateResponse(request,'index.html',{'uesr':'haiyang'})
    
    #urls
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.index),]
    
    #template
    <body>
      <h1>index{{ uesr }}</h1>
    </body>
    

转载于:https://www.cnblogs.com/haiyang11/p/11389832.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值