一篇博客搞定Django基础

web框架本质

一. scoket 回顾

1.TCP/UDP协议

TCP:一种面向连接的、可靠的、传输层通信协议(比如:打电话);优点:可靠,稳定,传输完整稳定,不限制数据大小;缺点:慢,效率低,占用系统资源高,一发一收都需要对方确认
应用:Web浏览器,电子邮件,文件传输,大量数据传输的场景

服务端:七步: 创建对象–>绑定ip和端口号–>开启监听–>三次握手–>处理收发逻辑–>四次挥手–>退还端口

import socket
#1.创建对象
sk = socket.socket()
#2.绑定ip和端口
sk.bind(("127.0.0.1",9001))
#3.开启监听
sk.listen()
#4.三次握手
conn,addr = sk.accept()
#5.处理收发数据逻辑
res = conn.recv(1024)
print(res.decode('utf-8'))
#6.四次挥手
conn.close()
#7.退还端口
sk.close()

客户端:四步:创建对象–>建立连接–>处理收发消息逻辑–>关闭连接

import socket
#1.创建对象
sk = socket.socket()
#2.建立连接
sk.connect(('127.0.0.1',9001))
#3.处理收发消息逻辑
sk.send('你好'.encode())
#4.关闭连接
sk.close()

UDP:一种无连接的,不可靠的传输层通信协议(比如:发短信); 优点:速度快,可以多人同时聊天,耗费资源少,不需要建立连接; 缺点:不稳定,不能保证每次数据都能接收到
应用:IP电话,实时视频会议,聊天软件,少量数据传输的场景

服务端:四步:创建对象–>绑定ip和端口–>收发消息(一开始只能收)–>关闭连接

import socket
#1.创建对象
sk = socket.socket(type=socket.SOCK_DGRAM)
#2.绑定ip和端口
sk.bind(('127.0.0.1',9002))
#3.收发消息
msg,cli_addr = sk.recvfrom(1024)
print(msg.decode())
sk.sendto('你好'.encode(),cli_addr)
#4.关闭连接
sk.close()

客户端:三步:创建对象–>发收消息–>关闭连接

import socket
#1.创建对象
sk = socket.socket(type=socket.SOCK_DGRAM)
#2.收发消息
sk.sendto('hello'.encode(),('127.0.0.1',9002))
msg,server_addr = sk.recvfrom(1024)
print(msg.decode())
#3.关闭连接
sk.close()

二.HTTP协议

HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准 .

1.http工作原理

  1. 客户端连接到Web服务器
  2. 发送HTTP请求
  3. 服务器接受请求并返回HTTP响应
  4. 释放连接TCP连接
  5. 客户端浏览器解析HTML内容

2.请求方法和响应方法

请求:

在这里插入图片描述
​ 请求方法:GET方法:读取数据 POST方法…

响应:

在这里插入图片描述
​ 状态码:1xx~4xx:

​ 1xx消息——请求已被服务器接收,继续处理

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

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

​ 4xx请求错误——请求含有词法错误或者无法被执行

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

三.自定义的web框架

自定义的web框架基于HTTP1.1和TCP主要是处理收发逻辑和如何连接到数据库.

1.多函数的自定义web框架

分析: 输入127.0.0.1:9000 —> 浏览器发送get请求 —> get 路径 HTTP1.1\r\n… —> 服务端答应 —> HTTP/1.1 200 ok\r\n\r\n 响应正文

import socket,time
#######socket---tcp连接#########################################
sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()
#######回消息的函数###############################################
def html(coon):     #动态页面
    cur_time = time.strftime("%Y-%m-%d %H:%M:%S") #是字符串类型,如果是=时间戳是浮点型
    with open('home.html',mode='r',encoding='utf-8') as f:
        data = f.read()
    data = data.replace("xxoo",cur_time).encode('utf-8')
    conn.send(data)
    conn.close()

def ico(conn):
    with open('logo.ico','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def css(conn):
    with open('test.css','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def jpg(conn):
    with open('bg.jpg','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def login(conn):
    with open('login.html','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def js(conn):
    with open('test.js','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

def zhuce(conn):
    with open('zhuce.html','rb') as f :
        data = f.read()
    conn.send(data)
    conn.close()

urlpatterns = [
    ('/',html),
    ('/logo.ico',ico),
    ('/test.css',css),
    ('/bg.jpg',jpg),
    ('/login.html',login),
    ('/test.js',js),
    ('/zhuce.html',zhuce)
]

while True:
    conn,addr = sk.accept()
    from_browser_msg = conn.recv(1024)
    print(from_browser_msg)
    #从请求中获取路径
    path = from_browser_msg.decode().split(' ')[1]
    #发送响应的状态行
    conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
    #发送响应正文
    for i in urlpatterns:
        if path == i[0]:
            i[1](conn)

sk.close()

home.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" href="logo.ico">
    <link rel="stylesheet" href="test.css">
</head>
<body class="c1">
    <h1 style="color: red ">欢迎来到祖安</h1>
    <h2 style="color: red ">你访问的时间是xxoo</h2>
    <button id="d1">登录</button>
    <a href="http://127.0.0.1:9000/zhuce.html" style="color: yellow">注册</a>
</body>
<script src="test.js"></script>
</html>

test.css文件:

.c1{
    background: url('bg.jpg') no-repeat ;
    background-size:100%,100%;
}

test.js文件:

var d = document.getElementById('d1')
d.onclick = function () {
    window.location.href="http://127.0.0.1:9000/login.html"
}

login.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:9000/login" method="post">
    用户名<input type="text">
    密码 <input type="password">
    <input type="submit">
</form>
</body>
</html>

zhuce.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 style="color: red">不会吧不会吧,不会真有人想当祖安人吧</h1>
</body>
</html>

2.wsgiref模块定义的web框架

**分析:**wsgiref模块封装了socket模块,用法如下:

from wsgiref.simple_server import make_server
def application(environ, start_response):
     '''
    environ: 是全部加工好的请求信息,加工成了一个字典,通过字典取值的方式就能拿到很多你想要拿到的信息
    start_response: 帮你封装响应信息的(响应行和响应头)
    :return: 需要发送的数据
    '''
    path = environ['PATH_INFO']  #获取路径
    method = environ['REQUEST_METHOD'] #获取请求方法
    content_length = int(environ.get('CONTENT_LENGTH',0)) #获取请求数据的长度
    request_data = environ['wsgi.input'].read(content_length) #post请求读取请求数据的方式 结果为字节流.decode()
    '''
    写逻辑
    data = ...
    '''
    start_response('200 OK', [('a','1'),])
    return [data]
httpd = make_server('127.0.0.1', 8001, application) #类似socketserver
httpd.serve_forever() #监听请求

**补充:**from urllib.parse import parse_qs , parse_qs方法用来解析数据,用法如下:

from urllib.parse import parse_qs
print('请求数据为', data) #请求数据为 username=asdf&password=asdf
data = parse_qs(data)
print('格式化之后的数据',data) #格式化之后的数据 {'username': ['asdf'], 'password': ['asdf']}
uname = data.get('username') #通过字典可以直接取出
pwd = data.get('password')

基于上述两个模块可以改造上面的web框架并连接到数据库,一个完整的web框架就完成了

主程序:

import time
from urllib.parse import parse_qs                   #parse : url的解析,合并,编码,解码
from wsgiref.simple_server import make_server       #按规范封装了socket
from auth import check

def html(environ):
    cur_time = time.strftime("%Y-%m-%d %H:%M:%S")          #是字符串类型,如果是=时间戳是浮点型
    with open('home.html',mode='r',encoding='utf-8') as f:
        data = f.read()
    data = data.replace("xxoo",cur_time).encode('utf-8')
    return data

def ico(environ):
    with open('logo.ico','rb') as f :
        data = f.read()
    return data

def css(environ):
    with open('test.css','rb') as f :
        data = f.read()
    return data

def jpg(environ):
    with open('bg.jpg','rb') as f :
        data = f.read()
    return data

def login(environ):
    if environ['REQUEST_METHOD'] == 'GET':
        with open('login.html','rb') as f :
            data = f.read()
        return data
    else:
        content_length = int(environ.get('CONTENT_LENGTH', 0))
        data = environ['wsgi.input'].read(content_length).decode('utf-8')
        data = parse_qs(data)
        uname = data.get('username')
        pwd = data.get('password')
        staut = check(uname,pwd)
        if staut:
            with open('login_suc.html','rb') as f :
                data = f.read()
                return data
        else:
            return '登录失败'.encode('gbk')

def js(environ):
    with open('test.js','rb') as f :
        data = f.read()
    return data

def zhuce(environ):
    with open('zhuce.html','rb') as f :
        data = f.read()
    return data

urlpatterns = [
    ('/',html),
    ('/logo.ico',ico),
    ('/test.css',css),
    ('/bg.jpg',jpg),
    ('/login.html',login),
    ('/test.js',js),
    ('/zhuce.html',zhuce),
    ('/login',login)
]

def application(environ,start_response):
    path = environ['PATH_INFO']
    start_response('200 OK',[('a','1'),])
    for i in urlpatterns:
        if i[0] == path:
            data = i[1](environ)
            break
    else:
        data = b'404 not found'
    return [data]
httpd = make_server('192.168.13.26',9000,application)
httpd.serve_forever()

auth.py 连接数据库程序:

import pymysql
def check(uname,pwd):
    conn = pymysql.connect(host='127.0.0.1',user='root',password='',database='db0913',port=3306)
    cursor = conn.cursor()
    sql = 'select * from t1 where id = %s and name = %s'
    res = cursor.execute(sql,(uname,pwd))
    return res

login_suc.html 登录成功界面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 style="color: yellow">宝贝儿,恭喜你登陆成功</h1>
</body>
</html>

3.jinja2 模板渲染

用于动态界面,原理字符串替换
传送门

4.MVC和MTV框架

MVC模式:

模型(M) ,控制器© ,视图(V) ,
在这里插入图片描述
MTV模式:

  • M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。

  • T 代表模板 (Template):负责如何把页面展示给用户(html)。

  • V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template

    还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template
    在这里插入图片描述

5.Django安装

pip3 install django==1.11.9

Django框架

一.url和views

1.用函数写views.py(FBV)

urls.py写法:1.分为命名分组和无命名分组,2.与views中函数一一对应.具体如下:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^book/(?P<year>\d+)/(?P<month>\d+)',views.book),
    url(r'^li1/(\d+)/(\d+)',views.li1)
]

**views.py写法:**1.第一个参数是request对象,2.返回的三种方式:HttpResponse,render,redirect.具体如下:

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.
def book(request,year,month):
    '''
    request.GET   #包含所有HTTP  GET参数的类字典对象 http://127.0.0.1:8000/index/?xx=oo -- <QueryDict: {'xx': ['oo']}>
	request.POST  #包含所有HTTP POST参数的类字典对象 <QueryDict: {'uname': ['asf'], 'pwd': ['asdf']}>
	request.method #请求方法
	request.path   #请求路径
	request.path_info  #返回用户访问url,不包括域名
	request.get_full_path() # /index/?xx=oo  路径+查询参数
	request.body # b'uname=asf&pwd=asdf'
    '''
    #return HttpResponse('XXX')  #返回xxx
    #return render(request,'book.html',{'title':'xx'}) #返回html页面
    return redirect('/li1/1/2')  #重定向
def li1(request,n,m):
    return HttpResponse('li1'+str(m)+str(n))

2.用类写views.py(CBV)

urls.py写法:用views.类.as_view().具体如下:

url(r'^book1/(\d+)/(\d+)',views.Book.as_view()),

views.py写法:继承View.具体如下:

from django.views import View
class Book(View):
    def get(self,request,year,month):
        return HttpResponse(year+month+'书籍')
    def post(self,requset,year,month):
        pass

用类写views.py文件,可以将请求方法分发到同名的类方法去处理---->继承了dispatch方法,其源码如下,很明显,用了反射,将请求方法分发到同名的类方法去处理

    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        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)

拓展dispatch方法:用类写views.py文件时,先触发dispatch方法–>反射–>触发get方法–>get方法返回值–>dispatch方法接收返回.具体代码如下:

views.py文件:

class Book2(View):
    def dispatch(self,request, *args, **kwargs):
        print('dispatch方法触发')
        ret = super().dispatch(request,*args,**kwargs)
        print('dispatch方法结束')
        return ret
    def get(self,request,year,month):
        print('get方法触发')
        return HttpResponse(year+month+'书籍')
    def post(self,requset,year,month):
        pass

urls.py文件:

url(r'^book2/(\d+)/(\d+)',views.Book2.as_view())

3.命名URL(别名)和URL的反向解析

起别名:

url(r'^home', views.home, name='home'),  # 给我的url匹配模式起名(别名)为 home,别名不需要改,路径你就可以随便改了,别的地方使用这个路径,就用别名来搞
url(r'^index/(\d*)', views.index, name='index'),  # 给我的url匹配模式起名为index

反向解析:

在html模板中:

{% url 'home' %}  #模板渲染的时候,被django解析成了这个名字对应的那个url,这个过程叫做反向解析

在views文件中:

from django.urls import reverse
reverse("index")

4.url分发和命名空间模式

url分发:

项目的urls.py写法:

from django.conf.urls import url,include    #include做路径分发
from django.contrib import admin
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
]

app01 urls.py写法:

from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r'^index/', views.index,name='index'),
]

app02 urls.py写法:

from django.conf.urls import url
from app02 import views
# app_name = 'app02'
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index,name='index'),
]

命名空间模式:

当不同的应用中路径的别名相同时,在进行url别名反向解析时,得到的结果都是settings文件中最后一个应用的路径.

解决办法:在include中加上namespace='app01’即为:include(‘app01.urls’ namespace=‘app01’)—>把app01下的所有路径划分了一个单独的空间.

项目的urls.py写法:

from django.conf.urls import url,include   
from django.contrib import admin
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls' namespace='app01')),
    url(r'^app02/', include('app02.urls' namespace='app02')),
]

app01 urls.py写法:

from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r'^index/', views.index,name='index'),
]

app02 urls.py写法:

from django.conf.urls import url
from app02 import views
# app_name = 'app02'
urlpatterns = [
    url(r'^index/', views.index,name='index'),
]

在views中函数中:

v = reverse('app01:index')

在html文件中:

{% url 'app01:index'%}

这样即使app中URL的命名相同,也可以反向解析出正确的URL.

二.模板渲染

1.基本语法

Django内置模板渲染方法:1.return render(request,‘html路径’,要替换的字典),2.{{}}.具体代码如下:

home.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>欢迎{{ username }}</h1>
<p>{{ num }}</p>
<p>{{ l1.1 }}</p> <!--列表可以用索引取值-->
<p>{{ d1.k1 }}</p> <!--字典可以通过健取值-->
<p>{{ a.xx }}</p>  <!--对象.属性-->
<p>{{ a.get_name }}</p>  <!-- 调用方法时,只能调用无参数的方法 -->
</body>
</html>

views.py文件:

def home(request):
    username = '123'
    num = 100
    l1 = [11,22,33]
    d1 = {'k1':'v1','k2':'v2'}
    class A():
        def __init__(self):
            self.xx = 'oo'
        def get_name(self):
            return self.xx+'123'
    a = A()
    dic = {
        'username':username,
        'num':num,
        'l1':l1,
        'd1':d1,
        'a':a,
    }
    return render(request,'xx/home.html',dic)

urls.py文件:

url(r'^home',views.home)

2.过滤器

home.html文件

<h1>过滤器</h1>
<p>{{ xxxx|default:'abc' }}</p> <!--没有xxxx时,默认用默认值代替-->
<p>{{ username|length }}</p> <!--用值的长度替换-->
<p>{{ file_size|filesizeformat }}</p> <!--展示为可读的大小,自动计算单位-->
<p>{{ username|slice:'0:2' }}</p> <!--切片-->
<p>{{ current_time|date:'Y-m-d H:i:s' }}</p> <!--时间格式化-->
<p>{{ a_tag|safe }}</p> <!--将标签字符串识别为一个标签效果,防xss注入-->
<p>{{ username|truncatechars:4 }}</p> <!--字符截断-->
<p>{{ username|truncatewords:2 }}</p> <!--单词截断-->
<p>{{ username|cut:' ' }}</p> <!--移除字符串-->
<p>{{ l1|join:'+' }}</p> <!--字符串拼接-->

views.py文件:

import datetime
def home(request):
    username = '123 456 789'
    num = 100
    l1 = [11,22,33]
    d1 = {'k1':'v1','k2':'v2'}
    class A():
        def __init__(self):
            self.xx = 'oo'
        def get_name(self):
            return self.xx+'123'
    a = A()
    file_size = 10230401
    current_time = datetime.datetime.now()
    a_tag = '<a href="">a标签</a>'
    dic = {
        'username':username,
        'num':num,
        'l1':l1,
        'd1':d1,
        'a':a,
        'file_size':file_size,
        'current_time':current_time,
        'a_tag':a_tag,
    }
    return render(request,'xx/home.html',dic)

三.标签

基本语法:{%标签逻辑%}

1.for循环标签

循环列表:

index.html文件:

<ul>
    {% for i in l1 %}
        <li>{{ i }}</li>
        {% empty %}
        <span></span>
    {% endfor %}
</ul>

views.py文件:

def index(request):
    l1 = [11,22,33]
    d1 = {'k1':'v1','k2':'v2'}
    return render(request,'index.html',{'l1':l1,'d1':d1})

循环字典:

index.html文件:

<ul>
    {% for k,v in d1.items %}
    <li>{{ k }}--{{ v }}</li>
    {% endfor %}
</ul>

forloop对象:

forloop.counter            当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
forloop.counter0           当前循环的索引值(从0开始)
forloop.revcounter         当前循环的倒序索引值(从1开始)
forloop.revcounter0        当前循环的倒序索引值(从0开始)
forloop.first              当前循环是不是第一次循环(布尔值)
forloop.last               当前循环是不是最后一次循环(布尔值)
forloop.parentloop         本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等

2.if循环标签

index.html文件

{% if num == 100 %}
    <h1>123</h1>
{% endif %}

3.for嵌套+if

index.html文件:

<ul>
    {% for i in l1 %}
        {% if forloop.last %}
            {% for j in i %}
                <li>{{ j }}--{{ forloop.counter }}--{{ forloop.parentloop.counter }}</li>
            {% endfor %}
        {% endif %}
    {% endfor %}
</ul>

views.py文件:

def index(request):
    l1 = [11,22,33,[1,2,3]]
    d1 = {'k1':'v1','k2':'v2'}
    num = 100
    return render(request,'index.html',{'l1':l1,'d1':d1,'num':num})

四.组件

基本语法:在html文件中加{% include ‘zujian.html’ %}可引入一个界面.具体代码如下:

index.html文件

{% include 'zujian.html' %}

zujian.html文件:

<h1>导航栏</h1>

五.静态文件配置

方式1:

从根目录直接引入:

html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/xx.css">
</head>
<body>
<div class="c1">hello</div>
<div class="c2"></div>
</body>
<script src="/static/xx.js"></script>
</html>

setting文件:

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

css文件:

.c1{
    color: red;
}
.c2{
    background-color: green;
    height: 200px;
    width: 200px;
}

js文件:

var divEle = document.getElementsByClassName('c2')[0];
divEle.onclick = function () {
    this.style.backgroundColor = 'red';
};

方式2:

用标签配置:1.引入{% load static %},2.,

html文件:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
{#    <link rel="stylesheet" href="/static/xx.css">#}
    <link rel="stylesheet" href="{% static 'xx.css' %}">
</head>
<body>
<div class="c1">hello</div>
<div class="c2"></div>
</body>
{#<script src="/static/xx.js"></script>#}
<script src="{% static 'xx.js' %}"></script>
</html>

六.ORM(操作数据库)

ORM,object relational mapping,对象关系映射,做数据库操作的.

1.操作Django自带数据库

app01/models.py:

class Book(models.Model):
    # id = models.AutoField(primary_key=True) # id int primary key auto_increment
    title = models.CharField(max_length=32) #对应sql语句:title varchar(32)
    price = models.DecimalField(max_digits=5,decimal_places=2) #price decimal(5,2)
    pub_date = models.DateField() #pub_date date
    publish = models.CharField(max_length=32)

执行数据库同步指令,在项目根目录下面执行

python manage.py makemigrations
python manage.py migrate

查看FIeld和mysql中的字段关系对比

    _data_types = {
        '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.通过添加属性(obj=models.Book();obj.save()),2.通过create方法(obj= models.Book.objects.create()),3.models.Book.objects.bulk_create(obj_list)批量添加

from app01 import models
import datetime
# Create your views here.
def book(request):
    #方式一
    '''
    obj = models.Book(
        title='金瓶梅后传',
        price=2.8,
        # pub_date='2000-08-12',  #这样的格式字符串
        pub_date=datetime.datetime.now(),  # 时间日期类型
        publish='31期红浪漫出版社',
    )
    obj.save()
    '''
    #方式二
    '''
    obj= models.Book.objects.create(
        # obj是当前创建的新的记录对象
        title='金瓶梅前传',
        price=9.9,
        # pub_date='2000-08-12',  #这样的格式字符串
        pub_date=datetime.datetime.now(),  # 时间日期类型
        publish='31期夕阳红出版社',
    )
    '''
    #批量添加
    obj_list = []
    for i in range(1,4):
        obj = models.Book(
            title='少妇白洁' + str(i),
            price=i,
            pub_date=f'2000-08-1{i}',
            publish='31期夕阳红出版社',
        )
        obj_list.append(obj)
    models.Book.objects.bulk_create(obj_list)
    return HttpResponse('ok')

2.操作mysql数据库

1.连接mysql数据库:

1.配置settings.py文件,2.配置主目录下的init.py文件

settings.py文件:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db0917',
        'HOST': '127.0.0.1',
        'POST': 3306,
        'USER': 'root',
        'PASSWORD': '123456'
    }
}

init.py文件:

import pymysql
pymysql.install_as_MySQLdb()

2.创建一张表

1.写models文件,2.在项目根目录下执行两条命令

models.py文件:

class T1(models.Model):
    # id = models.AutoField(primary_key=True) # id int primary key auto_increment
    title = models.CharField(max_length=32) #对应sql语句:title varchar(32)
    price = models.DecimalField(max_digits=5,decimal_places=2) #price decimal(5,2)
    pub_date = models.DateField() #pub_date date
    publish = models.CharField(max_length=32)

命令窗口执行:

python manage.py makemigrations
python manage.py migrate

3.增

增加有三中方式:1.通过添加属性(obj=models.T1();obj.save()),2.通过create方法(obj= models.T1.objects.create()),3.models.T1.objects.bulk_create(obj_list)批量添加

views.py文件:

from app01 import models
import datetime
# Create your views here.
def book(request):
    #方式一
    obj = models.T1(
        title='金瓶梅后传',
        price=2.8,
        # pub_date='2000-08-12',  #这样的格式字符串
        pub_date=datetime.datetime.now(),  # 时间日期类型
        publish='31期红浪漫出版社',
    )
    obj.save()
    
    #方式二
	'''
    obj= models.T1.objects.create(
        # obj是当前创建的新的记录对象
        title='金瓶梅前传',
        price=9.9,
        # pub_date='2000-08-12',  #这样的格式字符串
        pub_date=datetime.datetime.now(),  # 时间日期类型
        publish='31期夕阳红出版社',
    )
    '''
    
    #批量添加
    '''
    obj_list = []
    for i in range(1,4):
        obj = models.T1(
            title='少妇白洁' + str(i),
            price=i,
            pub_date=f'2000-08-1{i}',
            publish='31期夕阳红出版社',
        )
        obj_list.append(obj)
    models.T1.objects.bulk_create(obj_list)
    '''
    return HttpResponse('ok')

update_or_create:有就更新,没有就创建 ,还有个get_or_create,有就查询出来,没有就创建

4.查

13种方法:all() , filter(条件) , get(条件) , exclude(条件) , order_by() , reverse(),count(),first(),last(),exists(),values(),values_list(),distinct()

	ret = models.T1.objects.all()   #queryset类型
    ret = models.T1.objects.filter(title='少妇白洁1') #queryset类型
    ret = models.T1.objects.get(title='少妇白洁1') #获取有且只有一条数据,找不到/超过一条都会报错
    ret = models.T1.objects.exclude(id=5) #queryset类型,返回id不等于6的所有对象,也可以在queryset类型基础上执行
    ret = models.T1.objects.all().order_by('price','id') #queryset类型,按照price进行升序,price相同的数据,按照id进行升序
    ret = models.T1.objects.all().order_by('-price') #按price降序排列
    ret = models.T1.objects.all().order_by('price').reverse()#对查询结果反向排序
    ret = models.T1.objects.all().count() #对queryset类型数据调用,返回查询到的对象数量
    ret = models.T1.objects.all().first() #model对象,返回第一条记录
    ret = models.T1.objects.all()[0]    #同first,不支持负索引
    ret = models.T1.objects.all().last() #返回最后一条记录
    ret = models.T1.objects.all().exists() #QuerySet包含数据,就返回True,否则返回False
    ret = models.T1.objects.values('title','price') #querset类型,<QuerySet [{'title': '少妇白洁1', 'price': Decimal('1.00')}...
    ret = models.T1.objects.all().values('title','price') #还可以通过queryset类型数据来调用
    ret = models.T1.objects.all().values_list('title','price')#用法同values,返回的时元组
    ret = models.T1.objects.all().values_list('price').distinct() #values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录
    print(ret)

filter基于双下划线的模糊查询

__year , __day , __startswith , __istartswith , __endswith , __icontains , __in , __range , __gt , __gte , __lt , __lte
按日期查询
ret = models.Book.objects.filter(pub_date__year='2000', pub_date__month='8',pub_date__day='12')
    print(ret) 
ret = models.Book.objects.filter(title__startswith='少妇')
ret = models.Book.objects.filter(title__istartswith='py')
ret = models.Book.objects.filter(title__endswith='2')
ret = models.Book.objects.filter(title_icontains='python')
不区分大小写
ret = models.Book.objects.filter(title__icontains='python')
print(ret)

ret = models.Book.objects.filter(price__in=[3,4])  # or 等于3或者等于4
ret = models.Book.objects.filter(price__range=[1,3]) #between 1 and 3
print(ret)

ret = models.Book.objects.filter(pub_date__year=2018)
ret = models.Book.objects.filter(pub_date__year='2018')    

# 价格大于3的
ret = models.Book.objects.filter(price__gt=3)
# 价格大于等于3的
ret = models.Book.objects.filter(price__gte=3)
# 价格小于3的
ret = models.Book.objects.filter(price__lt=3)
# 价格小于等于3的
ret = models.Book.objects.filter(price__lte=3)

5.删

.delete()

 models.Book.objects.filter(title='少妇白洁1').delete()
 models.Book.objects.get(id=3).delete()   
query类型数据和模型类对象都可以调用delete方法来进行删除

6.改

两种方式:1 .update() 2.对象.属性修改

    # 修改
    # 方式1
    # models.Book.objects.filter(id=4).update(
    #     title='少妇白洁1',
    #     price=4,
    # )
    # 方式2
    obj = models.Book.objects.get(id=6)
    obj.price = 18
    obj.save()
    
    obj.update()  模型类对象不能直接使用update方法

3.多表操作

1.创建多表

ad = models.OneToOneField(to=“AuthorDetail”, to_field=‘id’, on_delete=models.CASCADE)–>级联,一对一

authors=models.ManyToManyField(to=‘Author’,)–>多对多默认创建第三张表

from django.db import models

# Create your models here.
# 作者表
class Author(models.Model): #比较常用的信息放到这个表里面
    name=models.CharField( max_length=32)
    age=models.IntegerField()  #int
    # 与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
    ad = models.OneToOneField(to="AuthorDetail", to_field='id', on_delete=models.CASCADE)
    # authordetail_id
    # foreign key + unique


# 作者详细信息表
class AuthorDetail(models.Model):#不常用的放到这个表里面
    birthday=models.DateField()
    telephone=models.CharField(max_length=11)
    addr=models.CharField(max_length=64)


# 出版社表
class Publish(models.Model):
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)


# 书籍表
class Book(models.Model):
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    # publish=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
    publish=models.ForeignKey(to="Publish") #默认级联删除,默认关联的是另外一张表的id字段
    authors=models.ManyToManyField(to='Author',) #自动创建第三张表,id author_id book_id,不会作为本表的字段出现

2.创建字段时的一些参数

(1)null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
 
(1)blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
 
(2)default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
 
(3)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True(4)unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
 
(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。

(6)db_index
  如果db_index=True 则代表着为此字段设置数据库索引。


DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。
(7)auto_now_add
    配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

(8)auto_now
    配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。

特别说明:

choices属性:

sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))

数据库里面存的是1或者2
通过model模型类对象.get_属性名称_display()可以获取到数字对应的文本内容

auto_now_add和auto_now参数:

class t1(models.Model):
    # defauit 在使用orm操作添加数据时生效.
    name = models.CharField(max_length=12, default='张三')
    sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))

    d1 = models.DateTimeField(auto_now_add=True,null=True)  #自动添加创建记录的时间
    d2 = models.DateTimeField(auto_now=True,null=True) #自动添加创建记录的时间,更新记录是也能自动更新为最新时间

auto_now 自动更新时间只有在save更新时才生效,update不生效
所以如果要做更新时间的处理,那么最好手动获取当前时间并修改

3.增和删

    增加
    一对一
    models.AuthorDetail.objects.create(
        birthday='2018-01-01',
        telephone='13800000000',
        addr='北京'
    )
    ad_obj = models.AuthorDetail.objects.get(id=1)
    models.Author.objects.create(
        name='明皓',
        age=38,
        # ad=ad_obj,
        ad_id=2,
    )
    ad_obj = models.AuthorDetail.objects.get(id=4)
    obj= models.Author(
        name='杨浩',
        age=47,
        ad=ad_obj,
        # ad_id=3,
    )
    obj.save()

    一对多
    models.Book.objects.create(
        title='金瓶梅第二部',
        publishDate='2018-08-08',
        price=22,
        # publishs=models.Publish.objects.get(id=1),
        publishs_id=1,

    )

    多对多
    obj = models.Book.objects.filter(id=1).first()
    a1 = models.Author.objects.get(id=1)
    a2 = models.Author.objects.get(id=2)
    obj.authors.add(a1,a2)
    obj.authors.add(1,2)
    obj.authors.add(*[1,2])

    删除和更新
    一对一和一对多 ,基本和单表一样(级联删除)
    models.Author.objects.get(id=1).delete()
    models.AuthorDetail.objects.get(id=2).delete()
    models.Book.objects.get(id=1).delete()

    多对多删除
    ret = models.Book.objects.get(id=2)
    ret.authors.clear()  # 清空该书籍对应的第三张表中的记录
    ret.authors.remove(3,4)  #指定删除该书和哪些作者的关系

4.改

4.1一对一/一对多修改

两种方式:1.用字段名称进行修改(ad_id=1),2.通过对象.属性进行修改.具体代码如下:

models.Author.objects.filter(name='精华').update(
    name='敬华',
    age=50,
    # ad=models.AuthorDetail.objects.get(id=1),
    ad_id=1,
)
4.2多对多修改

set方法

obj = models.Book.objects.get(id=2)
obj.authors.set(['3',]) # 值是字符串,clear + add

5.查看和执行原生sql

5.1查看

方式一:

在python文件中执行如下语句:

from django.db import connection  #通过这种方式也能查看执行的sql语句
# print(connection.queries)

方式二:

在settings.py文件中配置如下内容:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}
5.2执行原生sql语句

方式一:from django.db import connection

from django.db import connection
cursor = connection.cursor()
cursor.execute('select * from app01_book;')
print(cursor.fetchall())

方式二:pymysql

方式三:

ret = models.Author.objects.raw('select * from app01_author;')  #只限于本表操作
print(ret)
for i in ret:
    print(i.name)

6.查

6.1一对一

正向查询:

# 查看敬华作者的家庭住址
# select app01_authordetail.addr from app01_author inner join app01_authordetail on app01_author.ad_id app01_atuhordetail.id
# where app01_author.name='敬华'
obj = models.Author.objects.get(name='敬华')
print(obj.ad.addr)   #北京

反向查询:

obj = models.AuthorDetail.objects.get(addr='北京协和')
print(obj.author.name)
6.2 一对多

正向查询:

查询金鳞岂是池中物这本书的出版社
正向查询
obj = models.Book.objects.get(title='金鳞岂是池中物')
print(obj.publishs.name)

反向查询:

查询马哥出版社出版的书籍有哪些
反向查询
ret = models.Publish.objects.get(name='马哥出版社')
books = ret.book_set.all()  #<QuerySet [<Book: Book object>, <Book: Book object>]>
print(books.values('title'))
6.3 多对多

正向查询:

# 查询金瓶梅第二部是谁写的
# 正向查询
# obj = models.Book.objects.get(title='金瓶梅第二部')
# print(obj.authors.all().values('name'))

反向查询:

# 查询敬华写了哪些书
obj = models.Author.objects.get(name='敬华')
print(obj.book_set.all().values('title')) 
6.4基于双下划线的跨表查询
一对一
    查看敬华作者的家庭住址
    正向写法
    ret =  models.Author.objects.filter(name='敬华').values('ad__addr')
    反向写法
    ret = models.AuthorDetail.objects.filter(author__name='敬华').values('addr')
    print(ret) #<QuerySet [{'addr': '北京'}]>

一对多
    查询金鳞岂是池中物这本书的出版社
    ret = models.Book.objects.filter(title='金鳞岂是池中物').values('publishs__name')
    print(ret) #<QuerySet [{'publishs__name': '小马哥出版社'}]>
    ret = models.Publish.objects.filter(book__title='金鳞岂是池中物').values('name')
    print(ret) #<QuerySet [{'name': '小马哥出版社'}]>

多对多
    查询金瓶梅第二部是谁写的
    ret = models.Book.objects.filter(title='金瓶梅第二部').values('authors__name')
    print(ret)
    ret = models.Author.objects.filter(book__title='金瓶梅第二部').values('name')
    print(ret) #<QuerySet [{'name': '敬华'}, {'name': '杨浩'}]>
6.5聚合查询
from django.db.models import Avg,Max,Min,Count,Sum
# 聚合查询
ret = models.Book.objects.all().aggregate(Avg('price'))
ret = models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price'))
print(ret,type(ret)) #{'price__avg': 15.0} <class 'dict'>
#注意结果为字典类型.
6.6分组查询

sql_mode模式

https://www.cnblogs.com/clschao/articles/9962347.html

# # 分组查询
# 统计一下每个出版社出版书的平均价格
'''
select publishs_id,avg(price) from app01_book group by publishs_id;
select avg(app01_book.price) from app01_book inner join app01_publish on 
app01_book.publishs_id = app01_publish.id group by app01_publish.name;
'''
ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
print(ret)
ret = models.Publish.objects.annotate(a=Avg('book__price'))
print(ret.values('a','name'))
print(ret)
# <QuerySet [<Publish: Publish object>, <Publish: Publish object>]>
print(ret.values('a','name'))
# sql_mode
# only_full_group_by
6.7 F查询
#查询一下点赞数大于评论数的书籍
#针对本表不同字段数据进行对比时或者本表字典做一些统一修改时使用F查询
models.Book.objects.filter(dianzan__gt=comment)
ret = models.Book.objects.all()
book_list = []
for i in ret:
     if i.dianzan > i.comment:
        book_list.append(i)

#使用F查询   
from django.db.models import F
# 点赞数大于评论数的  
ret = models.Book.objects.filter(dianzan__gt=F('comment'))
print(ret)
# 所有书籍上调10块
models.Book.objects.all().update(price=F('price')+10) #支持四则运算
6.8Q查询
from django.db.models import Q
    # | -- or
    # & -- and
    # ~ -- not
ret = models.Book.objects.filter(Q(comment__gt=30)|Q(dianzan__gt=50))
ret = models.Book.objects.filter(Q(comment__gt=30)&Q(dianzan__gt=50))
# 等同于# 
ret = models.Book.objects.filter(comment__gt=30,dianzan__gt=50)
ret = models.Book.objects.filter(Q(comment__gt=30) | Q(dianzan__gt=50),publishDate__year='2018')
    # 注意没有Q包裹的条件,写在Q包裹的条件后面.
    
# 多层嵌套
ret = models.Book.objects.filter(Q(Q(comment__gt=30) | Q(dianzan__gt=50))&Q(xx=11),publishDate__year='2018')

# 条件取反
# 取评论数小于等于30 的,或者点赞数大于50的
ret = models.Book.objects.filter(~Q(comment__gt=30)|Q(dianzan__gt=50))
print(ret)

4.锁和事务

事务:必须所有的操作全部成功,最终提交数据,否则数据回滚,回到刚开始没操作的那个状态,给sql语句加事务有两种方式:1.装饰器:@transaction.atomic (所有被装饰的函数中所有sql语句都被捆绑为事务) ,2.with语句

锁:create、update、delete操作时,mysql自动加行级互斥锁,select_for_update()

from django.db import transaction
#方式一:
@transaction.atomic  
def xx(request):
    # a = 10
    # models......
    # a = a - 1
#方式二:
with transaction.atomic():
    models.Book.objects.filter(price=100).select_for_update()  #select * from app01_book where price=100 for update
 #with语句体里面的sql都捆绑为事务

七.ajax

ajax用法:异步请求 局部刷新

写法:html中用jquery–>$.ajax({}) views.py文件中:return HttpResponse(‘ok’)

html文件:

<body>
{# <form action=""> #}
    用户名: <input type="text" id="uname">
    密码: <input type="password" id="pwd">
    <input type="button" value="确定" id="btn">
    <span id="error_msg" style="color:red;font-size: 12px;"></span>
{#</form>#}
</body>

<script src="{% static 'js/jquery.js' %}"></script>
<script>
    $('#btn').click(function () {
        var uname = $('#uname').val();
        var pwd = $('#pwd').val();
        $.ajax({
            url:'/login/',
            type:'post',
            data:{username:uname,password:pwd},  // request .POST.get('username')
            success:function (res) {
                // res接收的是请求成功之后的响应结果,
                // 这对响应结果做一些事情
                console.log(res, typeof res);
                if (res === 'ok'){
                    location.href = '/xx/';
                }else {
                    $('#error_msg').text('用户名或者密码有误');
                }
            },
            error:function (res) {
                //res 请求失败之后获取到的响应结果  5xx,4xx
                console.log('>>>>>>',res);
                if (res.status === 403){
                    $('#error_msg').text(res.responseText);
                }
                //else if (res.status === 500){
                //}
            }
        })
    })
</script>

views.py文件:

    def login(request):
        if request.method == 'GET':
            print('看登录页面')
            return render(request, 'login.html')
        uname = request.POST.get('username')
        if uname == 'chao':
        # <QueryDict: {'username': ['root'], 'password': ['123']}>
        # return render(request,'xx.html')
            return HttpResponse('ok')
        else:
            ret = HttpResponse('用户名不对')  # 200
            # ret.status_code = 403
            return ret

八.中间件

1.中间件相关概念

中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法.

1.1 Django请求生命周期

在这里插入图片描述
传送门

1.2 中间件的五个方法

1 process_request(self,request)

多个中间件时,按setting文件倒叙执行,

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对象,就会报错

2 process_view(self, request, view_func, view_args, view_kwargs)

3 process_template_response(self,request,response)

4 process_exception(self, request, exception)

5 process_response(self, request, response)

必须有return值,且在视图函数之后执行且多个是倒叙执行,

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class Md1(MiddlewareMixin):
    def process_request(self,request):
        print("Md1请求")
     #process_request方法里面不写返回值,默认也是返回None,如果你自己写了return None,也是一样的效果,不会中断你的请求,但是如果你return 的一个httpresponse对象,那么就会在这个方法中断你的请求,直接返回给用户,这就成了非正常的流程了
        #并且,如果你在这里return了httpresponse对象,那么会从你这个中间件类中的process_response方法开始执行返回操作,所以这个类里面只要有process_response方法,肯定会执行
    def process_response(self,request,response):
        print("Md1返回")
        return response

class Md2(MiddlewareMixin):
    def process_request(self,request):
        print("Md2请求")
        #return HttpResponse("Md2中断")
    def process_response(self,request,response):
        print("Md2返回")
        return response
中间件可以定义五个方法,分别是:(主要的是process_request和process_response)

process_request(self,request)
	执行顺序:按setting文件配置的顺序从上到下依次执行.
	如果return了一个httpresponse对象则会在这个方法中断你的请求,直接返回给用户,不会走视图函数
	
process_view(self, request, view_func, view_args, view_kwargs)
	它应该返回None或一个HttpResponse对象。 
	如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 
	如果它返回一个HttpResponse对象,Django不会调用对应的视图函数。它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

process_template_response(self,request,response)
	process_template_response是在视图函数执行完成后立即执行,
	但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法

process_exception(self, request, exception)
	处理视图函数的异常顺序是倒序
	这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象
	如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常
	如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
	
process_response(self, request, response)
	执行顺序:在视图函数之后执行,按setting文件配置的倒叙从下到上依次执行.

2.创建中间件流程

1 在应用下创建一个文件夹

2 在文件夹中创建一个py文件

3 在py文件中写上如下内容

4 在settings.py文件中做如下配置

py文件内容:

from django.utils.deprecation import MiddlewareMixin

class Auth(MiddlewareMixin):  #类名随意,继承MiddlewareMixin
    # 如果想对请求做一些统一处理,那么就定义process_request方法
    def process_request(self,request):
        print('请求来啦!!!快看!!!!')
        
        
    def process_response(self,request,response):
        # response 视图响应回来的响应对象
        # response.status_code = 201
        print('请求走啦!!!')
        return response

settings文件配置:

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',
    
    'app01.mymiddlewares.xx.Auth',  #将中间件类的路径写到这里
]

九.cookie和session

1.什么是cookie

http协议(基于tcp)的特点:1 无连接(短连接);2 无状态.

1 无连接(短连接)
请求:connection : close,keepalive
2 无状态
http协议不会记录客户端和服务端的任何信息,导致服务端和客户端不能维持会话

正是由于http无状态的特性,所以出现了cookie(一种浏览器技术).请求头健值对.

浏览器访问服务端,带着一个空的cookie,然后由服务器产生内容,浏览器收到相应后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。

在这里插入图片描述

2.cookie操作

Ctrl + Shift + del三个键来清除页面缓存和cookie

2.1实例(登录)

from django.shortcuts import render,redirect
# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    uname = request.POST.get('uname')
    if uname == 'root':
        ret = redirect('/home/')
        ret.set_cookie('is_login',True)
        return ret
    return redirect('/login/')

def home(request):
    if request.COOKIES.get('is_login') == 'True':
        return render(request,'home.html')
    return redirect('/login/')

装饰器版:

from django.shortcuts import render,redirect
def outer(f):
    def inner(request,*args,**kwargs):
        if request.COOKIES.get('is_login') != 'True':
            return redirect('/login/')
        return f(request,*args,**kwargs)
    return inner

def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    uname = request.POST.get('uname')
    if uname == 'root':
        ret = redirect('/home/')
        ret.set_cookie('is_login',True)
        return ret
    return redirect('/login/')

@outer
def home(request):
    return render(request,'home.html')

中间件版:

1 在app01文件夹下建mymildewares/auth.py

2 在setting文件下配置路径

from django.shortcuts import redirect
from django.utils.deprecation import MiddlewareMixin

class LoginAuth(MiddlewareMixin):
    #设置白名单
    white_list = ['/login/',]
    def process_request(self,request):
        if request.path not in self.white_list:
            if request.COOKIES.get('is_login') != 'True':
                return redirect('/login/')

setting文件

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',
    'app01.mymildlewares.auth.LoginAuth'#配置路径
]

view文件:

from django.shortcuts import render,redirect

def login(request):
    if request.method == 'GET':
        return render(request,'login.html')
    uname = request.POST.get('uname')
    if uname == 'root':
        ret = redirect('/home/')
        ret.set_cookie('is_login',True)
        return ret
    return redirect('/login/')

def home(request):
    return render(request,'home.html')

2.2cookie操作方法

获取cookie
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) #带签名的值

设置cookie
rep = HttpResponse(...)
rep = render(request, ...)

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)

修改cookie
ret.set_cookie('username', 'xxx')  #相同的键设置不同的值就是修改

删除cookie
ret.delete_cookie('username')

参数说明:

参数:

      key, 键

      value='', 值

      max_age=None, 超时时间,单位为秒  5表示5秒后失效

      expires=None, 超时时间(IE requires expires, so set it if hasn't been already.) 值为时间日期类型数据

      path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
 

      domain=None, Cookie生效的域名

      secure=True, https传输

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

3.什么是session

产生原因:

1 cookie是明文存储的

2 大小限制:

Cookie大小上限为4KB;
一个服务器最多在客户端浏览器上保存20个Cookie;
一个浏览器最多保存300个Cookie,因为一个浏览器可以访问多个服务器。

特点:

cookie中放的数据是密文的

数据存在服务端,没有大小上限

一个浏览器对应一个服务端,就是一个session

原理:

在这里插入图片描述

4.操作session

4.1实例(登录)

views文件:

from django.shortcuts import render,redirect
def login(request):
    if request.method == "GET":
        return render(request,'login.html')
    if request.POST.get('uname') == 'root':
        request.session['is_login'] = True  #设置
        return redirect('/home/')
    return redirect('/login/')

def home(request):
    return render(request,'home.html')

中间件:

from django.shortcuts import redirect
from django.utils.deprecation import MiddlewareMixin

class LoginAuth(MiddlewareMixin):
    white_list = ['/login/',]
    def process_request(self,request):
        if request.path not in self.white_list:
            if request.session.get('is_login') != True: #获取
                return redirect('/login/')

数据库配置:

python manage.py makemigrations
python manage.py migrate

4.2session操作方法

#设置session
request.session['k1'] = 123
# request.session
#1 生成一个随机字符串
#2 将随机字符串放到cookie中,名称为sessionid
#3 将设置的session数据,序列化+加密,保存到了django-session表中
request.session.setdefault('k1',123)

#获取session
request.session['k1']
# request.session['is_login']
# 1 取出请求中cookie键为sessionid的值
# 2 通过这个值到django-session表中获取数据
# 3 将数据解密并且反序列化得到原来的数据
request.session.get('k1',None)

#删除session
del request.session['k1']

#清空session
request.session.flush()

# 注销
def logout(requset):
    requset.session.flush()
    # 1 删除cookie中的数据
    # 2 删除数据库中django-session表中的记录
    return redirect('/login/')

补充:django外部脚本调用django环境

import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_cookie.settings")
import django
django.setup()

from app01 import  models
models.Book.objects.create(
    title='xxx',
    price=200,
)

十.CSRF

csrf: 跨站请求伪造

在这里插入图片描述
解决csrf攻击的最直接的办法就是生成一个随机的csrftoken值,保存在用户的页面上,每次请求都带着这个值过来完成校验,django会验证表单中的token和cookie中token是否能解出同样的secret,secret一样则本次请求合法 .

form表单的csrf认证:

{% csrf_token %}

ajax的csrf认证(需要从cookie中拿取token添加到请求头中):

//方式一:
$.ajax({
  url: "/cookie_ajax/",
  type: "POST",
  data: {
    "username": "chao",
    "password": 123456,
    "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
  },
  success: function (data) {
    console.log(data);
  }
})
//方式二:
$.ajaxSetup({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});

十一.文件上传

form表单上传文件

<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    用户名:<input type="text" name="username" >
    头像:<input type="file" name="avatar" multiple>
    <input type="submit">
</form>

ajax上传文件

用户名:<input type="text" name="username" id="username">
头像:<input type="file" name="avatar" id="avatar">
<button id="ajax_btn">上传</button>


    $('#ajax_btn').click(function () {

        var uname = $('#username').val();
        var file_obj = $('#avatar')[0].files[0];

        var formdata = new FormData();
        formdata.append('username',uname);
        formdata.append('csrfmiddlewaretoken','{{ csrf_token }}');
        formdata.append('avatar',file_obj);

        $.ajax({
            url:'/login/',
            type:'post',
            data:formdata,
            processData: false ,    // 不处理数据
            contentType: false,    // 不设置内容类型
            success:function (res) {
                console.log(res);
            }
        })
    })

视图代码:

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        # print(request.POST)
        # print(request.FILES)
        file_obj = request.FILES.get('avatar')
        print(file_obj)
        name = file_obj.name
        print(name)
        # with open(fr'C:\Users\oldboy\Desktop\Pointofix\{name}', 'wb') as f:
        print(type(file_obj))
        with open(name, 'wb') as f:
			# 方式1
            # for i in file_obj:  # \r\n
			# 方式2
            # for i in file_obj.chunks():  # \r\n
            for i in file_obj.chunks():  # \r\n  读取65536B
                f.write(i)
        return HttpResponse('ok')
适合人群: 1、具有一定Python语言基础,有一定的web前端基础,想要深入学习Python Web框架的朋友; 2、学习完“跟着王进老师学开发Python篇”“跟着王进老师学Web前端开发”的朋友; 3、有Django基础,但是想学习企业级项目实战的朋友; 4、喜欢 Django 框架并想深入研究的朋友; 5、有一定的数据库基础   课程目标: 本系列课程是从零基础开始并深入讲解Django,最终学会使用Django框架开发企业级的项目。课程知识点全网最详细,项目实战贴近企业需求。本系列课程除了非常详细的讲解Django框架本身的知识点以外,还讲解了web开发中所需要用到的技术,学完本系列课程后,您将独立做出一个具有后台管理系统,并且前端非常优美实用的网站。   课程内容: 在人工智能大行其道的时代,许多开发者对Python这门编程语言都比较熟悉。但是如何用它实现一个企业级别的项目,可能许多朋友还存在一些困惑。联科教育“跟着王进老师学Python”系列课程是专门针对想要从事Python Web开发的朋友而准备的,并且按照企业需求的标准定制的学习路线。学习路线中包含Python基础和进阶、Web前端、MySQL数据库、Flask和Django框架以及N多个企业真实项目。在学习完本系列中所有的课程后,从前端页面的实现,到后台代码的编写,再到数据库的管理,一人可以搞定一个公司网站的事情,掌握实现全栈开发,让你升职加薪不是梦! 本季课程介绍了Django中ORM模型,使用ORM模型的优势;Django中ORM模型常用的字段,ORM实现数据查询;Django后台管理等。所有应用均通过案例“在线图书商城”完成讲解和演示,完整项目,贯穿全部知识点,边学边练,帮助大家快速掌握知识,了解企业要求。
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页