zoukankan      html  css  js  c++  java
  • 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
    
  • 相关阅读:
    分层图最短路(DP思想) BZOJ2662 [BeiJing wc2012]冻结
    动态规划 BZOJ1925 地精部落
    线性DP SPOJ Mobile Service
    线性DP codevs2185 最长公共上升子序列
    数位DP POJ3208 Apocalypse Someday
    线性DP POJ3666 Making the Grade
    杨氏矩阵 线性DP? POJ2279 Mr.Young's Picture Permutations
    tarjan强连通分量 洛谷P1262 间谍网络
    树链剖分 BZOJ3589 动态树
    二分图 BZOJ4554 [Tjoi2016&Heoi2016]游戏
  • 原文地址:https://www.cnblogs.com/haiyang11/p/11389832.html
Copyright © 2011-2022 走看看