• Django 框架


    目录

    Django

    HTTP协议:

    Socket和Tcp协议关系:

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

    HTTP协议概述:

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

    • HTTP优点:无连接:请求和响应,无状态:访问速度快,通信标准

    • 请求格式和响应格式:

    1566383310712

    1566383326942

    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
          请求方法,路径,协议版本
        #请求行
          k1:v1
      
          k2:v2
                       #请求头
          
      
          请求数据                   #get请求美哦与请求体
      
      响应(服务端返回给浏览器的数据 respons)
      	格式:
          	"协议版本" 状态码 状态描述 
      
               k1:v1
      
               k2:v2
        
               
      
               响应数据(响应体)"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
      content-type: text/html; charset=utf-8
      
      ')
          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-admin.exe

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

    创建项目:

    • 命令行创建:

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

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

        1566393550881

    • 创建后的项目目录:

      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对象关系映射,对象和关系型数据库的对应关系,将程序中的对象保存到数据库中

      • 根据对象类型生成表结构
      • 将对象操作转为sql语句,在将sql语句查询到的结果转换为对象
      • 作用:减少了开发的工作量,不用面对数据库操作

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

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

    orm关系映射

    • str

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

    admin

    • 创建一个超级用户

    • python36 manage.py createsuperuser admin

    • 输入用户名和密码

      • 注册

      • 在app下的admin.py中写

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

    • 修改账号密码:python manage.py changepassword 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.Book.objects.filter(pub__name__contains="沙河") 
      
      
      #表中没有外键,直接通过表名__字段
      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 两个字段比较,跟子查询很像

      F两个字段进行比较(里面不能进行聚合和models,只能使用字段)

      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原理

    • 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>
      
      

    django-请求生命周期图

    • 客户端发送HTTP请求,wsgi接受http请求,将http请求封装成request,传送给process_request -->路由urls -->process_vies ---> 视图 --->process-response

    django请求流程图

    csrf

    csrf装饰器:

    • csrf_exempt,csrf_protect使用

      from django.shortcuts import render,redirect,HttpResponse
      
      from django.views.decorators.csrf import csrf_exempt,csrf_protect  #csrf装饰器
      @csrf_exempt     #设置csrf,访问不需要进行csrf校验
      @csrf_protect    #不设置csrf,必须要进行校验
      def form(request):
          return render(request,'form.html')
      
      
      #类中设置csrf
      from django.utils.decorators import method_decorator    #引入装饰器
      from django.views import View
      
      
      @method_decorator(csrf_exempt,name='dispatch')
      class From(View):
          def get(self,request):
              return render(request,'form.html')
      
          def post(self,request):
              return render(request,'form.html')
      
      

    csrf的中间件流程:

    • 查看中间件的方法:

      #找到中间件进行引用
      from django.middleware.csrf import  CsrfViewMiddleware
      
      

    • 想要能通过csrf校验的前提条件 必须要有csrftoken 的cookie

      • 第一种:{% csrf_token %} 生成token

      • 第二种:from django.views.decorators.csrf import ensure_csrf_cookie

        @method_decorator(ensure_csrf_cookie)
        def get(self,request):
        
        

    • csrf流程:

      • 从cookie获取csrftoken的值 与 POST提交的数据中的csrfmiddlewaretoken的值做对比

      • 如果从request.POST中获取不到csrfmiddlewaretoken的值,会尝试从请求头中获取x-csrftoken的值,并且拿这个值与csrftoken的值做对比,对比成功也能通过校验。

    • 源代码:

      class CsrfViewMiddleware(MiddlewareMixin):
      def _get_token(self, request):
              if settings.CSRF_USE_SESSIONS:   #第二步:判断session是否为flase
                  try:
                      return request.session.get(CSRF_SESSION_KEY)  #获取session
                  except AttributeError:
              else:
                  try: #第三部:获取浏览器cookie对应的值
                      cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] 
                  except KeyError:
                      return None
      
                  csrf_token = _sanitize_token(cookie_token) #第四步:调用_sanitize_token
                  if csrf_token != cookie_token:             #判断cookie是否匹配
                      request.csrf_cookie_needs_reset = True #cookie不相等,重新设置
                  return csrf_token  		      #第五步返回token
                  
                  
      def _sanitize_token(token):  			  #判断cookie是否符合
          if re.search('[^a-zA-Z0-9]', force_text(token)):
              return _get_new_csrf_token()      #匹配token,获取到新的token
          elif len(token) == CSRF_TOKEN_LENGTH: #匹配长度是64的直接返回
              return token
          elif len(token) == CSRF_SECRET_LENGTH:
              return _salt_cipher_secret(token)
          return _get_new_csrf_token()
      
      
      def process_request(self, request):
      	#第一步:调用_get_token函数,从cookie中获取csrftoken的cookie值
      	csrf_token = self._get_token(request)  
      	if csrf_token is not None:
      		request.META['CSRF_COOKIE'] = csrf_token  #有结果,将获取的cookie值放在请求头部信息
              
              
      def process_view(self, request, callback, callback_args, callback_kwargs):
      	#csrf_processing_done等于true 返回none view为none正常流程
          if getattr(request, 'csrf_processing_done', False): 
              return None
      	
      	#在viws中函数加入csrf_exempt,值就会给true
          #callback从函数里面拿csrf_exempt豁免,如果等于true返回none,		
      	if getattr(callback, 'csrf_exempt', False):
              return None
      	
      	#不是这些请求的话返回,是post del
      	if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
          	return self._accept(request)  #self._accept 处理完成等于true返回none,正常流程
              
              #不需要进行强制性效验
              if getattr(request, '_dont_enforce_csrf_checks', False):
              
              if request.is_secure():   #如果发的是https请求
              csrf_token = request.META.get('CSRF_COOKIE')#之前代码添加的,从字典里面获取cookie值
              
              if csrf_token is None:    #如果请求csrf_token没有值403
              	return self._reject(request, REASON_NO_CSRF_COOKIE)
      
              request_csrf_token = ""
      		if request.method == "POST": #如果是post请求
              	#获取浏览器页面post提交的数据
              	request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
      		
      		#比较加密过的数据是否效验成功
      		if not _compare_salted_tokens(request_csrf_token, csrf_token):
                      return self._reject(request, REASON_BAD_TOKEN)
      
      		#从请求头中获取x-csrf_token,赋值html post数据
      		if request_csrf_token == "":
              	request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
      	return self._accept(request)
      
      

    ajax

    • 简介:
      • JAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。

    • ajax

    • js技术,给服务发送请求。

    • 特点: 异步(发送请求,不等待结果)传输的数据量小 局部刷新(输入框返回来的不存在信息)

    • ajax请求方式:

      • 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求
      • 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求

    • 请求方式-同步请求,发送一个请求,等待响应,在发送下一个

      • 浏览器地址栏输入地址 get

      • a标签 get

      • form表单 get/post

    发送请求:

    • 发送代码示例:

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
          {% load static %}
      </head>
      
      <body>
          <input type="text" name="i1">+
          <input type="text" name="i2">=
          <input type="text" name="i3">
          <button id="b1">计算</button>
          <script src="{% static 'jquery-3.4.1.js' %}"></script>  #使用ajax 引用js
      
          <script>
              $('#b1').click(function () {
                  //发ajax请求
                  $.ajax({
                      url: '/calc/',    #发送请求地址
                      type: 'post',     #请求方式,默认get
                      data:{            #发送的数据
                          'x1':$('[name="i1"]').val(),
                          'x2':$('[name="i2"]').val(),
                      },
                      success:function (res) {   #成功响应后的回调函数,res views响应return的数据
                          {#$('[name="i3"]').val(res)#}
                          location.href=res      #前端跳转配置
                      }
                  })
              })
          </script>
      </body>
      </html>     
      
      
      #urls
      urlpatterns = [
          url(r'^calc/', views.calc),
      ]
      
      #views
      def calc(request):
          i1 = request.POST.get('x1')
          i2 = request.POST.get('x2')
          return HttpResponse(i3)     
      
      
      #html
          <button id="b2">参数的测试</button>
      		<script>
              $('#b2').click(function () {
                  //发ajax请求
                  $.ajax({
                      url: '/test/',
                      type: 'post',
                      data:{
                          'name':'haiyang',
                          'age':28,
                          {#'hobby':['台球','吃饭','俊丽']#}
                          'hobby':JSON.stringify(['台球','吃饭','俊丽'])
                      },
                      success:function (res) {
                          {#console.log(res)#}
                          console.log(res.status)
                      },
                      error:function (res) {
                          console.log(res)
                          location.href=res    #前端定义跳转月面,res为后端传送的路径
                      }
                  })
              })
      		</script>
      
      
      
      

    使用json传值

    import json
    def test(request):
    print(request.POST)
    # hobby = request.POST.getlist('hobby[]')
    hobby = json.loads(request.POST.get('hobby'))
    print(hobby,type(hobby))
    # return HttpResponse("ok")
    # int("aaa") #错误时候调用
    return JsonResponse({'status':'ok','msg':'xxx'}) #回应请求头,变成对象

      
      ​							
    
    ### 使用ajax上传文件
    
    * 上传文件代码示例;
    
      ```js
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
          {% load static %}
      </head>
      
      <body>
          <input type="file" id="f1"> <button id="b3">上传文件</button>
      
          <script src="{% static 'jquery-3.4.1.js' %}"></script>
      
          <script>
              $('#b3').click(function () {
                  {##生成form键值对, FormData默认有编码方式#}
                  var form = new FormData();
                  form.append('filname','xxx')
                  form.append('f1',$('#f1')[0].files[0])  #f1后端获取参数
      
                  //发ajax请求
                  $.ajax({
                      url: '/upload/',
                      type: 'post',
                      data:form,
                      processData:false,   // 不需要处理编码方式
                      contentType:false,   // 不需要处理contentType请求头
                      success:function (res) {
                          {#console.log(res)#}
                          console.log(res.status)
                      },
                  })
              })
          </script>
      </body>
      </html>   
         
                   
      #urls
      urlpatterns = [
          url(r'^upload/', views.upload),]
                   
      #views
      def upload(request):
          f1 = request.FILES.get('f1')
      
          with open(f1.name,'wb') as f:
              for i in f1.chunks():
                  f.write(i)
      
          return JsonResponse({'status': '上传成功'})  # 回应请求头,变成对象
          
          
      #浏览器访问信息:
      Content-Type: multipart/form-data; boundary=----WebKitFormBoundarygNy1hssp07PGlGbq
      
      Form Data
          filname: xx
          f1: (binary)             
    
    

    ajax通过django的csrf校验的方式

    • 第一种方法:给post,data中加csrfmiddlewaretoken键值对,与浏览器中获取的csrfcookie对比

      data:{
          'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val(),
          'x1':$('[name="i1"]').val(),
          'x2':$('[name="i2"]').val(),
      },
      
      #浏览器查询
      Form Data
      csrfmiddlewaretoken: VAfy6g35ZvuEh30J0wmKv9wNd02jlZkOetmPJ4PxpbQyv8IgAIXR4CnzPbzXNKT6
      
      
    • 第二种方法:data中加请求头 x-csrftoken键值对

      headers:{
          'x-csrftoken':$('[name="csrfmiddlewaretoken"]').val(),
      },
      
      
    • 自定义ajax全部添加

      #在status中创建文件,自己写一个getcookie方法
      
      function getCookie(name) {  #给cookie名字按照格式分隔开
          var cookieValue = null;
          if (document.cookie && document.cookie !== '') {
              var cookies = document.cookie.split(';');
              for (var i = 0; i < cookies.length; i++) {
                  var cookie = jQuery.trim(cookies[i]);
                  // Does this cookie string begin with the name we want?
                  if (cookie.substring(0, name.length + 1) === (name + '=')) {
                      cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                      break;
                  }
              }
          }
          return cookieValue;
      }
      var csrftoken = getCookie('csrftoken');  #调用函数,返回具体值
      
      
      function csrfSafeMethod(method) {        #请求匹配
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); #如果请求为POST 返回True
      }
      
      $.ajaxSetup({    #给全局的ajax进行配置
        beforeSend: function (xhr, settings) {
          if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);  #为post设置请求头
          }
        }
      });
      
      #在html文件中导入引用
      <script src="{% static 'ajax_setup.js' %}"></script>
      
      

    sweetalert:

    • 代码示例:

      #引入sweetalert
      <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
      
      <button id='del' class="btn btn-primary btn-danger" url='/student_del/?pk={{ obj.id }}'>删除</button>
      
      
      <script>
          $('.btn-danger').click(function () {   #获取按钮class触发
              swal({
                  title: "你确定要删除吗?",
                  text: "删除可就找不回来了哦!",
                  icon: "warning",
                  buttons: true,
                  dangerMode: true,
              })
                  .then((willDelete) => {
                      if (willDelete) {
                          $.ajax({
                              url:  $(this).attr('url'),    #获取del中url信息,传到后端
                              success: (res) => {
                                  if (res) {
                                      $(this).parent().parent().remove();
                                      swal("删除成功!", "你可以准备跑路了!", {
                                          icon: "success",
                                      });
                                  } else {
                                      swal("删除失败", "你可以再尝试一下!", "error")
                                  }
                              }
                          })
      
                      } else {
                          swal("你的选择很正确!");
                      }
                  });
          })
      </script>
      

    Form表单

    • 常用的字段:

      CharField             #input
      ChoiceField 		  #select
      MultipleChoiceField   #多选
      DecimalField		  #时间
      ModelMultipleChoiceField  #可选数据放入数据库,每次刷新页面都会重新查询
      MultipleChoiceField       #单选
      
      #具体代码见视图标注
      
    • 字段内的参数:

      initial          #初始值
      label    	     #中文提示
      error_messages   #错误提示
      min_length       #最小长度
      max_length		 #最大长度
      choices          #可选择的值
      widget           #更改插件样式 
      PasswordInput    #隐藏密码
      disabled		 #为true不可编辑
      
      #具体代码见视图标注
      

    视图:

    from django import forms
    
    def check_user(value):    #自定义效验,写函数
        if 'haiyang' in value:
            raise ValidationError('haiyang 不能注册')
    
    from django.core.validators import validate_email,RegexValidator  #内置的效验方法
    
    class RegForm(forms.Form):
    	#CharField文本输入框,disabled=True不可编辑,validators填写自定义的效验和内置的效验方法
        user = forms.CharField(label='用户名',initial='胡海洋',validators=[validate_email])
        pwd  = forms.CharField(          #PasswordInput密码框
            label='密码',                 #label 输入款前的提示
            widget=forms.PasswordInput,  #隐藏密码
            required=True,               #默认是true,为false不用填写,不校验
            min_length=6,
            max_length=12,
            error_messages={             #根据设置属性,修改错误提示
                "required":'该字段是必须要填写',
                'min_length':'密码最少是6位',
                'max_length':'长度过长'
            })
        
        re_pwd  = forms.CharField(        #PasswordInput密码框
            label='确认密码',              #label 输入款前的提示
            widget=forms.PasswordInput,   #隐藏密码
            required=True,                #默认是true,为false不用填写,不校验
            min_length=6,
            max_length=12,
            error_messages={             #根据设置属性,修改错误提示
                "required":'该字段是必须要填写',
                'min_length':'密码最少是6位',
                'max_length':'长度过长'
            })
        
        gender = forms.ChoiceField(         #ChoiceField单选
            label='性别',
            choices=((1,'男'),(2,'女'),(3,'xx')),
            widget=forms.RadioSelect        #更改样式
        )
        # hobby = forms.MultipleChoiceField(         #MultipleChoiceField多选
        #     label='爱好',
        #     initial=[1,2],                         #initial默认选择多个
        #     # choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        #     widget=forms.CheckboxSelectMultiple    #好用鼠标勾选
        # )
    
        hobby = forms.ModelMultipleChoiceField(  #多选|forms.MultipleChoiceField单选
            label='爱好',
            initial=[1,2],
            queryset=models.Hobby.objects.all(),  
            widget=forms.CheckboxSelectMultiple
        )
        birth = forms.DecimalField()
    
    
    def reg2(request):
        from_obj = RegForm()              #对象实例化
    
        if request.method == 'POST':
            from_obj = RegForm(data=request.POST)   #POST又实例化一次
            if from_obj.is_valid():       #做校验
                return HttpResponse('OK')
    
        return render(request, 'reg2.html',{'from_obj':from_obj})  #将对象传给前端
    

    模板:

    {{ form_obj.as_p }}               # 展示所有的字段
    {{ form_obj.user }}               # input框
    {{ form_obj.user.label }}         # label标签的中文提示,用户名
    {{ form_obj.user.id_for_label }}  # input框的id user
    {{ form_obj.user.errors  }}       # 一个字段的错误信息
    {{ form_obj.user.errors.0  }}     # 一个字段的第一个错误信息
    {{ form_obj.errors  }}            # 所有字段的错误
    
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    
    <body>
    <form action="" method="post" novalidate>
        {% csrf_token %}
        <p>
            <label for="{{ from_obj.user.id_for_label }}">{{ from_obj.user.label }}:</label>
            {{ from_obj.user }} <span>{{ from_obj.user.errors.0 }}</span>
        </p>
    
        <p>
            <label for="{{ from_obj.pwd.id_for_label }}">{{ from_obj.pwd.label }}:</label>
            {{ from_obj.pwd }}  <span>{{ from_obj.pwd.errors.0 }}</span>
        </p>
    
        <p>
         	<label for="{{ from_obj.re_pwd.id_for_label }}">{{ from_obj.re_pwd.label }}:</label>
            {{ from_obj.re_pwd }}  <span>{{ from_obj.re_pwd.errors.0 }}</span>
        </p>
    
        <p>
            <label for="{{ from_obj.gender.id_for_label }}">{{ from_obj.gender.label }}:</label>
            {{ from_obj.gender }}  <span>{{ from_obj.gender.errors.0 }}</span>
        </p>
    
        <p>
            <label for="{{ from_obj.hobby.id_for_label }}">{{ from_obj.hobby.label }}:</label>
            {{ from_obj.hobby }}  <span>{{ from_obj.hobby.errors.0 }}</span>
        </p>
    
        <p>
            <label for="{{ from_obj.phone.id_for_label }}">{{ from_obj.phone.label }}:</label>
            {{ from_obj.phone }}  <span>{{ from_obj.phone.errors.0 }}</span>
        </p>
    
    {#    {{ from_obj.errors }}#}
        <button>注册</button>
    
    </form>
    </body>
    </html>
    

    校验

    required     #前端输入字段是浏览器自动添加的required,post后面添加novalidate去除效验
    min_length   #校验字段长度
    
    

    自定义的校验:

    • 写函数:

      from django.core.exceptions import ValidationError
      
      def check_user(value):
      	# 通过校验   不做任何操作
      	# 不通过校验  抛出ValidationError异常 
          if 'haiyang' in value:
              raise ValidationError('haiyang 不能注册')
      
      #在user字段中使用validators=列表中填写效验函数
      user = forms.CharField(label='用户名',initial='胡海洋',validators=[check_user]) 
      

    • 使用内置的校验器:

      #RegexValidator 使用内置效验器过滤
      from django.core.validators import validate_email, RegexValidator
      
      phone = forms.CharField(validators=[RegexValidator(r'^1[3-9]d{9}$','手机号格式不正确')])
      

    • is_valid流程:

      1. is_valid()中要执行full_clean():    #第一步、第四步
         1. self.is_bound data数据是否传送 and self._errors ={}#第二步重新定义一个存放错误信息的字典 
         2. self.cleaned_data = {}         #第三步:定义一个存放有效的数据
         
         
      2. 执行self._clean_fields()           #第五步:清洗字段
         1. 先执行内置的校验和校验器的校验      #第六步:
         2. 有局部钩子,执行局部钩子
         
      3. 执行 self.clean() 全局钩子
      

    局部钩子和全局钩子

    #局部钩子对当前字段进行效验
        def clean_user(self):
        v = self.cleaned_data.get('user')
        # 局部钩子
        # 不通过校验 抛出异常
        # 通过校验   必须返回当前字段的值
        if v =='haiyang':
            raise ValidationError("不配")
        return v
    
    				
    #全局部钩子对全部字段进行效验
    from django.core.exceptions import ValidationError
    def clean(self):
        # 全局钩子
        # 不通过校验 抛出异常
        # 通过校验   必须返回所有字段的值 self.cleaned_data
        pwd = self.cleaned_data.get('pwd')
        re_pwd = self.cleaned_data.get('re_pwd')
        if pwd != re_pwd:
            self.add_error('re_pwd','两次密码不一致!')
            raise ValidationError('两次密码不一致')
        return self.cleaned_data
    
  • 相关阅读:
    mhWaveEdit 1.4.8
    FFmpeg — 屏幕录制器材
    GNOME 主题: Troll
    cGmail — 主动反省邮件
    最小化布置 Ubuntu
    GNOME Do — 疾速翻开法式和文件
    PyTone 一个控制台音乐播放器
    高恪守编辑器 VIM-把持篇(2)
    Cankiri:玲珑实用的屏幕录像机
    LiVES 0.9.6pre4
  • 原文地址:https://www.cnblogs.com/haiyang11/p/11389832.html
Copyright © 2020-2023  润新知