• Python


    Django

    安装:

    可以自己指定版本号

    pip install django==1.11.9
    

    创建项目:

    django-admin startproject 项目名称
    

    创建应用:

    python manage.py startapp 应用名称
    

    启动项目:

    python manage.py runserver 127.0.0.1:8001
    

    配置settings

    应用配置

    INSTALLED_APPS结尾添加

    应用名称.apps.App1Config'
    

    数据库配置(mysql)

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql', 
            'NAME': "库名", 
            'USER': "用户名",
            'PASSWORD': "密码",
            'HOST': "IP地址",
            'PORT': 端口号,
        }
    }
    

    项目文件init文件中配置:

    import pymysql
    pymysql.install_as_MySQLdb()
    

    静态文件配置

    1.项目根目录下创建存放静态文件的文件夹

    2.在settings文件中进行配置

    STATIC_URL = '/static/' # 别名
    STATICFILES_DIRS = [os.path.join(BASE_DIR,"静态文件的文件夹名")]
    

    静态文件引入

    <link rel="stylesheet" href="/static/css/index.css"> <!--引入css文件-->
    <script type="text/javascript" src="/static/js/jquery.js"></script> <!--引入js文件-->
    

    MVC框架

    Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求

    MTV框架

    Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:

    1. M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
    2. T 代表模板 (Template):负责如何把页面展示给用户(html)。
    3. V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
    4. URL分发器,将URL的页面请求分发给不同的View处理,View再调用相应的Model和Template

    http协议

    超文本传输协议,规定一次请求一次响应后断开连接,体现了协议的无状态,短链接特性,请求包含请求头和请求体,请求头之间 隔开,请求体使用两个 隔开。响应包含响应头和响应体

    常见的请求头,:

    user-agent:获取访问网站的浏览器

    content-type:请求的数据格式是什么

    超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。

    HTTP 请求/响应的步骤

    1. 客户端连接到Web服务器

      一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接

    2. 发送HTTP请求

      通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分 组成。

    3. 服务器接受请求并返回HTTP响应

      Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

    4. 释放连接TCP连接

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

    5. 客户端浏览器解析HTML内容

      客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

    HTTP请求方法

    1. GET

      向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,

    2. HEAD

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

    3. POST

      向指定资源提交数据,请求服务器进行处理。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。

    4. PUT

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

    5. DELETE

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

    6. TRACE

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

    7. OPTIONS

      这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。

    8. CONNECT

      HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。

    9. 注意点

      1. 方法名称是区分大小写的
      2. 当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed)
      3. 当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)
      4. HTTP服务器至少应该实现GET和HEAD方法

    GET和POST

    1. GET提交的数据会放在url后,以?分割,多个参数以&相连,提交的数据有大小限制
    2. POST提交的数据会放在HTTP包的请求体中,提交的数据没有限制

    状态码

    • 1xx:信息状态码,接受的请求正在处理
    • 2xx:成功状态码,请求正常处理完毕
    • 3xx:重定向状态码,需要后续操作完成该请求
    • 4xx:客户端错误状态码,请求含有词法错误或者无法被执行
    • 5xx:服务器错误状态码,服务器在处理某个正确请求时发生错误

    URL

    基本元素

    1. 传输协议
    2. 层级URL标记符(//,固定不变)
    3. 服务器(域名或ip地址)
    4. 端口号(HTTP的默认值80可以省略)
    5. 路径(以/区分路径中的目录名称)
    6. 查询(GET模式的窗体参数,以?为起点,以&隔开参数,以=分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
    7. 片段(以#为起点)

    wsgiref

    WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。
    作用:

    1. 按http请求协议解析数据
    2. 按http响应协议组装数据
    from wsgiref.simple_server import make_server
    from urls import dic
    def a(evention,start_response):
        path = evention["PATH_INFO"]
        start_response("200 ok",[])
        for url in dic:
            if url == path:
                ref = dic[url]()
        return [ref]
    if __name__ == '__main__':
        h = make_server("127.0.0.1",8088,a)
        h.serve_forever()
    

    jinja2

    jinja2是Flask作者开发的一个模板系统,起初是仿django模板的一个模板引擎,为Flask提供模板支持,由于其灵活,快速和安全等优点被广泛使用。

    语法

    • 控制结构 {% %} 标签
    • 变量取值 {{ }} 变量
    • 注释 {# #}

    视图

    一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。

    视图文件:views.py

    请求方法

    HTTPRequest

    print(request.path) # 纯路径
    print(request.path_info) # 纯路径
    print(request.get_full_path()) # 全部路径,不含ip地址和端口号
    print(request.META) # 请求头相关数据,是一个字典
    print(request.method) # 获取请求的类型(GET、POST)
    print(request.GET.get("a")) # 获取GTE请求的数据
    print(request.POST.get("a")) # 获取POST请求的数据
    print(request.body) # 拿到POST请求的数据bytes类型
    

    响应方法

    render

    返回页面

    from django.shortcuts import render
    return render(request,"index.html",{"data":"数据"})
    

    HTTPResponse

    返回字符串

    from django.shortcuts import render,HttpResponse
    return HttpResponse("这是首页")
    

    redirect

    重定向

    from django.shortcuts import render,redirect
    return redirect("http://www.baidu.com") # 重定向
    

    FBV

    在视图里使用函数处理请求

    from django.shortcuts import render,HttpResponse
    
    # Create your views here.
    def login(request):
        if request.method == "GET":
            return render(request,"login.html")
        else:
            if request.POST.get("username") == "lai" and request.POST.get("password") == "123":
                return render(request,"show.html")
                # return HttpResponse('登录成功')
            else:
                return render(request,"login.html")
                # return HttpResponse('失败')
    

    CBV

    在视图里使用类处理请求

    # views.py
    from django.views import View
    from django.shortcuts import render,HttpResponse
    class Login(View):
        def dispatch(self, request, *args, **kwargs): # 重写父类方法
            print("请求前的操作")
            ret = super().dispatch(request, *args,**kwargs)
            print("请求后的操作")
            return ret
        def get(self,request): # 处理get请求
            return render(request,"index.html")
        def post(self,request): # 处理post请求
            return HttpResponse("登陆成功")
    # urls.py
    from app1 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index/', views.Login.as_view()), # 路径需要使用as_view方法
    

    添加装饰器

    from django.views import View
    from django.shortcuts import render,HttpResponse
    from django.utils.decorators import method_decorator # 导入模块
    def a(obj): # 装饰器
        def b(*args,**kwargs):
            print("装饰器前")
            ret = obj(*args,**kwargs)
            print("装饰器后")
            return ret
        return b
    # 方式一
    @method_decorator(a,name="get") # get请求触发装饰器
    @method_decorator(a,name="post") # post请求触发装饰器
    class Login(View):
    # 方式二
        @method_decorator(a) # 所有请求都触发装饰器
        def dispatch(self, request, *args, **kwargs): # 重写父类方法
            print("请求前的操作")
            ret = super().dispatch(request, *args,**kwargs)
            print("请求后的操作")
            return ret
    # 方式三
        @method_decorator(a) # get 请求触发装饰器
        def get(self,request):
            return render(request,"index.html")
        def post(self,request):
            return HttpResponse("登陆成功")
    

    模板渲染

    语法

    1. 变量 {{ }}
    2. 逻辑{% %}
    3. 注释 {# #}

    变量

    jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的Python数据类型比如列表、字段、对象等。

    def get(self,request):
        num = 1
        name = "张三"
        li = [1,2,3,4]
        dic = {"1":"a","2":"b"}
        class Cl():
            count = "abc"
            def foo(self):
                print("foo")
        # return render(request,"index.html",{"num":num,"name":name,"li":li,"dic":dic,"cl":cl})
        return render(request,"index.html",locals())
    
    <h1>{{ num }}</h1>
    <h1>{{ name }}</h1>
    <h1>{{ li }}</h1>
    <h1>{{ dic.k1}}</h1>
    <h1>{{ dic.keys }}</h1>
    <h1>{{ dic.values }}</h1>
    <h1>{{ dic.items }}</h1>
    <h1>{{ Cl.count }}</h1>
    <!--通过点可以进行字典查询、属性或方法查询、索引查询-->
    

    标签

    for循环

    <!--正序-->
    {% for i in li %}
        <span>{{ i }}</span>
    {% endfor %}
    
    <!--倒序-->
    {% for i in li reversed %}
        <span>{{ i }}</span>
    {% endfor %}
    
    <!--找不到或为空时执行empty-->
    {% for i in li %}	
        <span>{{ i }}</span>
    {% empty %}
        <p>sorry,no person here</p>
    {% endfor %}
    
    <!--
    forloop.counter            当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
    forloop.counter0           当前循环的索引值(从0开始)
    forloop.revcounter         当前循环的倒序索引值(从1开始)
    forloop.revcounter0        当前循环的倒序索引值(从0开始)
    forloop.first              当前循环是不是第一次循环(布尔值)
    forloop.last               当前循环是不是最后一次循环(布尔值)
    forloop.parentloop         本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等
    -->
    {% for i in li %}
        <span>{{ forloop.counter }} {{ i }}</span>
    {% endfor %}
    

    if结构

    if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,条件两边都有空格。

    {% if num > 100 or num < 0 %}
        <p>1</p>
    {% elif num > 80 and num < 100 %}
        <p>2</p>
    {% else %}
        <p>3</p>
    {% endif %}
    

    with

    用于给一个复杂的变量起别名

    <!--方式1-->
    {% with total=business.employees.count %}
        {{ total }} <!--只能在with语句体内使用-->
    {% endwith %}
    <!--方式2-->
    {% with business.employees.count as total %}
        {{ total }}
    {% endwith %}
    

    自定义标签

    自定义标签的参数个数不限,使用前需要先引入自定义标签所在的py文件

    # 自定义add.py
    from django import template
    register = template.Library()
    @register.simple_tag
    def add(a,b,c): # 参数个数不限
        return a+b+c
    
    <!DOCTYPE html>
    {% load add %} <!--引入自定义标签,不需要加py后缀名-->
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% add  name "123" "456" %} <!--使用自定义标签-->
    </body>
    </html>
    

    过滤器

    通过使用 过滤器 来改变变量的显示。使用管道符"|"来应用过滤器。

    注意点

    1. 过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
    2. 过滤器可以接受参数
    3. 过滤器参数包含空格的话,必须用引号包起来。
    4. '|'左右不能有空格

    内置过滤器

    标签 描述
    default 设置默认值
    length 获取长度
    filesizeformat 转换值
    slice 切片
    date 格式化时间
    safe 转义html和js
    truncatechars 截取字符串
    truncatewords 截取字符串
    cut 去除指定字符
    join 拼接字符串
    timesince 将日期格式设为自该日期起的时间

    default

    设置默认值如果变量是flase或为空时使用设置的默认值

    <h1>{{ num|default:"默认值" }}</h1>
    

    length

    获取长度

    <h1>{{ li|length }}</h1>
    

    filesizeformat

    将值转换为方便读取的格式

    <h1>{{ size|filesizeformat }}</h1>
    

    slice

    切片

    <h1>{{ name|slice:"1:3" }}</h1>
    

    date

    格式化时间

    <h1>{{ date|date:"Y-m-d H:i:s" }}</h1>
    

    safe

    转义html和js

    truncatechars

    截取字符串,如果截取数小于原数长度则隐藏部分显示三个点,每个点占一个截取数

    <h1>{{ name|truncatechars:6}}</h1>
    <!--结果:abc...-->
    

    truncatewords

    在一定数量的字后截断字符串,截取的是多少个单词。

    <h1>{{ name|truncatewords:2}}</h1>
    <!--结果:ab cd ...-->
    

    cut

    对字符串去除指定字符

    <h1>{{ name|cut:"a"}}</h1>
    <!--结果:b cd ef ghi-->
    

    join

    按指定字符拼接

    <h1>{{ li|join:"+" }}</h1>
    <!--结果:1+2+3+4-->
    

    timesince

    将日期格式设为自该日期起的时间

    {{ blog_date|timesince:comment_date }}
    

    自定义过滤器

    在应用下创建一个templatetatags(固定名称)的文件夹,存放自定义的过滤器(过滤器是.py文件)

    # 过滤器add
    from django import template
    register = template.Library() # register是固定名称
    @register.filter
    def add(a,b): # 最多接受两个参数,第一个参数接受|符前的数据,第二个参数接受|符后传递的数据
        return a+b
    
    <!--使用自定义过滤器-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {{ name|add:"123" }} <!--使用自定义过滤器,可以放在if for语句中-->
    </body>
    </html>
    

    inclusion_tag

    将一个页面里的内容用函数的返回值渲染,作为一个组件加载到调用这个函数的html文件中

    # add.py文件
    from django import template
    register = template.Library()
    @register.inclusion_tag("test.html") # 
    def add(a): # 可以传入多个参数
        return {"data":a} # 返回数据
    
    <!--test.html-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    传入数据{{ data }}
    </body>
    </html>
    
    <!--index页面-->
    <!DOCTYPE html>
    {% load add %} <!--导入py文件-->
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% add "这是数据" %} <!--使用函数-->
    </body>
    </html>
    

    组件

    将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方使用

    <!--module.html-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>组件页面</h1>
    </body>
    </html>
    
    <!--index.html-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% include "module.html" %}
    </body>
    </html>
    

    母版继承

    Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让你创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。

    <!--模板页面temp.html-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div class="c1">
        这是一个模板
    </div>
    {% block m1 %} <!--预留钩子,其他使用该模板的页面可以替换内容-->
    <h1>模板页面内容</h1>
    {% endblock %} <!--结束-->
    </body>
    </html>
    
    <!DOCTYPE html>
    {% extends "temp.html" %} <!--导入模板-->
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% block m1 %} <!--定义和模板相同的钩子-->
        {{ block.super }} <!--设置模板页面和子页面内容同时存在-->
        <h1>index</h1>  <!--子页面要替换的内容-->
    {% endblock %} <!--结束-->
    </body>
    </html>
    

    路由

    urls.py

    格式

    from django.conf.urls import url
    from django.contrib import admin
    from app1 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^login/', views.login),
    ]
    

    别名和反向解析

    别名

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^index2/', views.index,                                                                                                                    ="index"), # 起别名
        url(r'^login2/', views.login,name="login"), 
    ]
    

    反向解析

    # 后端
    from django.urls import reverse # 导入模块
    def index(request):
        print(reverse("index")) 
        return render(request,"index.html")
    # 带参
    def login(request):
        print(reverse("login",args=("参数1","参数2")))
        print(reverse("login",kwargs={"参数1":"值","参数2":"值"}))
        return render(request,"login.html")
    
    <!--前端-->
    {% url '别名' "参数1" "参数2" %}
    

    分组

    无名分组

    # urls.py
    url(r'^index/(d+)/(d+)/', views.index,name='index')
    # views.py
    def index(request,m,n)  # 位置传参,位置不可变
    

    有名分组

    # urls.py
    url(r'^index/(?P<name>d+)/(?P<age>d+)/', views.index,name='index')
    # views.py
    def index(request,name,age) # 关键字传参,位置可以不固定
    

    include路由分发

    创建多个app,然后配置app,并在每个app应用文件夹下面创建urls.py文件

    # 项目的urls.py文件
    from django.conf.urls import include
    urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
    ]
    
    # app应用下的urls.py文件
    from django.conf.urls import url
    from app01 import views
    urlpatterns = [
    url(r'^index/', views.index,name='index'),
    ]
    

    url命名空间

    防止不同app内的别名相同引起冲突

    urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls',namespace='app01')),
    url(r'^app02/', include('app02.urls',namespace='app02')),
    ]
    
    # 反向解析:
    reverse('命名空间名称:别名') 
    # 例
    reverse('app01:index')
    # 页面
    {% url 'app01:index' %}
    

    orm

    models文件中创建类

    class UserInfo(models.Model): 
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=10)
        sex = models.BooleanField()
        birthday = models.DateField()
    # 所有字段默认不为空
    

    执行数据库同步命令

    python manage.py makemigrations
    python manage.py migrate
    

    表结构

    class Sex(models.Model):
        name = models.CharField(max_length=2)
        class Meta:
            db_table = "sex"  # 指定表名
            ordering = ['id']  # 设置排序方式
    class Seat(models.Model):
        name = models.CharField(max_length=10)
        class Meta:
            db_table = "stat"  # 指定表名
            ordering = ['id']  # 设置排序方式
    class Grand(models.Model):
        names = models.CharField(max_length=10)
        class Meta:
            db_table = "grand"  # 指定表
            ordering = ['id']  #设置排序方式
    class Student(models.Model):
        name = models.CharField(max_length=10)
        sex = models.OneToOneField("Sex", on_delete=models.CASCADE)  # 一对一关系
        sex=models.OneToOneField(to="Sex",to_field="id",on_delete=models.CASCADE) #完整写法
        # on_delete=models.CASCADE 设置级联删除
        grand = models.ForeignKey(to="Grand", on_delete=models.CASCADE) # 一对多
        seat = models.ManyToManyField("Seat") # 多对多关系
        class Meta:
            db_table = "student"  # 指定表名
            ordering = ['id']  #设置排序方式
    

    常用字段

    1. CharField

      字符串字段,必须要有一个参数maxlenght,用于指定该字段最大长度

    2. IntegerField

      保存一个整数

    3. DecimalField

      浮点数,必须提供两个参数,max_digits(总位数)和decimal_place(小数位数),总位数要大于小数位数

    4. AutoField

      类似于IntegerField,在添加记录时会自动增长,如果没有手动指定主键,系统会自动添加一个主键

    5. TextField

      容量很大的文本字段

    6. EmailField

      一个可以检测Email是否合法的CharField

    7. DateField

      日期字段

    8. DateTimeField

      日期时间字段

    9. ImgeField

      文件字段,检验上传对象是否是一个合法图片,当指定heigth_field和width_field时这个图片会按照提供的宽高进行保存

    10. FileField

      文件上传字段

      orm字段 数据库实际字段
      AutoField integer AUTO_INCREMENT
      BigAutoField bigint AUTO_INCREMENT
      BinaryField longblob
      BooleanField bool
      CharField varchar(%(max_length)s)
      CommaSeparatedIntegerField varchar(%(max_length)s)
      DateField date
      DateTimeField datetime
      DecimalField numeric(%(max_digits)s, %(decimal_places)s)
      DurationField bigint
      FileField varchar(%(max_length)s)
      FilePathField varchar(%(max_length)s)
      FloatField double precision
      IntegerField integer
      BigIntegerField bigint
      IPAddressField char(15)
      GenericIPAddressField char(39)
      NullBooleanField bool
      OneToOneField integer
      PositiveIntegerField integer UNSIGNED
      PositiveSmallIntegerField smallint UNSIGNED
      SlugField varchar(%(max_length)s)
      SmallIntegerField smallint
      TextField longtext
      TimeField time
      UUIDField char(32)

    参数

    1. null

      默认为False,设置为True时,将用Null在数据库中存储空值

    2. blank

      默认为False,设置为True时,该字段允许不填

    3. default

      默认值

    4. primary_key

      设置为True时,就是将这个字段设置为主键。如果未设置主键Django会自动添加一个主键

    5. unique

      设置为True时,表示该字段是唯一的

    6. db_index

      设置为True时,表示为该字段设置数据库索引

    增加

    单表增加

    # 方式1
    from app1 import models
    def index(request):
        obj = models.UserInfo(id = 1,name="张三",sex=1,birthday="2000-11-12")
        obj.save() # 翻译成sql语句,然后由pymysql发送到服务端
        return HttpResponse("123")
    # 方式2
    from app1 import models
    def index(request):
        ret = models.UserInfo.object.creat(id = 1,name="张三",sex=1,birthday="2000-11-12")
        print(ret) # 得到一个model对象
        print(ret.name)
        return HttpResponse("123")
    

    批量添加

    for i in range(1,10):
        bk_obj = models.Student(name=f"张{i}",sex=1,grand=1,seat=1)
        bk_list.append(bk_obj)
    models.Student.objects.bulk_create(bk_list)
    

    多表添加

    # 一对一、一对多
    sea = models.Seat.objects.filter(name="座位2").first() # 查询座位2对应的对象
    obj = models.Student.objects.create(
        grade_id=1, # 第一种方式,通过表指端名直接给值
        seat=sea) # 第二种方式,通过模型内变量名赋值一个对象
    # 多对多
    obj.teacher.add(*[1,1])
    obj.teacher.add(1,1)
    obj.teacher.add(对象1,对象2)
    obj.teacher.add(*[对象1,对象2])
    

    删除

    单表删除

    models.UserInfo.objects.filter(id=1).delete() # queryset对象调用
    models.UserInfo.objects.filter(id=1)[0].delete() # model对象调用
    

    多表删除

    models.Seat.objects.filter(name="座位1").delete() # 一对一
    
    models.Grade.objects.filter(name="座位2").delete() # 一对多
    
    obj = models.Student.objects.get(id=1) # 多对多
    obj.teacher.remove(1) # 删除关系表中学生id为1,教师id为1的数据不影响其他表,
    obj.teacher.clear() # 清空所有学生id为1的数据
    obj.teacher.set([1,2]) # 先清空再加入
    

    修改

    单表修改

    models.UserInfo.objects.filter(id=1).update(id = 2, name = "李四",sex = "女")
    # model对象不能调用update方法
    ret = models.UserInfo.objects.filter(id=1)[0]
    ret.name = "李四"
    ret.sex = "女"
    

    多表修改

    obj = models.Seat.objects.filter(id=4).first()
    models.Student.objects.filter(pk=3).update(
        name = "李四",
        grade_id = 2,
        seat = obj)
    

    查找

    单表查找

    models.UserInfo.objects.all()
    

    方法

    1. all

      查找全部,结果为queryset类型

      <QuerySet [<Student: Student object>, <Student: Student object>, <Student: Student object>, <Student: Student]>
      
    2. filter

      条件查询,如果查找不到数据时不会报错,返回一个空的queryset,<QuerySet []>,如果没有查询条件会查询全部数据,queryset类型数据可以继续调用filter

      models.Student.objects.filter(sex=1,name="张1")
      
    3. get

      get可以得到且只能得到一个model对象,当查不到数据时会报错,查询结果多余一个时也会报错

      models.Student.objects.get(name="张三")
      
    4. exclude

      排除匹配的项object和quereyset类型数据都能进行调用

      models.Student.objects.exclude(name="张三")
      models.Student.objects.all().exclude(name="张三")
      
    5. order_by

      排序

      models.Student.objects.all().order_by("id") # 正序
      models.Student.objects.all().order_by("-id") # 倒序
      models.Student.objects.all().order_by("-id","price") # 先按id排序如果id相同再按price排序
      models.Student.objects.all().order_by("id").reverse() # 反转排序结果,需要先排序
      
    6. count

      计数,返回结果的数量

      models.Student.objects.all().count()
      
    7. first

      获取查询结果中的第一条数据,得到一个model对象

      models.Student.objects.all().first()
      
    8. last

      获取查询结果中的最后一条条数据,得到一个model对象

      models.Student.objects.all().last()
      
    9. exists

      判断返回结果有无数据,得到布尔类型

      models.Student.objects.filter(id="999").exists()
      
    10. values

      返回queryset类型,其中数据是字典类型

      models.Student.objects.all().values()
      '''结果
      <QuerySet [{'id': 1, 'name': '张三', 'sex': 1, 'grand': 1, 'seat': 1}, {'id': 2, 'name': '张1', 'sex': 1, 'grand': 1, 'seat': 1}]
      '''
      
    11. values_list

      返回queryset类型,其中数据是数组类型

      models.Student.objects.all().values_list()
      '''结果
      <QuerySet [(1, '张三', 1, 1, 1), (2, '张1', 1, 1, 1)]
      '''
      
    12. distinct

      去重,需要配合values或values_list使用

      models.Student.objects.all().values("sex")
      

    filter双下划线查询

    1. gt

      大于

      models.Student.objects.filter(id__gt=4)
      
    2. gte

      大于等于

      models.Student.objects.filter(id__gte=4)
      
    3. lt

      小于

      models.Student.objects.filter(id__lt=4)
      
    4. lte

      小于等于

      models.Student.objects.filter(id__lte=4)
      
    5. range

      区间,大于第一个值小于第二个值

      models.Student.objects.filter(id__range=[3,6])
      
    6. contains

      模糊查询,匹配所有指定列包含指定值的数据

      models.Student.objects.filter(name__contains="张")
      
    7. icontains

      不区分大小写查找

      models.Student.objects.filter(name__icontains="ab")
      
    8. startswitch

      匹配以什么开头

      models.Student.objects.filter(name__startswith="张")
      
    9. date

      匹配日期

      models.Student.objects.filter(publish_date="2000-01-01")
      

      匹配指定年、月、日

      models.Book.objects.filter(publish_date__year='2019',publish_date__month='8',publish_date__day='1')
      
    10. isnull

      匹配指定字段为空的数据

      models.Book.objects.filter(hoby__isnull=True)
      

    跨表查找

    obj = models.Student.objects.filter(id=3)[0]
    print(obj.seat.name) # 正向查询
    obj = models.Seat.objects.filter(name="座位4")[0]
    print(obj.student.name) # 逆向查询
    obj = models.Grade.objects.filter(id=2)[0]
    print(obj.student_set.all())
    

    基于双下划线的跨表查询

    # 多对多
    obj = models.Book.objects.filter(name="论语").values("writer__name") # 正向查询
    obj = models.Writer.objects.filter(book__name="论语").values("name") # 逆向查询
    # 一对多
    obj = models.Press.objects.filter(name="黄山书社").values("book__name")
    obj = models.Book.objects.filter(press__name="黄山书社").values("name")
    

    聚合查询

    from django.db.models import Avg, Sum, Max, Min, Count
    obj = models.Book.objects.all().aggregate(a=Max("price"),m=Min("price"))
    

    分组查询

    obj = models.Book.objects.values("press_id").annotate(a=Avg("price")) # 通过book表的press_id查询
    obj = models.Book.objects.values("press__id").annotate(a=Avg("price")) # 通过press表的id查询
    obj = models.Press.objects.annotate(a=Avg("book__price")).values("name","a") # 反向查询
    

    F查询

    from django.db.models import F
    # 查询评论数大于喜欢数的书
    obj = models.Book.objects.filter(comment__gt=F("like"))
    for i in obj:
        print(i.name)
    # 每本书价格加100
    obj = models.Book.objects.all().update(
        price=F("price")+100
    )
    

    Q查询

    用作与或非时使用

    obj = models.Book.objects.filter(Q(like__gt=10)&Q(comment__lt=900)).values("name") # 与
    obj = models.Book.objects.filter(Q(like__gt=10)|Q(comment__lt=900)).values("name") # 或
    obj = models.Book.objects.filter(~Q(like__gt=10)).values("name") # 非
    

    cookie和session

    保存在用户浏览器端的键值对,向服务端发请求时会自动携
    带。

    设置cookie

    ret = redirect("/index/")
    ret.set_cookie(key="cook",value=uid,max_age=10,path="/index/")
    return ret
    # key:键
    # value:值
    # max_age:过期时间
    # path:生效页面,/全部页面生效,""当前页面生效,/index/在index页面生效
    

    获取cookie

    cook = request.COOKIES.get("cook")
    

    session

    session依赖cookie,是一种存储数据的方式

    实现本质:用户向服务器发送请求,服务器产生随机字符串并为此用户开辟一个独立的空间存放数据,当视图函数处理完毕响应客户时,将随机字符串存储在用户浏览器的cookie中

    session设置值

    request.session["uid"] = uid
    request.session["pwd"] = pwd
    

    session取值

    uid = request.session.get("uid")
    pwd = request.session["pwd"]
    
    # 其他操作
    request.session.keys()
    request.session.values()
    request.session.items()
    

    session删除

    del request.session['uid']
    

    session配置

    SESSION_COOKIE_NAME = "sessionid" #Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
    
    SESSION_COOKIE_PATH = "/" # Session的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,默认修改之后才保存
    

    django的session默认存储位置

    文件

    SESSION_ENGINE =
    'django.contrib.sessions.backends.file'
    SESSION_FILE_PATH = '/sssss/'
    

    缓存(内存)

    SESSION_ENGINE =
    'django.contrib.sessions.backends.cache'
    SESSION_CACHE_ALIAS = 'default'
    CACHES = {
    'default': {
    'BACKEND':
    'django.core.cache.backends.locmem.LocMem
    Cache',
    'LOCATION': 'unique-snowflake',
    }
    }
    

    缓存(redis)

    SESSION_ENGINE =
    'django.contrib.sessions.backends.cache'
    SESSION_CACHE_ALIAS = 'default'
    CACHES = {
    "default": {
    "BACKEND":
    "django_redis.cache.RedisCache",
    "LOCATION":
    "redis://127.0.0.1:6379",
    "OPTIONS": {
    "CLIENT_CLASS":
    "django_redis.client.DefaultClient",
    "CONNECTION_POOL_KWARGS":
    {"max_connections": 100}
    # "PASSWORD": "密码",
    }
    }
    }
    

    cookie和session的区别

    cookie是存储在客户端浏览器上的键值对,发送请求时浏览器会自动携带.

    session是一种存储数据方式,基于cookie实现,将数据存储在服务端(django默认存储到数据库)

    ajax

    特点:异步请求,局部刷新

    $("#bu").click(function () {
           var uid = $("#uid").val();
           var pwd = $("#pwd").val();
           $.ajax({
               url:"/login/", // 路径
               type:"post", // 提交类型
               data:{uid:uid,pwd:pwd},
               success:function (res) { // 执行正常自动运行匿名函数,返回结果由res接受
                   if (res==="1") {
                       location.href="/index/"
                   }else {
                       $("span").text("账号或密码错误")
                   }
               }
           })
        });
    

    csrftoken

    CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。

    解决csrf攻击的最直接的办法就是生成一个随机的csrftoken值,保存在用户的页面上,每次请求都带着这个值过来完成校验。

    form表单

    <form action="" method="post">
        {% csrf_token %}
        <input type="text" id="uid">用户名<br>
        <input type="password" id="pwd">密码<br>
        <input type="submit" value="提交">
    </form>
    

    ajax

    // 方式一 在页面任意位置添加 {% csrf_token %}
    $("#bu").click(function () {
           var uid = $("#uid").val();
           var pwd = $("#pwd").val();
           $.ajax({
               url:"/login/",
               type:"post",
               data:{
                   "uid":uid,
                   "pwd":pwd,
                   "csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val() //手动获取{% csrf_token %}转换成隐藏input标签的值
               },
               success:function (res) {console.log(res)}
           })
        });
    
    // 方式二 在页面任意位置添加 {% csrf_token %}
    $("#bu").click(function () {
           var uid = $("#uid").val();
           var pwd = $("#pwd").val();
           $.ajax({
               url:"/login/",
               type:"post",
               data:{
                   "uid":uid,
                   "pwd":pwd,
                    csrfmiddlewaretoken:"{{ csrf_token }}" // 获取csrf_token
               },
               success:function (res) {console.log(res)}
           })
        });
    
    // 方式三
    <script type="text/javascript" src="/static/js/jquery.cookie.js"></script> //导入jquery.cookie.js
    <script>
        $("#bu").click(function () {
           var uid = $("#uid").val();
           var pwd = $("#pwd").val();
           $.ajax({
                //自定制请求头
               url:"/login/",
               type:"post",
               data:{"uid":uid, "pwd":pwd,},
               headers:{"X-CSRFToken":$.cookie("csrftoken")},
               success:function (res) {console.log(res)}
           })
        });
    </script>
    

    文件上传

    form表单

    默认情况下,enctype的值是application/x-www-form-urlencoded,不能用于文件上传,只有使用了multipart/form-data,才能完整的传递文件数据。

    <form action="" method="post" enctype="multipart/form-data"> 
        {% csrf_token %}
        <input type="file" name="file">
        <input type="submit" value="提交">
    </form>
    
    file = request.FILES.get("file")
            print(file.name) # 文件名
            with open(file.name,mode="wb") as f:
                for chunk in file.chunks():
                    f.write(chunk)
    

    ajax

    {% csrf_token %}
    <input type="file" name="file">
    <button id="bu">提交</button>
    <span style="color: red">1</span>
    
    $("#bu").click(function () {
            var data = new FormData();
            var file = $("[type=file]")[0].files[0];
            data.append("file",file);
            $.ajax({
                url: "/login/",
                type: "post",
                data: data,
                processData:false, //必须设置
                contentType:false, //必须设置
                headers: {"X-CSRFToken": $.cookie("csrftoken")},
                success: function (res) {
                    console.log(res)
                }
            })
        });
    

    jsonresponse

    if request.method == "GET":
            return render(request,"login.html")
        else:
            uid = request.POST.get("uid")
            pwd = request.POST.get("pwd")
            dic = {"stats":None,"msg":None}
            print(uid)
            print(pwd)
            if uid == "1" and pwd == "1":
                dic["stats"] = 100
                dic["msg"] = "登陆成功"
            else:
                dic["stats"] = 101
                dic["mas"] = "登陆失败"
    		# dic = json.dumps(dic,ensure_ascii=False)
            # return HttpResponse(dic,content_type='application/json')
            return JsonResponse(dic) # 通过JsonResponse将数据自动序列化
        return JsonResponse(dic,safe=False) #  非字典类型需要设置safe=False
    
    $("#bu").click(function () {
            var uid = $("#uid").val();
            var pwd = $("#pwd").val();
            $.ajax({
                url: "/login/",
                type: "post",
                data: {"uid":uid,"pwd":pwd},
                headers: {"X-CSRFToken": $.cookie("csrftoken")},
                success: function (res) { 
                    // var res = JSON.parse(res);  //-- json.loads()
                    console.log(res); // 得到的结果就是反序列化的结果可以直接取值
                    if(res.stats === 100){
                        location.href="/index/"
                    }else {
                        $("span").text(res.msg)
                    }
                }
            })
        });
    

    # mysql:  
    select * from book where id=1 for update;
    
    begin;  start transaction; # 验证
    	select * from t1 where id=1 for update;
    commit
    
    rollback;
    
    # django orm
    models.Book.objects.select_for_update().filter(id=1)
    

    事务

    给函数做装饰器来使用

    from django.db import transaction
    
    @transaction.atomic
    def viewfunc(request):
        # This code executes inside a transaction.
        do_stuff()
    

    作为上下文管理器来使用,其实就是设置事务的保存点

    from django.db import transaction
    
    def viewfunc(request):
        # This code executes in autocommit mode (Django's default).
        do_stuff()
    with transaction.atomic():   #保存点
        # This code executes inside a transaction.
        do_more_stuff()
    
    do_other_stuff()
    

    transaction的其他方法

    @transaction.atomic
    def viewfunc(request):
    
      a.save()
      # open transaction now contains a.save()
      sid = transaction.savepoint()  #创建保存点
    
      b.save()
      # open transaction now contains a.save() and b.save()
    
      if want_to_keep_b:
          transaction.savepoint_commit(sid) #提交保存点
          # open transaction still contains a.save() and b.save()
      else:
          transaction.savepoint_rollback(sid)  #回滚保存点
          # open transaction now contains only a.save()
    
      transaction.commit() #手动提交事务,默认是自动提交的,也就是说如果你没有设置取消自动提交,那么这句话不用写,如果你配置了那个AUTOCOMMIT=False,那么就需要自己手动进行提交。
    

    中间件

    是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。

    默认中间件

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    

    自定义中间件方法

    process_request

    它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,而将相应对象返回给浏览器。

    from django.utils.deprecation import MiddlewareMixin
    
    
    class MD1(MiddlewareMixin):
    
        def process_request(self, request):
            print("MD1里面的 process_request")
    
    
    class MD2(MiddlewareMixin):
        def process_request(self, request):
            print("MD2里面的 process_request")
            pass
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'middlewares.MD1',  # 自定义中间件MD1,这个写的是你项目路径下的一个路径,例如,如果你放在项目下,文件夹名成为utils,那么这里应该写utils.middlewares.MD1
        'middlewares.MD2'  # 自定义中间件MD2
    ]
    

    结果:

    MD1里面的 process_request
    MD2里面的 process_request
    app01 中的 index视图
    

    多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。

    img

    process_response

    它有两个参数,一个是request,一个是response,request就是上述例子中一样的对象,response是视图函数返回的HttpResponse对象。该方法的返回值也必须是HttpResponse对象。

    from django.utils.deprecation import MiddlewareMixin
    
    
    class MD1(MiddlewareMixin):
    
        def process_request(self, request):
            print("MD1里面的 process_request")
            #不必须写return值
        def process_response(self, request, response):#request和response两个参数必须有,名字随便取
            print("MD1里面的 process_response")
            #print(response.__dict__['_container'][0].decode('utf-8')) #查看响应体里面的内容的方法,或者直接使用response.content也可以看到响应体里面的内容,由于response是个变量,直接点击看源码是看不到的,你打印type(response)发现是HttpResponse对象,查看这个对象的源码就知道有什么方法可以用了。
         return response  #必须有返回值,写return response  ,这个response就像一个接力棒一样
            #return HttpResponse('瞎搞') ,如果你写了这个,那么你视图返回过来的内容就被它给替代了
    
    class MD2(MiddlewareMixin):
        def process_request(self, request):
            print("MD2里面的 process_request")
            pass
    
        def process_response(self, request, response): #request和response两个参数必须要有,名字随便取
            print("MD2里面的 process_response") 
            return response  #必须返回response,不然你上层的中间件就没有拿到httpresponse对象,就会报错
    

    process_view

    它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用对应的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

    from django.utils.deprecation import MiddlewareMixin
    
    
    class MD1(MiddlewareMixin):
    
        def process_request(self, request):
            print("MD1里面的 process_request")
    
        def process_response(self, request, response):
            print("MD1里面的 process_response")
            return response
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-" * 80)
            print("MD1 中的process_view")
            print(view_func, view_func.__name__) #就是url映射到的那个视图函数,也就是说每个中间件的这个process_view已经提前拿到了要执行的那个视图函数
            #ret = view_func(request) #提前执行视图函数,不用到了上图的试图函数的位置再执行,如果你视图函数有参数的话,可以这么写 view_func(request,view_args,view_kwargs) 
            #return ret  #直接就在MD1中间件这里这个类的process_response给返回了,就不会去找到视图函数里面的这个函数去执行了。
    
    class MD2(MiddlewareMixin):
        def process_request(self, request):
            print("MD2里面的 process_request")
            pass
    
        def process_response(self, request, response):
            print("MD2里面的 process_response")
            return response
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-" * 80)
            print("MD2 中的process_view")
            print(view_func, view_func.__name__)
    

    process_view方法是在process_request之后,reprocess_response之前,视图函数之前执行的,执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的

    img

    process_exception

    这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

    from django.utils.deprecation import MiddlewareMixin
    
    
    class MD1(MiddlewareMixin):
    
        def process_request(self, request):
            print("MD1里面的 process_request")
    
        def process_response(self, request, response):
            print("MD1里面的 process_response")
            return response
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-" * 80)
            print("MD1 中的process_view")
            print(view_func, view_func.__name__)
    
        def process_exception(self, request, exception):
            print(exception)
            print("MD1 中的process_exception")
    
    
    class MD2(MiddlewareMixin):
        def process_request(self, request):
            print("MD2里面的 process_request")
            pass
    
        def process_response(self, request, response):
            print("MD2里面的 process_response")
            return response
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-" * 80)
            print("MD2 中的process_view")
            print(view_func, view_func.__name__)
    
        def process_exception(self, request, exception):
            print(exception)
            print("MD2 中的process_exception")
    

    img

    process_template_response

    process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。

    class MD1(MiddlewareMixin):
    
        def process_request(self, request):
            print("MD1里面的 process_request")
    
        def process_response(self, request, response):
            print("MD1里面的 process_response")
            return response
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-" * 80)
            print("MD1 中的process_view")
            print(view_func, view_func.__name__)
    
        def process_exception(self, request, exception):
            print(exception)
            print("MD1 中的process_exception")
            return HttpResponse(str(exception))
    
        def process_template_response(self, request, response):
            print("MD1 中的process_template_response")
            return response
    
    
    class MD2(MiddlewareMixin):
        def process_request(self, request):
            print("MD2里面的 process_request")
            pass
    
        def process_response(self, request, response):
            print("MD2里面的 process_response")
            return response
    
        def process_view(self, request, view_func, view_args, view_kwargs):
            print("-" * 80)
            print("MD2 中的process_view")
            print(view_func, view_func.__name__)
    
        def process_exception(self, request, exception):
            print(exception)
            print("MD2 中的process_exception")
    
        def process_template_response(self, request, response):
            print("MD2 中的process_template_response")
            return response
    

    视图函数执行完之后,立即执行了中间件的process_template_response方法,顺序是倒序,先执行MD2的,在执行MD1的,接着执行了视图函数返回的HttpResponse对象的render方法,返回了一个新的HttpResponse对象,接着执行中间件的process_response方法。

    生命周期

    wsgi, 他就是socket服务端,用于接收用户请求并将请求进行初次封装,然后将请求交给web框架(Flask、Django)

    中间件,帮助我们对请求进行校验或在请求对象中添加其他相关数据,例如:csrf、request.session

    路由匹配

    视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:orm、templates => 渲染

    中间件,对响应的数据进行处理。

    wsgi,将响应的内容发送给浏览器。

    Form

    功能:

    ​ 生成页面可用的HTML标签

    ​ 对用户提交的数据进行校验

    ​ 保留上次输入内容

    生成标签

    视图中导入forms模块

    from django import forms
    class User(forms.Form):
        username = forms.CharField(
            label="用户名", # 自定义label名,默认为当前字段名
            widget=forms.TextInput(attrs={"class":"c1"}) # 设置插件和属性 默认TextInput
        )
    
    <div>
        {{ u_obj.username.label }}{{ u_obj.username }}
    </div>
    
     writers = forms.ModelChoiceField(queryset=models.Writer.object.all)
     # 模型中定义str方法返回name
    

    常用字段与插件

    initial

    初始值,input框里面的初始值

    username = forms.CharField(
            label="用户名",
            initial="abcd",)
    

    error_messages

    重新定义错误信息

    sername = forms.CharField(
            label="用户名",
            required=True, # 不能为空
            error_messages={
                "required":"不能为空",
            }
        )
    

    password

    密码框

     password = forms.CharField(
            label="密码",
            widget=forms.widgets.PasswordInput(render_value=True)) # 通过render_value=True设置提交出错后保留数据
    

    radioSelect

    sex = forms.ChoiceField(
            label="性别",
            choices=((1,"女"),(2,"男")), # 1:values值,女:显示值
            widget=forms.RadioSelect)
    

    Select

    sex = forms.ChoiceField(
            label="性别",
            choices=((1,"女"),(2,"男")),
            widget=forms.Select, # 默认值 单选
            widget=forms.SelectMultiple, # 多选
        )
    

    checkbox

    hobby = forms.ChoiceField(
            label="爱好",
            choices=((1,"抽烟"),(2,"喝酒")),
            widget=forms.CheckboxSelectMultiple, # 多选
            widget=forms.CheckboxInput, # 单选
        )
    

    date

    date = forms.DateField(
            label = "日期",
            widget=forms.DateInput(attrs={"type":"date"}) # 手动设置属性
        )
    

    内置字段

    Field
        required=True,               是否允许为空 默认为True
        widget=None,                 HTML插件
        label=None,                  用于生成Label标签或显示内容
        initial=None,                初始值
        help_text='',                帮助信息(在标签旁边显示)
        error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
        validators=[],               自定义验证规则
        localize=False,              是否支持本地化
        disabled=False,              是否可以编辑
        label_suffix=None            Label内容后缀
     
     
    CharField(Field)
        max_length=None,             最大长度
        min_length=None,             最小长度
        strip=True                   是否移除用户输入空白
     
    IntegerField(Field)
        max_value=None,              最大值
        min_value=None,              最小值
     
    FloatField(IntegerField)
        ...
     
    DecimalField(IntegerField)
        max_value=None,              最大值
        min_value=None,              最小值
        max_digits=None,             总长度
        decimal_places=None,         小数位长度
     
    BaseTemporalField(Field)
        input_formats=None          时间格式化   
     
    DateField(BaseTemporalField)    格式:2015-09-01
    TimeField(BaseTemporalField)    格式:11:12
    DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
     
    DurationField(Field)            时间间隔:%d %H:%M:%S.%f
        ...
     
    RegexField(CharField)
        regex,                      自定制正则表达式
        max_length=None,            最大长度
        min_length=None,            最小长度
        error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
     
    EmailField(CharField)      
        ...
     
    FileField(Field)
        allow_empty_file=False     是否允许空文件
     
    ImageField(FileField)      
        ...
        注:需要PIL模块,pip3 install Pillow
        以上两个字典使用时,需要注意两点:
            - form表单中 enctype="multipart/form-data"
            - view函数中 obj = MyForm(request.POST, request.FILES)
     
    URLField(Field)
        ...
     
     
    BooleanField(Field)  
        ...
     
    NullBooleanField(BooleanField)
        ...
     
    ChoiceField(Field)
        ...
        choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
        required=True,             是否必填
        widget=None,               插件,默认select插件
        label=None,                Label内容
        initial=None,              初始值
        help_text='',              帮助提示
     
     
    ModelChoiceField(ChoiceField)
        ...                        django.forms.models.ModelChoiceField
        queryset,                  # 查询数据库中的数据
        empty_label="---------",   # 默认空显示内容
        to_field_name=None,        # HTML中value的值对应的字段
        limit_choices_to=None      # ModelForm中对queryset二次筛选
         
    ModelMultipleChoiceField(ModelChoiceField)
        ...                        django.forms.models.ModelMultipleChoiceField
     
     
         
    TypedChoiceField(ChoiceField)
        coerce = lambda val: val   对选中的值进行一次转换
        empty_value= ''            空值的默认值
     
    MultipleChoiceField(ChoiceField)
        ...
     
    TypedMultipleChoiceField(MultipleChoiceField)
        coerce = lambda val: val   对选中的每一个值进行一次转换
        empty_value= ''            空值的默认值
     
    ComboField(Field)
        fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                                   fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
     
    MultiValueField(Field)
        PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
     
    SplitDateTimeField(MultiValueField)
        input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
        input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
     
    FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
        path,                      文件夹路径
        match=None,                正则匹配
        recursive=False,           递归下面的文件夹
        allow_files=True,          允许文件
        allow_folders=False,       允许文件夹
        required=True,
        widget=None,
        label=None,
        initial=None,
        help_text=''
     
    GenericIPAddressField
        protocol='both',           both,ipv4,ipv6支持的IP格式
        unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
     
    SlugField(CharField)           数字,字母,下划线,减号(连字符)
        ...
     
    UUIDField(CharField)           uuid类型
    
    

    字段校验

    from django import forms
    class User(forms.Form):
        username = forms.CharField(
            label="用户名",
            required=True,
            error_messages={
                    "required":"不能为空",
            }
        )
        password = forms.CharField(
            label="密码",
            widget=forms.widgets.PasswordInput, 
            )
    def index(request):
        if request.method=="GET":
            u_obj = User()
            return render(request, "index.html", {"u_obj": u_obj})
        else:
            u_obj = User(request.POST)
            if u_obj.is_valid(): # 调用is_valid方法进行校验
                print(u_obj.cleaned_data) # 校验通过的信息
            else:
                print(u_obj.errors) # 校验失败的信息
                return render(request,"index.html",{"u_obj":u_obj})
    
    <form action="" method="post" novalidate>
        {% csrf_token %}
        <div>
            {{ u_obj.username.label }} {{ u_obj.username }} {{ u_obj.username.errors.0 }} <!--显示错误-->
        </div>
        <div>
            {{ u_obj.password.label }} {{ u_obj.password }} {{ u_obj.password.errors.0 }} <!--显示错误-->
        </div>
        <input type="submit" >
    </form>
    

    正则校验

    from django import forms
    from django.core.validators import RegexValidator
    class User(forms.Form):
        username = forms.CharField(
            label="用户名",
            required=True,
            error_messages={
                "required":"不能为空",
            },
            validators=[RegexValidator(r"^a","必须以a开头"),RegexValidator(r"$c","必须以c结尾")]
        )
    

    自定义校验函数

    from django import forms
    from django.core.exceptions import ValidationError
    import re
    def custom_verify(value):
        verify_re = re.compile(r"^a")
        if not verify_re.match(value):
            raise ValidationError("必须以a开头")
    
    class User(forms.Form):
        username = forms.CharField(
            label="用户名",
            required=True,
            error_messages={
                "required":"不能为空",
            },
            validators=[custom_verify,]
        )
    

    局部钩子

    先执行username字段内的校验,当校验完成后,cleaned_data中值就存在了,然后再校验局部钩子,然后是password字段内的校验

    class User(forms.Form):
        username = forms.CharField(
            label="用户名",
            required=True,
            error_messages={
                "required":"不能为空",
            },
        )
        password = forms.CharField(
            label="密码",
            widget=forms.widgets.PasswordInput, # 默认TextInput
            )
        def clean_username(self):
            value = self.cleaned_data.get("username")
            if "xx" in value:
                raise ValidationError("不能含有xx")
            else:
                return value
    

    全局钩子

    class User(forms.Form):
        username = forms.CharField(
            label="用户名",
            required=True,
            error_messages={
                "required":"不能为空",
            },
        )
        password = forms.CharField(
            label="密码",
            widget=forms.widgets.PasswordInput, # 默认TextInput
            )
        def clean(self):
            username = self.cleaned_data.get("username")
            password = self.cleaned_data.get("password")
            if "xx" in username and "xx" in password:
                self.add_error("password","不能含有xx")
            else:
                return self.cleaned_data
    

    批量添加属性

    class User(forms.Form):
        username = forms.CharField(
            label="用户名",
            required=True,
            error_messages={
                "required":"不能为空",
            },
        )
        password = forms.CharField(
            label="密码",
            widget=forms.widgets.PasswordInput, )
        def __init__(self,*args,**kwargs):
            super().__init__(*args,**kwargs)
            for name,field in self.fields.items():
                field.widget.attrs.update({"class":"form-control"})
    
    <form action="" method="post" novalidate>
        {% csrf_token %}
        {% for foo in u_obj %}
            {{ foo.label }}
            {{ foo }}
            <span>{{ foo.errors.0 }}</span>
        {% endfor %}
    
        <input type="submit" >
    </form>
    

    ModelForm

    from django import forms
    class BookModlForm(forms.ModelForm):
        class Meta:
            model = models.Books
            fields = "__all__"  # 指定将所有字段
            labels = { # 分别指定labe名
                "name": "书名",
                "price": "价格",
                "date": "日期",
                "press": "出版社",
                "writer": "作者",}
            widgets = { # 指定类型
                "date": forms.DateInput(attrs={"type": "date"})}
            error_messages = { # 指定 错误信息
                "name":{"required": "不能为空",},
                "price":{"required": "不能为空",},
                "date":{"required": "不能为空",},
                "press":{"required": "不能为空",},
                "writer":{"required": "不能为空",},}
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            for name, field in self.fields.items():
                field.widget.attrs.update({"class": "form-control"}) # 统一设置样式
    
    # 添加
    def add(request):
        if request.method == "GET":
            book_model = BookModlForm()
            return render(request, "add.html", {"book_model": book_model})
        else:
            book_model = BookModlForm(request.POST)
            if book_model.is_valid(): # 开始校验
                book_model.save() # 保存
                return redirect("/show/")
            else:
                return render(request, "add.html", {"book_model": book_model})
    
    <!--添加页面-->
    <form method="post" action="/add/" novalidate>
            {% csrf_token %}
            {% for foo in book_model %}
                {{ foo.label }}
                {{ foo }}
                <div><span style="color: red">{{ foo.errors.0 }}</span></div>
            {% endfor %}
        <button type="submit" class="btn btn-default">添加</button>
    </form>
    
    # 修改
    def update(request, uid):
        book = models.Books.objects.filter(id=uid)[0]
        if request.method == "GET":
            book_model = BookModlForm(instance=book)
            return render(request, "update.html", {"book_model":book_model})
        else:
            book_model = BookModlForm(request.POST,instance=book) # 指定更新哪条数据
            if book_model.is_valid():
                book_model.save()
                return redirect("/show/")
            else:
                return render(request, "update.html", {"book_model": book_model})
    
    # 修改页面
    <form method="post" action="">
            {% csrf_token %}
            {% for foo in book_model %}
                <label for="{{ foo.id_for_label }}">{{ foo.label }}</label>
                {{ foo }}
            {% endfor %}
            <button type="submit" class="btn btn-default">添加</button>
    </form>
    

    跨域和同源

    同源机制:域名、协议、端口号相同即为同源

    # 项目s1
    def index(request):
        date = {"name":"app1"}
        ret = JsonResponse(date)
        ret['Access-Control-Allow-Origin'] = "http://127.0.0.1:8001" # 设置响应头
        return ret
    
    <!--项目s2-->
    <script>
        $("#bt").click(function () {
            $.ajax({
                url:"http://127.0.0.1:8000/index/",
                type:"post",
                data:{"a":"1"},
                success(res){
                    console.log(res)
                }
            })
        })
    </script>
    

    CORS

    CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

    整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

    因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

    浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

    只要同时满足以下两大条件,就属于简单请求

    (1) 请求方法是以下三种方法之一:(也就是说如果你的请求方法是什么put、delete等肯定是非简单请求)
    HEAD
    GET
    POST
    (2)HTTP的头信息不超出以下几种字段:(如果比这些请求头多,那么一定是非简单请求)
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain,也就是说,如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。
    

    凡是不同时满足上面两个条件,就属于非简单请求

    复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
    
    “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
    
    “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
    res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001'
    res['Access-Control-Allow-Headers'] = 'content-type'
    # res['Access-Control-Allow-Methods'] = 'PUT'
    # res['Access-Control-Allow-Origin'] = '*'
    
  • 相关阅读:
    使用socket BPF/Linux内核工程导论——网络:Filter(LSF、BPF、eBPF)
    使用Iperf工具测试android系统网络wifi的吞吐量wifithrougput
    html input中 button和submit的区别
    Linux转发性能评估与优化(转发瓶颈分析与解决方案)
    交叉编译器 arm-linux-gnueabi 和 arm-linux-gnueabihf 的区别
    MySQL查询不区分大小写的sql写法
    Docker镜像保存save、加载load
    将Spring-boot应用部署到Docker容器
    Docker 安装使用
    Scalatra文件下载时中文乱码
  • 原文地址:https://www.cnblogs.com/zgboy/p/13483005.html
Copyright © 2020-2023  润新知