Django
HTTP協議:
Socket和Tcp協議關系:
-
socket本身並不是協議而是一個調用的接口,socket的出現使我們更方便的使用tcp協議,如socket的基本接口,listen send recv
HTTP協議概述:
-
HTTP存在應用層的超文本傳輸協議,協議規定了客戶端和服務器之間的通信標准,采用的是請求和響應模型,客戶端發送一個請求報文,服務端進行響應
-
HTTP優點:無連接:請求和響應,無狀態:訪問速度快,通信標准
-
請求格式和響應格式:
HTTP工作原理(重要):
-
第一步:客戶端連接web服務端
-
客戶端連接服務端,會建立一個TCP套接字連接,(套接字就是socket,客戶端通常是瀏覽器)
-
-
第二步:發送http請求
-
通過TCP套接字,客戶端向web服務端發送一個文本的請求報文,一個請求報文由請求行,請求頭部,和請求數據組成
-
-
第三步:服務器接收請求並返回HTTP響應
-
響應就是服務端分析發送過來的請求,通過解析請求定位到資源,服務器將資源寫到tcp套接字中,返回給客戶端
-
-
第四步:釋放tcp連接
-
如果connection模式為close時,服務端主動關閉tcp連接,客戶端被動關閉連接,釋放tcp,若connection 模式為keepalive,則該連接會保持一段時間,在該時間內可以繼續接收請求
-
-
第五步:客戶端瀏覽器解析HTML內容
-
客戶端瀏覽器首先解釋狀態行,查看請求的狀態碼是否成功,然后解析每一個響應頭,客戶端瀏覽器讀取響應數據的HTML,在根據HTML語法在瀏覽器窗口中進行展示
-
瀏覽器地址欄輸入URL,流程:
-
瀏覽器向DNS服務器請求解析該URL中的域名對應的IP地址
-
解析出IP地址,根據IP地址和默認端口,和服務器建立TCP連接
-
瀏覽器發送HTTP請求(包含url后面對應的文件路徑),該請求報文由TCP第三次握手發送給服務器
-
服務器對瀏覽器做出相應,將對應的html返回給瀏覽器
-
釋放TCP連接
-
瀏覽器將該html文本進行展示
HTTP 請求方法
-
HTTP/1.1協議中共定義了八種方法(也叫“動作”)來以不同方式操作指定的資源:
-
GET:
- 向指定資源發出顯示請求,使用GET方法只用在讀取數據,
-
POST:
- 向指定資源提交數據,請求服務器進行處理(如提交表單或者上傳文件),數據被包含在請求文本中
-
HEAD:
-
與GET方法一樣,都是向服務器發出指定資源的請求。只不過服務器將不傳回資源的本文部分。它的好處在於,使用這個方法可以在不必傳輸全部內容的情況下,就可以獲取其中“關於該資源的信息”(元信息或稱元數據
-
-
其余請求:
-
put 向指定資源位置上傳其最新內容
-
delete 請求服務器刪除Request-URI所標識的資源。
-
trace 回顯服務器收到的請求,主要用於測試或診斷
-
options 使服務器傳回該資源所有http請求方法
-
connect HTTP/1.1協議中預留給能夠將連接改為管道方式的代理服務器
-
-
請求注意事項:
-
方法名稱是區分大小寫的。當某個請求所針對的資源不支持對應的請求方法的時候,服務器應當返回狀態碼405(Method Not Allowed),當服務器不認識或者不支持對應的請求方法的時候,應當返回狀態碼501(Not Implemented)
-
HTTP 狀態碼
-
1xx消息——請求已被服務器接收,繼續處理
-
2xx成功——請求已成功被服務器接收、理解、並接受
-
3xx重定向——需要后續操作才能完成這一請求
-
4xx請求錯誤——請求含有詞法錯誤或者無法被執行 404:沒有內容 403:沒有權限
-
5xx服務器錯誤——服務器在處理某個正確請求時發生錯誤
URL
-
超文本傳輸協議,統一資源定位獲取五個基本元素,
https://www.sogou.com/web?query=新聞&_asf=www.sogou.com&_ast=&w=0 # http 傳送協議 # // 層級URL表示符號,固定格式 # www.sogou.com 域名,服務器和:端口號 #/ 區分每個路徑的的目錄 #/web 頁面路徑 #?query=新聞 GET模式查詢的窗口參數(?字符為起點,每個參數以&隔開,再以=分開參數名稱和數據) # 錨點 請求(瀏覽器發給服務器的數據 request0 請求方法,路徑,協議版本\r\n #請求行 k1:v1\r\n k2:v2\r\n #請求頭 \r\n 請求數據 #get請求美哦與請求體 響應(服務端返回給瀏覽器的數據 respons) 格式: "協議版本" 狀態碼 狀態描述 \r\n k1:v1\r\n k2:v2\r\n \r\n 響應數據(響應體)"html文本"
web框架
-
web框架的本質都是一個socket服務端,而用戶瀏覽器就是一個socket客戶端部,這樣就實現了web框架
web框架的功能:
-
使用socket收發消息 (wsgiref wsgi模塊也可以收發消息,uwsgi線上使用)
-
根據不同路徑返回不同的內容
-
返回動態的數據(字符串的替換 模板的渲染 jinja2)
分類:
-
Django 2 3
-
Flask 2
-
tornado 1 2 3
自己寫web框架:
-
框架示例:
import socket import time sk = socket.socket() # 創建一個socket對象 sk.bind(('127.0.0.1',667)) # 綁定ip和端口 sk.listen() # 監聽 def index(url): with open('index.html', 'rb') as f: #讀取index標簽 return f.read() def home(url): ret = '歡迎回家! - {}'.format(url) return ret.encode('utf-8') def help_me(url): ret = '再等30年,你又是條好漢! - {}'.format(url) return ret.encode('utf-8') def timer(url): now = time.time() with open('time.html','r',encoding='utf-8') as f: ret = f.read() ret = ret.replace('@@time@@',str(now)) #將獲取的時間在time中替換展示在頁面上 return ret.encode('utf-8') list1 = [ ('/index', index), ('/home', home), ('/help_me', help_me), ('/time', timer),] while True: conn, addr = sk.accept() # 等待連接 data = conn.recv(1024) url = data.decode('utf-8').split()[1] func = None for i in list1: if url == i[0]: func = i[1] break if func: ret = func(url) else: ret = '被查辦了!'.encode('utf-8') conn.send(b'HTTP/1.1 200 OK\r\ncontent-type: text/html; charset=utf-8\r\n\r\n') conn.send(ret) conn.close() # index頁面: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1 style="color: forestgreen"> 歡迎光臨! </h1> </body> </html> # time頁面: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>當前時間是:@@time@@ </h1> </body> </html>
Django安裝簡單使用
安裝Django:
-
命令行方法:
-
使用django必須是專業版
-
pip3 install django==1.11.23 -i https://pypi.tuna.tsinghua.edu.cn/simple (清華源速度快)
-
-
查看是否安裝成功:
-
查看此目錄是否有django-admin.exe
-
D:\Program Files (x86)\python3.6.8\Scripts
-
創建項目:
-
命令行創建:
- django-admin startproject 項目名稱
-
pycharm創建:
-
flie _ new_project _ django _ 項目路徑 選解釋器
-
-
創建后的項目目錄:
xiangmu/ ├── idea # pycharm環境 ├── manage.py # 管理文件 ├─— templates # html css樣式目錄 └── xiangmu # 項目目錄 ├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函數的對應關系 └── wsgi.py # runserver命令就使用wsgiref模塊做簡單的web server settings.py配置文件: ALLOWED_HOSTS = ['*'] #設置可以訪問的主機,*=所有 TEMPLATES-->DIRS #關聯html css文件位置 urls.py URL路徑地址:
啟動項目:
-
命令行:
#進入python目錄進行啟動 python manage.py runserver 127.0.0.1:8000 python manage.py runserver 127.0.0.1:80 python manage.py runserver 0.0.0.0:80
-
pycharm:
-
pycharm 點綠三角, 不要右鍵運行文件
-
Django項目創建步驟:
-
下載
命令行:
pip install django==1.11.23
pip install django==1.11.23 -i 源的地址
pycharm
file——》 settings ——》解釋器 ——》 點+號 ——》 輸入django ——》 選擇版本 ——》下載
-
創建項目
命令行:
切換到存項目的目錄下
django-admin startproject 項目名
pycharm:
file ——》 new project ——》 django ——》 輸入項目的路徑 ——》 選擇解釋器 ——》 寫一個APP的名字 ——》 create
-
啟動項目
命令行:
切換進去到項目的根目錄 manage.py
python manage.py runserver # 127.0.0.1:8000
python manage.py runserver 80 # 127.0.0.1:80
python manage.py runserver 0.0.0.0:80 # 0.0.0.0:80
pycharm:
點綠三角 啟動 確定是django項目
可配置ip和端口
-
配置settings
靜態文件
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,’static‘),
os.path.join(BASE_DIR,’static1‘),
]
中間件
csrf 中間件注釋掉 可以提交POST請求
INSTALLED_APPS app相關
數據庫
模板 DIRS
-
APP
創建app
命令行:
python manage.py startapp app名稱
pycharm:
tools ——> run manage.py task ——> startapp app名稱
注冊app
INSTALLED_APPS = [ # 'app01', 'app01.apps.App01Config', ]
-
urls.py
urlpatterns = [ url(r'^index/', views.index), url(r'^home/', views.home), url(r'^login/', views.login), ]
-
views.py
from django.shortcuts import HttpResponse, render, redirect def index(request,): return HttpResponse()
HttpResponse('字符串') 返回字符串
render(request,'html文件名') 返回一個html頁面
redirect(’重定向的地址‘) 重定向
-
form表單
-
form標簽的屬性 action ='' 提交的地址 method=’post‘ 請求方式 novalidate 不校驗
-
input標簽要有name 有些需要value
-
有一個類型為submit的input或者 button
-
-
get 和post的分別
get 獲取一個頁面
沒有請求體
?k1=v1&k2=v2
django 中獲取 request.GET request.GET['k1'] request.GET.get('k1','xxxx')
post 提交數據 數據隱藏
django 中獲取 request.POST request.POST['k1'] request.POST .get('k1','xxxx')
orm-增刪改查:
-
__str__和__repr__區別
:有時候我們想讓屏幕打印的結果不是對象的內存地址,而是它的值或者其他可以自定義的東西,以便更直觀地顯示對象內容,可以通過在該對象的類中創建或修改__str__()或__repr__()方法來實現(顯示對應方法的返回值) # 兩種觸發方式: 使用print()時 使用%s和f'{}'拼接對象時 使用str(x)轉換對象x時 在上述三種場景中,會優先調用對象的__str__()方法;若沒有,就調用__repr__()方法;若再沒有,則顯示其內存地址。 # 特別地,對於下面兩種場景: 用%r進行字符串拼接時 用repr(x)轉換對象x時 則會調用這個對象的__repr__()方法;若沒有,則不再看其是否有__str__()方法,而是顯示其內存地址
-
創建表數據
from django.db import models class Publisher(models.Model): # Publisher:表名,繼承models.Model類 pid = models.AutoField(primary_key=True) # 自增、主鍵 name = models.CharField(max_length=32,unique=True) def __str__(self): return "{}{}".format(self.pid,self.name) # 返回給調用者內容
-
查詢展示數據:
#查詢所有數據 all_publisher = models.Publisher.objects.all() #將數據返回到前端 return render(request,'publisher_list.html',{'all_publisher':all_publisher}) #展示數據 from django.shortcuts import render,HttpResponse,redirect from app import models #從數據庫中展示數據 #從數據庫中查詢出所有的出版社 #從數據庫中查詢的數據展示到前端頁面 def publisher_list(request): all_publisher = models.Publisher.objects.all().order_by('pid') return render(request,'publisher_list.html',{'all_publisher':all_publisher})
-
模板的語法:
{{ all_publishers }} 變量 {% for i in all_publishers %} #for循環 {{ forloop.counter }} #循環打印的內容 {{ i }} {% endfor %} #閉合
-
新增數據:
# 方式一:通過create將數據插入到數據庫中(推薦) ret = models.Publisher.objects.create(name=pub_name) return redirect("/publisher_list") #方式二:先創建對象在save進行保存 obj = models.Publisher(name=pub_name) obj.save() #新增出版社 def publisher_add(request): pub_name,error='','' if request.method == "POST": #判斷前端是POST類型還是GET類型 pub_name = request.POST.get('pub_name') #通過POST獲取前端的出版社名稱 if not pub_name: error = "輸入不能為空" elif models.Publisher.objects.filter(name=pub_name): error = "數據已經存在" else: #通過create將數據插入到數據庫中(推薦) models.Publisher.objects.create(name=pub_name) return redirect("/publisher_list") return render(request, 'publisher_add.html', {'pub_name': pub_name, 'error': error})
-
刪除數據
pk = request.GET.get('pk') query = models.Publisher.objects.filter(pk=pk) # 對象列表 query.delete() # 通過queryset 刪除 query[0].delete() # 通過單獨的對象 刪除 # 刪除數據 def publisher_del(request): pk_id = request.GET.get('pk') # 獲取前端URL數據 query = models.Publisher.objects.filter(pk=pk_id) if not query: return HttpResponse("要刪除的數據不存在") query.delete() # 參數有多個刪除多個,query[0].delete()#只刪除一個 return redirect("/publisher_list")
-
編輯數據
obj = models.Publisher.objects.filter(pk=pk).first() # 對象列表中第一個對象 obj.name = pub_name # 內存中修改 obj.save() # 提交 #編輯數據 def publisher_edit(request): error="" pk_id = request.GET.get("pk") #url地址攜帶的參數,first()查出多個只取一個,沒有不報錯 obj = models.Publisher.objects.filter(pk=pk_id).first() if not obj: return HttpResponse("編輯的對象不存在") if request.method == "POST": # 獲取新提交的數據,編輯原始的對象 pub_name = request.POST.get('pub_name') if not pub_name: error = "輸入不能為空" elif models.Publisher.objects.filter(name=pub_name): error = "數據已經存在" else: obj.name = pub_name obj.save() return redirect("/publisher_list") return render(request,'publisher_edit.html',{'obj':obj,'error':error})
ORM一對多關系:
外鍵的設計
# ForeignKey 添加的外鍵('Publisher' 添加字符串通過反射進行查找)
on_delete=models.set(1) 唯一
default=11,on_delete=models.DEFERRED 默認值
null=True on_delete=models.SET_NULL 設置字段可有為空
on_delete=models.DO_NOTHING 什么操作都不做
# on_delete 2.0 必填 ,關聯刪除后的選項
class Book(models.Model):
id = models.AutoField(primary_key=True) # 自增、主鍵
title = models.CharField(max_length=32)
pid = models.ForeignKey('Publisher', on_delete=models.CASCADE) #默認關聯主鍵
增刪改查
-
查詢:
all_books = models.Book.objects.all() print(all_books) for book in all_books: print(book) print(book.pk) print(book.title) print(book.pub,type(book.pub)) # 一對多關聯的是對象 print(book.pub_id,type(book.pub_id)) # 所關聯的對象的pk print('*' * 32)
-
新增
#pub=models.Publisher.objects.get(pk=pub_id) 只能等於一個對象 models.Book.objects.create(title=title,pub=models.Publisher.objects.get(pk=pub_id)) #第二種方法,pub外鍵必須等於一個對象,但是都要轉換成id,所以第二種方法直接添加ID也可以 models.Book.objects.create(title=title, pub_id=pub_id)
-
刪除
pk = request.GET.get('pk') models.Book.objects.filter(pk=pk).delete()
-
編輯
book_obj.title = title book_obj.pub_id = pub_id #第一種方法 book_obj.pub = models.Publisher.objects.get(pk=pub_id) book_obj.save() #第二種方法 book_obj.pub_id = new_pub_id #將數據保存 book_obj.save()
ORM 多對多關系:
多對多關系創建
-
第一種方法:django通過ManyToManyField自動創建第三張表
# 在上下兩個表中添加都可以,只是順序不一樣 class Book(models.Model): title = models.CharField(max_length=32) pub = models.ForeignKey('Publisher', on_delete=models.CASCADE) # authors = models.ManyToManyField('Author') # 描述多對多的關系 不生成字段 生成關系表 def __repr__(self): return self.title __str__ = __repr__ class Author(models.Model): id = models.AutoField(primary_key=True) # 自增、主鍵 name = models.CharField(max_length=32) books = models.ManyToManyField('Book') # 描述多對多的關系 不生成字段 生成關系表
-
第二種方法:自己手動創建
class Book(models.Model): title = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book_Author(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE) author = models.ForeignKey(Author, on_delete=models.CASCADE) date = models.DateField() #自己創建表可以添加多個字段(時間字段)
-
第三種方法:自己創建 + ManyToManyField
# 第三張表包含了第一張和第二張表的使用方法 class Book(models.Model): title = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) #第三種方法:to book表字段,through=指定多對多關系 books = models.ManyToManyField(Book, through='Book_Author') class Book_Author(models.Model): #創建關系表 book = models.ForeignKey(Book, on_delete=models.CASCADE) author = models.ForeignKey(Author, on_delete=models.CASCADE) date = models.DateField()
多對多-增刪改查
-
查詢:
def author_list(request): all_author = models.Author.objects.all().order_by("id") for author in all_author: print(author) print(author.name) print(author.pk) print(author.books) #多對多拿到books是關系對照表 print(author.books.all()) #通過all拿到關聯的所有對象 print("*"*32) return render(request,'author_list.html',{'all_author':all_author}) # 從book表中查詢author作者數據,下面是獲取到author對象 book.author_set.all # 前端展示示例: <tbody> {% for author in all_author %} <tr> <td>{{ forloop.counter }}</td> <td>{{ author.pk }}</td> <td>{{ author.name }}</td> <td> {% for book in author.books.all %} 《{{ book.title }}》 {% endfor %} </td> <td> <a href="">刪除</a> <a href="">編輯</a> </td> </tr> {% endfor %} </tbody>
-
新增:
books = request.POST.getlist('books') # 獲取多個元素 author_obj = models.Author.objects.create(name=name) # 新建作者 author_obj.books.set(books) #【id,id】,給作者和書籍綁定關系 #新增示例: def author_add(request): if request.method == "POST": name = request.POST.get('name') books = request.POST.getlist('books') #列表方式獲取書籍ID ['1'] author_obj = models.Author.objects.create(name=name) #作者名寫入數據庫 author_obj.books.set(books) #寫入關聯方式 return redirect("/author_list/") all_books = models.Book.objects.all() return render(request,'author_add.html',{'all_books':all_books})
-
刪除:
def author_del(request): pk_id = request.GET.get("pk") models.Author.objects.filter(pk=pk_id).delete() return redirect('/author_list/')
-
編輯:
def author_edit(request): pk_id = request.GET.get("pk") obj = models.Author.objects.filter(pk=pk_id).first() if request.method == "POST": name = request.POST.get('name') books = request.POST.getlist('books') #列表方式獲取書籍 obj.name = name #修改作者名稱 obj.save() obj.books.set(books) #添加作者關聯的書籍地址,每次都是刪除后在添加 return redirect('/author_list//') all_books = models.Book.objects.all() return render(request,'author_edit.html',{'obj':obj,'all_books':all_books}) # 前端示例 <form action="" method="post"> <p> 作者:<input type="text" name="name" value="{{ obj.name }}"> </p> <p> 著作:<select name="books" id="" multiple> {% for book in all_books %} {% if book in obj.books.all %} <option selected value="{{ book.pk }}">{{ book.title }}</option> {% else %} <option value="{{ book.pk }}">{{ book.title }}</option> {% endif %} {% endfor %} </select> </p> <button>提交</button> </form>
模板使用:
MVC 和 MTV
-
MVC:
-
mvc是軟件中一種軟件架構模式,把軟件系統分為三個基本部分,具有耦合性低,重用性高,生命周期成本低
- M: model 模型 和數據庫交互
- V:view 視圖 展示頁面 HTML
- C: controller 控制器 調度 業務邏輯
-
MTV:
-
M : model 模型 操作數據庫 orm、
-
T : template 模板 HTML
-
V: view 視圖 業務邏輯
-
常用語法:
-
{{ 變量 }} :兩個大括號 表示變量,在模板渲染的時候替換成值
-
{{% 邏輯%}} : 括號中帶有兩個百分號表示邏輯相關操作
變量
-
{{ 變量名. }}
- .索引 .key .屬性 .方法
- 列表.索引 字典.key 類.屬性 類.方法
-
示例:
# 模板中的變量是通過views render后端返回到前端,前端通過{{}}顯示到頁面 # 注意事項:在字典中創建跟字典方法相同key 會優先調用字典中的key > 對象的屬性或方法 > 列表的索引 {{ name_list.1 }} 拿到列表第一個索引 {{ dic.name }} 通過字典的key取值 {{ dic.keys }} 字典的數據類型,keys {{ dic.values }} 字典的數據類型,values {{ p1 }} 類中方法,在類中定義str返回值,返回所有值 {{ p1.name }} 返回name屬性 {{ p1.talk }} 調用類中方法,不用加括號 #views def index(request): class Person(): def __init__(self,name,age): self.name = name self.age = age def talk(self): return "我太難了" def __str__(self): return f"Person obj:{self.name}{self.age}" booll = True num = 666 string = "小窩窩" name_list = ["海洋","俊麗","小寶寶","大寶寶"] dic = { "name":"小明", "age":"18", "sex":"18", 'hobby':["台球","籃球","足球",'羽毛球']} p1 = Person("胡海洋",18) context = { 'booll':booll, 'num':num, 'string':string, 'name_list':name_list, 'dic':dic, 'p1':p1} return render(request,'index.html',context) # templates <body> {{ booll }}<br> {{ num }}<br> {{ string }}<br> {{ name_list.1 }}<br> {{ dic.values }}<br> {{ p1}} {{ p1.name }} {{ p1.talk }} </body>
過濾器-Filters(管道)
-
修改變量的顯示結果
-
語法
-
{{ value|filter_name }} {{ value|filter_name:參數 }}
-
-
default: 提供默認值
# 傳過來的變量不存在或者為空 使用默認值,:后面不可以有空格!!! {{ qq|default:'默認值自己設置' }} # 后端變量如果無效可以增加提示 注:TEMPLATES的OPTIONS可以增加一個選項:string_if_invalid:'找不到',可以替代default的的作用。
-
列表切片
{{ name_list|slice:'::-1' }} 從后往前切片 {{ name_list|slice:'0:2' }} 切兩個值
-
filesizeformat 文件大小格式化
-
將值格式化為一個 “人類可讀的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)
{{ filesize|filesizeformat }} 后端:'filesize':1*1024*1024
-
-
**add **
數字的加法 字符串的拼接 列表的合並 {{ num|add:2 }} # 給數字進行加2,如果num為字符串類型也會進行相加 {{ string|add:"haha"}} # 通過add給string字符串進行拼接 {{ name_list|add:name_list}} # 列表拼接 {{ num|add:-2 }} 減法 {% widthratio num 1 4%} 乘法 {% widthratio num 2 1%} 除法 # 1放在中間時乘法,放在后面是除法
-
其他內置過濾器方法:
-
字符串
- 變小:{{ string|lower}}
- 變大:{{ string|upper}}
- 計數:{{ string|length}}
-
列表
-
取第一個元素: {{ name_list|first}}
-
取最后一個元素 : {{ name_list|last}}
-
字符串拼接列表: {{ name_list|join:'**'}}
-
按字符串長度取值: {{ string|truncatechars:'10'}} ,truncatewords按照單詞區分
-
-
-
日期格式化:
{{ now|date:'Y-m-d H:i:s' }} 年月日時分秒 {{ now|date}} 設置settings后使用方法 {{ now|time}} # 后端格式 'now':datetime.datetime.now() # settings 配置 USE_L10N = False DATETIME_FORMAT = 'Y-m-d H:i:s' DATE_FORMAT = 'Y-m-d' TIME_FORMAT = 'H:i:s'
-
safe 告訴django不需要“”轉義:
{{ js|safe }} {{ a|safe }} # 后端js,字符串在前端還是普通字符串,想要展示效果,添加safe不需要轉義 'js':''' <script> for (var i = 0; i < 5; i++) { alert('11111') } </script> ''' 'a':'<a href="http://www.baidu.com">跳轉</a>'
-
PY文件中不轉義方法
# 第二種方法mark_safe 在后端進行處理,前端直接調用: {{ a }} # 調用 from django.utils.safestring import mark_safe 'a':mark_safe('<a href="http://www.baidu.com">跳轉</a>')
自定義過濾器
-
在app下創建一個名為templatetags的python包(文件夾)
-
在包內創建py文件 —— 》 自定義 my_yags.py
-
在py文件中寫入:
from django import template register = template.Library() # register不能變
-
定義函數 + 加裝飾
@register.filter # new_upper(|后面的方法) value(原本的變量) arg最多有一個(冒號就后面的參數,可默認) def new_upper(value, arg=None): # arg 最多有一個 print(arg) return value.upper() # 不返回 前端結果為None
-
在模板中使用:
{% load my_yags %} # 加載寫函數定義的文件 {{ string|new_upper:dic }} # string變量,new_upper函數方法,dic后端數據,可以返回到函數arg中
for 循環
-
forloop
# for 循環格式 {% for name in name_list %} <li> {{ forloop.counter0 }} - {{ name }}</li> {% endfor %} {{ forloop.counter0 }} 字典中都已經定義好了一下參數 {{ forloop.counter }} 當前循環的從1開始的計數 {{ forloop.counter0 }} 當前循環的從0開始的計數 {{ forloop.revcounter }} 當前循環的倒敘計數(到1結束) {{ forloop.revcounter0 }} 當前循環的倒敘計數(到0結束) {{ forloop.first}} 當前循環是否是第一次循環 布爾值 {{ forloop.last}} 當前循環是否是最后一次循環 布爾值 {{ forloop.parentloop }} 當前循環父級循環的forloop(多層循環中才可以查看到)
-
empty
{% for name in name_list %} {{ name }} {% empty %} 空的數據,循環過程沒有數據返回此提示 {% endfor %}
if 判斷
-
if語句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判斷。
# 判斷列表中為偶數的變顏色,if里面可以使用過濾器 <table border="1"> <tbody> {% for name in name_list2 %} <tr> {% for new in name %} {% if forloop.counter|divisibleby:2 and forloop.parentloop.counter|divisibleby:2%} <td style="background-color: yellow">{{ new }}</td> {% else %} <td>{{ new }}</td> {% endif %} {% endfor %} </tr> {% endfor %} </tbody> </table> # 多層判斷 {% if p1.age < 18 %} 寶寶 {% elif p1.age == 18 %} 剛成年 {% else %} 上班 {% endif %} # 不支持算數運算 {% if 1 + 2 == 3 %} {% if 1|add:2 == 3 %} 可以這樣寫 # 不支持連續判斷 {% if 10 >5 > 1 %}
with和csrf_token
-
with示例:
# 取別名兩種方式 {% with alex=person_list.1.name age=person_list.1.age %} {{ alex }} {{ age }} {{ alex }} {% endwith %} {% with person_list.1.name as junli %} {{ junli }} {% endwith %}
-
{% csrf_token %}
# {% csrf_token %}標簽放在form標簽中,不安全報錯403 # form表單中有一個隱藏的input標簽(name ='csrfmiddlewaretoken'),后端通過這個值進行驗證 <form action="" method="post"> {% csrf_token %} </form>
母板和繼承
-
母板就是將公共部分提取出來,不重復的定義block,之后再進行集成填充block塊,減少重復代碼,提高效率
-
母板:
-
html頁面 提取多個頁面的公共部分
-
定義多個block塊,需要讓子頁面覆蓋填寫
# 找到重復的頁面定義bolck塊 # 第一種block塊,設置母版 <div class="col-sm-3 col-md-2 sidebar"> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> {% block content %} <h1>母版</h1> {% endblock %} </div> # 第二種block可以設置為參數 <ul class="nav nav-sidebar"> <li class="{% block pub_active%}{% endblock %}"> <li class="{% block book_active%}{% endblock %}"> <li class="{% block author_active%}{% endblock %}"> </ul> # 每個頁面的css樣式,在繼承中填寫link {% block css%} {% endblock %}
-
-
繼承母版:
-
{% extends ‘母板文件名’ %}
-
重寫block塊 ,寫在block內部
{% extends 'base.html' %} # 引用母版文件 {% block content %} <h3 class="sub-header"> 出版社列表 </h3> #填寫block塊,將代碼覆蓋添加到塊中 <div class="table-responsive"> {% endblock %} {% block book_active %} # 重寫block會覆蓋block內的內容 active {% endblock %} {% block css %} # 重寫每個頁面文件的樣式,這樣避免重復,公共樣式寫在母版中 <link rel="stylesheet" href=""> {% endblock %}
-
-
注意:
-
注意事項
-
{% extends 'base.html' %} 帶上引號 不帶的話會當做變量
-
{% extends 'base.html' %} 上面不要寫其他內容,否則會顯示出來
-
要顯示的內容寫在block塊中
-
母板可以定義多個block 定義 css js,每個子頁面繼承自己的css和js樣式
-
-
組件
-
組件就是將一段常用HTML代碼段存放單獨的html中,想調用直接引用
-
include組件示例:
#組件其實就是將代碼進行拆分 #新建一部分代碼如導航欄,存放在 nav.html中 #拆分的組件可以使用include在母版中,也可以使用在單獨的html中: {% include ‘nav.hmtl ’ %}
靜態文件相關
-
引入靜態文件:
# 從配置文件中自動找到static別名 # 第一種方式 {% load static %} <link rel="stylesheet" href="{% static 'css/dashboard.css' %}"> # 第二種方式 <link rel="stylesheet" href="{% get_static_prefix %}css/dashboard.css">
定義 filter simple_tag inclusion_tag
-
在app下創建templatetags的python包文件
-
在包內創建py文件 my_tags.py
-
在py文件中寫代碼:
from django import template register = template.Library()
-
定義函數 + 加裝飾器
-
filter 過濾器方法
@register.filter def add_arg(value,arg): # 只能接受兩個 return "{}_{}".format(value,arg) {% load my_yags %} # 加載寫函數定義的文件,不可以嵌套,可以在for循環if里使用 {{ string|new_upper:"haha" }}
-
simple_tag 方法:
# 可以接受多個參數 @register.simple_tag def join_str(*args, **kwargs): return '_'.join(args) + "*".join(kwargs.values()) # 調用方法 {% load my_tags %} {% join_str "v1" "v2" k3="v3" k4="v4"%}
-
inclusion_tag 方法
# my_tags中填寫 inclusion_tag函數 @register.inclusion_tag('page.html') # 必須制定頁面,而且是動態,要不和組件一樣 def page(num): return {'num':range(1,num+1)} # 返回必須是字典交給模板,接受HTML返回的參數,返回給page # 前端頁面調用 page函數 {% load my_tags %} {% page 5%} # page.heml 頁面添加一些分頁功能,接受page發送剛過來的參數num </li> {% for i in num %} <li><a href="#">{{ i }}</a></li> {% endfor %} <li>
視圖
-
視圖系統,是一個簡單的python函數,接受web傳過來的請求,並向web進行響應,響應可以是html,重定向,錯誤提示啊,無論你的視圖包含什么邏輯,都需要返回響應
-
FBV: function based view
-
CBV: class based view
-
cbv示例:
from django.views import View class AddPublisher(View): def get(self,request): #處理get請求 return xx def post(self,request): #處理post請求 return xx # urls調用 url(r'^publisher_add/',views.AddPublisher.as_view() ),
-
CBV的流程
-
看這個cbv流程主要是要知道request和get是怎么執行的
-
項目啟動時,運行urls.py
url(r'^publisher_add/',views.AddPublisher.as_view() ) # 執行as_view() 函數 AddPublisher.as_view() 執行 ——> view函數 # 將此函數AddPublisher傳給 as_view() # view函數 def as_view(cls, **initkwargs): # cls = AddPublisher def view(request, *args, **kwargs): # reqyest 前端的請求 self = cls(**initkwargs) # AddPublisher實例化對象self if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request # 類中使用的request就是self.request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) # 調用dispatch方法 view.view_class = cls view.view_initkwargs = initkwargs update_wrapper(view, cls, updated=()) update_wrapper(view, cls.dispatch, assigned=()) return view
-
請求到來時,實行view函數:
-
實例化AddPublisher ——> self
-
self.request = request
-
執行View中self.dispatch(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: # 判斷請求是否被准許,方法有八種 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 反射,判斷請求是否錯誤 else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) # 執行 handler=get加括號 # 判斷請求方式是否被允許 允許 通過反射獲取請求方式對應的方法 ——> handler 不允許 self.http_method_not_allowed ——> handler # 返回的是錯誤代碼信息 執行handler 獲取到響應
-
視圖加裝飾器
-
裝飾器函數:
# 視圖添加裝飾器,先創建好裝飾器 def timer(func): def inner(*args,**kwargs): start = time.time() ret = func(*args,*kwargs) print(f"{time.time()-start}") return ret return inner
-
FBV 函數添加裝飾器直接加:
@timer def publisher_edit(request): #編輯數據 pass
-
CBV 類中方法添加裝飾器:
# 第一種在方法中添加: from django.utils.decorators import method_decorator class AddPublisher(View): @method_decorator(timer) def get(self, request): # 處理get請求 print('get') return render(request, 'publisher_add.html') # 第二種在dispatch上添加,這樣所有請求都帶有裝飾器功能: @method_decorator(timer) def dispatch(self, request, *args, **kwargs): print("dispatch 前") ret = super().dispatch(request,*args,**kwargs) print("dispatch 后") return ret # 第三種加在類上面: @method_decorator(timer,name='post') @method_decorator(timer,name='get') @method_decorator(timer,name='dispatch') class AddPublisher(View): pass
request:
-
獲取請求參數:
request.method # 請求方式 GET POST request.GET # url上攜帶的參數 {} get() request.POST # post請求提交的參數 {} get() request.META # http頭部的信息,具體信息取決於客戶端和服務器,獲取頁面request headers request.body # 請求體的數據 post也是在body里面取值的 request.path_info # 獲取路徑信息 不包含IP和端口 也不包含查詢參數 request.FILES # 獲取上傳的文件 request.scheme # 獲取前端是http或者是https字符串 request.COOKIES # cookie 記錄狀態 鍵值對存儲在瀏覽器 request.session # session 記錄狀態,鍵存儲在瀏覽器,值存儲在數據庫中,更安全些 request.get_full_path() # 完整的路徑信息 包含查詢參數 不包含IP和端口 request.is_ajax() # 判斷是否是ajax請求
-
上傳文件注意事項:
# form表單的屬性 enctype="multipart/form-data" # input type = 'file' # request.FILES.get('f1') class File_upload(View): def get(self,request): return render(request,'file_upload.html') def post(self,request): file_obj = request.FILES.get('f1') with open(file_obj.name,'wb') as f: for i in file_obj.chunks(): f.write(i) return HttpRespose("ok") # 前端頁面: <form action="" method="post" enctype="multipart/form-data"> <p> 上傳文件:<input type="file" name="f1" multiple> </p> <button>上傳</button> </form>
response
-
HttpResponse('字符串') # 返回字符串
-
render(request,'模板的文件名',{}) # 返回一個完整的頁面
def render(request, template_name, context=None, content_type=None, status=None, using=None): # content loader.render_to_string 這里已經將模板頁面進行渲染完成 content = loader.render_to_string(template_name, context, request, using=using) # 將content內容封裝成httpresponse對象 return HttpResponse(content, content_type, status)
-
redirect('要跳轉的地址') # 重定向 響應頭 Location: url
-
301: url后面不添加/ 302:添加/會直接永久重定向
def redirect(to, *args, **kwargs): if kwargs.pop('permanent', False): redirect_class = HttpResponsePermanentRedirect # 301 暫時重定向 else: redirect_class = HttpResponseRedirect # 302 永久重定向 return redirect_class(resolve_url(to, *args, **kwargs))
-
前后端分離-JSON:
# 第一種json傳參方法: import json def get_data(request): data = {'status':0,'data':{'k1':'v1'}} return HttpResponse(json.dumps(data),content_type='application/json') # 第二種簡單方法(常用,前端自動進行反序列化): def get_data(request): data = {'status':0,'data':{'k1':'v1'}} return JsonResponse(data) # 不是字典,列表的話需要添加safe=False from django.http.response import JsonResponse def get_data(request): l1 = ["1",'2','3'] return JsonResponse(l1,safe=False)
路由
-
路由的本質就是,url與調用的視圖函數之間的映射表,也就是說你訪問我哪個url路由執行相對應的函數
-
路由匹配的只是路徑,獲取的參數永遠只是字符串
-
urls的基本配置
from django.conf.urls import url from django.contrib import admin from app import views urlpatterns = [ url(正則表達式, views視圖,參數,別名), ]
正則表達式
-
正則規則
-
以什么開頭^ 結尾$ [0-9] [a-zA-Z]{4}
-
. 數字:\d 數字和字母:\w 0個或者多個:? 至少一個:+ 0和或者無數個:*
-
-
從上到下的進行匹配 匹配到一個不再往下進行匹配,開頭不加 / ,游覽器后面/自動添加
urlpatterns = [ url(r'^admin/', admin.site.urls), # r轉義,^以什么開頭,前面不加/ url(r'^blog/$', views.bolg), # 如果上下第一層目錄相同,添加$表示結尾 url(r'^blog/[0-9]{4}/[0-9]{2}/$', views.bolgs), # 簡單的動態路由可以匹配多個 ]
分組和命名分組
-
從URL上獲取到的參數的類型都是字符串,獲取的參數按照位置參數傳遞給視圖函數
# urls urlpatterns = [ url(r'^blog/([0-9]{4})/([0-9]{2})/$', views.bolgs),] # views def bolgs(request,year,month): #year和month按照順序接受分組前端返回信息 print(year) print(month) return HttpResponse("首頁")
-
命名分組:獲取的參數按照關鍵字參數傳遞給視圖函數,視圖中實參可以指定默認值
urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.bolgs), ] def bolgs(request,year,month): #year和month按照順序接受分組前端返回信息,前后端命名相同 print(year) print(month) return HttpResponse("首頁")
路由分發include:
-
分發示例:
# luyou from django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), # url(r'^',include('ap.urls')), # 匹配時為空,跟之前相同都是一級目錄 url(r'^app/',include('app.urls')), # 匹配app中的目錄 url(r'^app01/',include('app01.urls')) # 可以匹配多個app ] # app from django.conf.urls import url from app import views urlpatterns = [ url(r'^blog/$', views.bolg), #$表示結尾 url(r'^blog/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.bolgs), ]
命名URL和URL反向解析
-
命令url和反向解析,解決了路徑寫死問題,讓路徑修改更加靈活
-
命名url:
urlpatterns = [ url(r'^admin/', admin.site.urls,), url(r'^publisher_list/',views.publisher_list.as_view(),name='publisher'),]
-
反向解析:
-
模板
# 模板解釋方法,通過命令url中的設置的name,獲取到真實路徑 <a href="{% url 'publisher' %}">出版社列表 </a>
-
py文件
# py文件中使用,也是通過name獲取到路徑 from django.urls import reverse def AddPublisher(request): return redirect(reverse('publisher')) #redirect('publisher') 相同
-
-
分組命名URL
-
分組命令url
# url命名動態路由 urlpatterns = [ url(r'^blogs/([0-9]{4})/([0-9]{2})/$', views.bolgs,name='blogs'), url(r'^home/$', views.home), ]
-
反向解析
-
模板
# 模板中反向解析 <a href="{% url 'blogs' '2019' '12' %}">xxx</a> # views def home(request): return render(request,'home.html')
-
py文件
def home(request): return redirect('blogs','2020','10')
-
-
-
命名分組URL(了解)
-
分組命名
url(r'^blogs/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.blogs,name='blogs'),
-
反向解析
-
模板
<a href="{% url 'blogs' year='2019' month='13' %}">xxx</a>
-
py文件
from django.urls import reverse reverse('blogs',args=('2019','09')) ——> /app01/blogs/2019/09/ reverse('blogs', kwargs={'year': '2019', 'month': '12'}) ——> /app01/blogs/2019/12/
-
-
namesapce 名稱空間
-
名稱空間示例,解決命名分組相同問題
# app,app01 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^app/',include('app.urls',namespace='app')), url(r'^app01/',include('app01.urls',namespace='app01')), ] # app urlpatterns = [ url(r'^home/$', views.home,name='home'),] def home(request): return render(request,'home.html') # app01 urlpatterns = [ url(r'^home/$', views.home,name='home'),] def home(request): return render(request,'home1.html')
-
反向解析
# app:blogs 起了相同的name時,根據名稱空間來區分 <a href="{% url 'app:blogs' year='2019' month='13' %}">xxx</a>
示例:
-
html模板
<a href="{% url 'delete' 'author' author.pk %}">刪除</a>
-
urls
url(r'(\w+)_del/(\d+)/$',views.delete,name='delete')
-
views
def delete(request, table, pk): print(table, pk) # 查詢到對應的數據進行刪除 model_cls = getattr(models, table.capitalize(),) model_cls.objects.filter(pk=pk).delete() # 重定向 return redirect(reverse(table))
ORM
-
orm對象關系映射,對象和關系型數據庫的對應關系,將程序中的對象保存到數據庫中
- 根據對象類型生成表結構
- 將對象操作轉為sql語句,在將sql語句查詢到的結果轉換為對象
- 作用:減少了開發的工作量,不用面對數據庫操作
-
注意不同的django和不同版本的mysql存在兼容性
- 類 表
- 屬性 字段
- 對象 數據行
-
str
# str返回的只是顯示出來信息 def __str__(self): return f"{self.pid}{self.name}{self.age}{self.bith}"
admin
-
創建一個超級用戶
-
python36 manage.py createsuperuser admin
-
輸入用戶名和密碼
-
注冊
-
在app下的admin.py中寫
from django.contrib import admin from app01 import models admin.site.register(models.Person) #注冊 Person(model中的表名)
-
-
登陸網頁http://127.0.0.1:8000/admin/(找到對應的表做增刪改查)
-
修改賬號密碼:python manage.py changepassword admin
腳本運行
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lainxi.settings")
django.setup()
from app01 import models #放在最后面引入
ORM 常用字段
-
常用字段
-
AutoField 主鍵
#自增的整形字段,必填參數primary_key=True,則成為數據庫的主鍵。無該字段時,django自動創建。 #一個model不能有兩個AutoField字段。 pid = models.AutoField(primary_key=True)
-
CharField 字符串
#字符類型,必須提供max_length參數。max_length表示字符的長度。 name = models.CharField(max_length=32)
-
IntegerField 整型
#-21億到正21億 age = models.IntegerField()
-
DecimalField 十進制小數
max_digits=5 #總長度五位 decimal_places=2 #小數位長度 999.99 price = models.DecimalField(max_digits=5,decimal_places=2,default=0)
-
DateTimeField 日期時間
auto_now和auto_now_add和default參數是互斥的,不能同時設置 # auto_now=True 每次修改時間,都會變化 bith = models.DateTimeField(auto_now=True) # auto_now_add=True #新增數據,自動添加時間 bith = models.DateTimeField(auto_now_add=True)
-
-
其他字段
BooleanField(Field) #布爾值 NullBooleanField(Field) #可以為空的布爾值 BigIntegerField(IntegerField) #存入的數字更多 BigAutoField(AutoField) #自增存入的數字更多 SmallIntegerField(IntegerField) #小整數 -32768 ~32767 FloatField(Field) #浮點數 TextField #存儲大字符串多內容 EmailField(CharField) #判斷字符串是否是email格式 IPAddressField(Field) #判斷字符是否是IPV4格式 URLField(CharField) #判斷是否是正常的url地址 UUIDField(Field) #uuid格式校驗 FilePathField(Field) #存儲文件路徑 FileField(Field) #存儲文件 ImageField(FileField) #存儲圖片 DateField() #日期格式 TimeField() #時間格式
-
自定義char字段
class MyCharField(models.Field): # 自定義的char類型的字段類 def __init__(self, max_length, *args, **kwargs): #init可以自定義,不寫源碼里面也有 self.max_length = max_length super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs) def db_type(self, connection): #限定生成數據庫表的字段類型為char,長度為max_length指定的值 return 'char(%s)' % self.max_length #調用: phone = MyCharField(max_length=11)
字段參數
-
null=True 數據庫該字段可以為空
-
blank=True 校驗時可以為空,from表單可以不填
-
default 默認值
-
unique 唯一索引,有重復的添加不了
-
verbose_name admin頁面顯示的名稱
-
choices=((True, '男'), (False, '女')) 可選擇的參數
# 單選,通過bool進行設置,true為1 false為0,默認為男1 gender = models.BooleanField(default=True,choices=((True,'男'),(False,'女'))) gender = models.BooleanField(max_length=32,choices=((1,'男'),(2,'女'),(3,'xxx')))
-
其他參數:
-
db_column 跟數據庫關聯時使用,關聯數據庫中的列名
-
db_index=True 數據庫字段創建索引
-
editable=False 不可編輯,默認為True
-
help_text admin頁面顯示提示信息
-
表的參數
class Person(models.Model):
pid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32,db_column='username',db_index=True)
age = models.IntegerField(null=True,blank=True,editable=False)
bith = models.DateTimeField(auto_now=True)
phone = MyCharField(max_length=11,null=True,blank=True)
gender = models.BooleanField(default=True,choices=((True,'男'),(False,'女')))
class Meta:
db_table = "person" #數據庫中生成表名稱 默認app名稱+下划線+類名
verbose_name = '個人信息' # admin中顯示的表名稱
verbose_name_plural = '所有用戶信息' # admin中導航欄修改
index_together = [
("name", "age"), # 聯合索引,兩個存在的字段
]
unique_together = (("name", "age"),) # 聯合唯一索引,不可重復
必會13條查詢語句:
-
在文件中查詢引入配置:
import os import django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled.settings") django.setup() from app01 import models
-
查詢語句
# all 獲取表中所有數據,結果是對象列表:QuerySet ret = models.Person.objects.all() # filter 獲取所有滿足條件的數據,結果也是對象列表:QuerySet ret = models.Person.objects.filter(pk=1) # first和last 獲取第一個/最后一個 拿不到值為none,結果為對象 ret = models.Person.objects.filter().first() # get 獲取到一個滿足條件的數據,存在而且唯一,查不到或者多個報錯,結果是對象 ret = models.Person.objects.get(pk=1) # exclude 獲取不滿足條件結果,取反,結果是對象列表:QuerySet ret = models.Person.objects.exclude(pk=1) #order_by 按照字段排序,默認升序,降序為'-pid', ret = models.Person.objects.all().order_by('pid') ret = models.Person.objects.all().order_by('age','-pid') #多個字段排序,先排前面 #reverse 只能對已經排序的QuerySet進行翻轉 ret = models.Person.objects.all().order_by('pid').reverse() #values 獲取對象的字段名和字段值,結果是對象列表:QuerySet,列表里面為字典 ret = models.Person.objects.all().values() ret = models.Person.objects.all().values('pid','name') #可以指定字段 ret = models.Person.objects.all().values_list('pid','name') #拿取字段值 #distinct 只能是一樣的對象才可以去重,distinct中不可填寫字段 ret = models.Person.objects.all().distinct() #只能去重相同對象 ret = models.Person.objects.values('age').distinct() #這樣可以去重 #count 計數 ret = models.Person.objects.all().count() # exists 判斷數據是否存在,結果為布爾值 ret = models.Person.objects.filter(pk=1).exists() print(ret) 返回queryset:all filter exculde values(字典) values_list() order_by reverse distinct 返回對象:get first last 返回布爾值:exists 返回數字:count
-
單表的雙下划線(字段__過濾條件)
-
大於小於
ret = models.Person.objects.filter(pk__gt=1) #id大於1(greater than) ret = models.Person.objects.filter(pk__lt=5) #id小於5(less than) ret = models.Person.objects.filter(pk__gte=1) #id大於等於1 (greater than equal) ret = models.Person.objects.filter(pk__lte=5) #id小於等於5 (greater than equal)
-
查詢范圍
ret = models.Person.objects.filter(pk__range=[1,3]) ret = models.Person.objects.filter(pk__in=[1,3,5]) #in 查詢多個
-
模糊查詢(like),查當前字段中有g的數據
ret = models.Person.objects.filter(name__contains='g') #查詢包含g的字段 ret = models.Person.objects.filter(name__icontains='g') #ignore 忽略大小寫 ret = models.Person.objects.filter(name__startswith='h') #查詢以h開頭的 ret = models.Person.objects.filter(name__istartswith='h') #ignore 忽略大小寫 ret = models.Person.objects.filter(name__endswith='g') #查詢以g結尾的 ret = models.Person.objects.filter(name__iendswith='g') #ignore 忽略大小寫
-
查詢時間
ret = models.Person.objects.filter(bith__year='2020') #查看年份 ret = models.Person.objects.filter(bith__contains='09') #查看月份 ret = models.Person.objects.filter(bith__contains='2019-09') #查看年月
-
查詢為空數據
ret = models.Person.objects.filter(age__isnull=True)
-
外鍵查詢:
-
對象正向查詢
#外鍵的正常查詢可以取到書籍是哪個出版社出版的 book_obj = models.Book.objects.get(pk=1) print(book_obj) #關聯的對象 print(book_obj.pub_id) #關聯的對象的id print(book_obj.pub.name) #關聯的對象的id,通過書籍查詢到出版社
-
對象反向查詢
pub_obj = models.Publisher.objects.get(pk=1) print(pub_obj.name) #當前出版社 print(pub_obj.book_set) #關系管理對象 不指定related_name print(pub_obj.book_set.all()) #關聯的所有對象,根據出版社ID查詢書籍 # 外鍵 related_name publisher = models.ForeignKey(to="Publisher", related_name='books') print(pub_obj.book.all()) #在models中指定related_name,替換book_set
-
基於字段查詢
#表中有外鍵的話,直接通過外鍵__字段 ret = models.Book.objects.filter(pub__name="沙河出版社") #__跨表查詢,根據出版社拿書 ret = models.Book.objects.filter(pub__name__contains="沙河") #表中沒有外鍵,直接通過表名__字段 ret = models.Publisher.objects.filter(book__title='神雕俠侶') #根據書拿出版社 #指定related_name 使用類名小寫,related_query_name='book' 后面覆蓋前面 ret = models.Publisher.objects.filter(books__title='神雕俠')
外鍵關系管理對象:
-
SET不可用,set不能通過ID添加,只能添加對象
-
add
#外鍵添加數據,修改書籍的出版社 pub_obj = models.Publisher.objects.get(pk=1) pub_obj.book_set.add(*models.Book.objects.filter(pk__in=[2,4,6]))
-
remove
#外鍵刪除,字段要為空 pub_obj.book_set.remove(*models.Book.objects.filter(pk__in=[2,4,6]))
-
clear
pub_obj.book_set.clear() #清空
-
create
pub_obj.book_set.create(title='小白',pub_id=2) #添加書籍和出版社,不填默認自己1
多對多
-
對象正向查詢(跟外鍵使用方法一樣)
auth_obj = models.Author.objects.get(pk=1) print(auth_obj.name) print(auth_obj.books) #關聯管理對象 print(auth_obj.books.all()) #關聯對象 通過作者找書籍
-
對象反向查找(跟外鍵使用方法一樣)
book_obj = models.Book.objects.get(pk=1) print(book_obj.title) print(book_obj.author_set) #關聯管理對象 print(book_obj.author_set.all()) #關聯對象 通過書籍找作者
-
基於字段查詢
ret = models.Book.objects.filter(author__name='俊麗') #通過作者找書名 ret = models.Author.objects.filter(books__title='少有人走的路') #通過書名找作者
多對多關系管理對象
-
set:添加之前會刪除清空
author_obj = models.Author.objects.get(pk=1) author_obj.books.set([1,2]) #set [存放的是關聯的ID,可以是多個],之前的會覆蓋掉 author_obj.books.set(models.Book.objects.filter(pk__in=[1,2])) #set [也可以是對象]
-
add :新增新的關系,之前不變,重復添加之前的也不變
# add添加是不會覆蓋刪除,而是新增 author_obj = models.Author.objects.get(pk=1) author_obj.books.add(3) #添加ID author_obj.books.add(models.Book.objects.get(pk=4)) #添加對象 author_obj.books.add(*models.Book.objects.filter(pk__in=[3,4])) #添加多個對象,*打散
-
remove 刪除
author_obj = models.Author.objects.get(pk=1) author_obj.books.remove(2) #跟add語法一樣 author_obj.books.remove(*models.Book.objects.filter(pk__in=[3,4]))
-
clear 清空
# 將關系全部清空 author_obj = models.Author.objects.get(pk=1) author_obj.books.clear()
-
create 新增
#給作者添加書籍和出版社 author_obj = models.Author.objects.get(pk=2) author_obj.books.create(title='小紅書',pub_id=1) #通過作者2對象,添加一本小紅書和出版社1的id #給書籍添加作者 book_obj = models.Book.objects.get(pk=2) book_obj.author_set.create(name='小黑') #先查找書籍,在通過書籍對象反向寫入作者
聚合
-
引入聚合函數
from django.db.models import Max,Min,Count,Avg,Sum
-
aggregate
#統計書中最高的價格,整個字段,aggregate為終止方法,后面不可在添加方法了 ret= models.Book.objects.aggregate(max=Max('price'),min=Min('price')) #先進行篩選,選擇范圍之后在使用聚合函數 ret=models.Book.objects.filter(pk__range[3,7]).aggregate(max=Max('price'),min=Min('price')) print(ret['max']) #返回的是字典,使用key來取值
分組
-
統計每一本數的作者個數
#book:按照book_id進行分組 annotate:填寫聚合函數(將count結果注釋到Book中) ret = models.Book.objects.annotate(count=Count('author')).values() for i in ret: print(i)
-
統計出每個出版社的最便宜書的價格 分組聚合
# 方法一 # 先使用出版社進行分組,annotate注釋 填寫聚合函數,將結果寫入對象中,以出版社為准 ret= models.Publisher.objects.annotate(min=Min('book__price')).values() for i in ret: print(i['min']) # 方法二!!! #values分組條件 #以書籍為准 ret = models.Book.objects.values('pub__id').annotate(min=Min('price')) for i in ret: print(i['min'])
-
統計不止一個作者的圖書-篩選
# 先分組聚合,之后進行篩選 ret = models.Book.objects.annotate(count=Count('author')).filter(count__gt=1) for i in ret: print(i.title)
-
根據作者數量,進行-排序
# 先分組聚合,之后排序 ret = models.Book.objects.annotate(count=Count('author')).order_by('-count') for i in ret: print(i.title)
-
查詢各個作者書的總價格
#第一種方法:通過作者分組,反向取price 以作者表進行左連接 ret = models.Author.objects.annotate(sum=Sum('books__price')) for i in ret: print(i.name,i.sum) #第二種方法:通過作者分組,在通過錢來取值 以書進行左連接,所以會出現none情況 ret = models.Book.objects.values('author').annotate(sum=Sum('price')) for i in ret: print(i #轉成sql過程 1.先把book表和authro進行left join連表 2.連完表之后進行group by分組 3.之后再進行函數取值
F和Q
-
save和update字段區別:
-
save會全部都保存一遍
-
update會只針對查詢的數據進行保存,!!!
-
-
F 兩個字段比較,跟子查詢很像
F兩個字段進行比較(里面不能進行聚合和models,只能使用字段)
from django.db.models import F #sale字段大於(gt)num字段的有哪些 ret = models.Book.objects.filter(sale__gt=F('num')) #篩選book表pk字段等於1,針對這個行數據的sale進行修改 ret = models.Book.objects.filter(pk=1).update(sale=F('sale') *2 + 10)
-
Q 或與非
from django.db.models import F,Q ret = models.Book.objects.filter(Q(pk__gt=5)|Q(pk__lt=2)) #大於5或者小於2 ret = models.Book.objects.filter(~Q(pk__gt=5)&Q(pk__lt=2)) #與 ret = models.Book.objects.filter(~Q(pk__gt=5)|Q(pk__lt=2)) #小於等於5或者小於2(~非) ret = models.Book.objects.filter(Q(~Q(pk__gt=5)&Q(pk__lt=2))|Q(pk__lt=2)) #設置多個匹配
事務
-
事務把一系列操作,可能是多條的,我去執行,要么成功要么都失敗,當出現問題全部都要回滾回去,保證原子性
# 驗證事務 from django.db import transaction try: with transaction.atomic(): book1 = models.Book.objects.get(pk=1) book2 = models.Book.objects.get(pk=2) book1.num -=50 book1.save() int("sssss") #默認是有事務,try int的錯誤 book2.num += 50 book2.save() except Exception as e: print(e)
Cookie
-
cookie就是保存在瀏覽器上的一組鍵值對,這個鍵值對是服務器發送出來存儲在瀏覽器上,當瀏覽器在訪問服務器時,會自動攜帶這些鍵值對,服務器接受后可以利用處理這些鍵值對
-
為什么要有cookie:HTTP協議是沒有狀態的,每次請求都是獨立的,不會受前面的結果影響
cookies和session區別:
-
應為http本身是無狀態的,所以使用cookies和session都是用來記錄客戶端的狀態
-
cookies是以文本鍵值對存儲在瀏覽器中,session是存儲在服務端
-
cookies的存儲數量有限只有4kb,而session是無限量的
-
還有就是安全問題,我們可以輕松訪問到cookie值,但是我們無法直接訪問會話值,因此session更安全
原理
-
首先客戶端向服務端發送一個post請求,提交賬號密碼
-
服務器校驗賬號密碼,正確向瀏覽器寫入登錄成功狀態
-
瀏覽器再次請求時,會自動攜帶cookie的狀態在發送給服務器,服務器在判斷狀態
-
特性:
-
服務器讓瀏覽器進行設置的,鍵值對保存在瀏覽器
-
瀏覽器下次訪問事自動攜帶對應的cookie
-
-
應用:
-
登陸,記錄登錄狀態
-
投票,投票記錄狀態
-
記錄網頁的瀏覽習慣,分頁多少條數據
-
django中操作cookies
-
設置
#設置cookie Set-Cookie: is_login=1; 添加響應頭 ret.set_cookie('is_login','1000') #設置加密的cookie Set-Cookie: is_login=1 ret.set_signed_cookie('is_login', '1000', '鹽')
-
獲取
is_login = request.COOKIES.get('is_login') #讀取cookies if is_login != '1000': #判斷是否有cookies #獲取加密鹽的cookie request.get_signed_cookie('is_login',default='',salt='鹽') #獲取不到值,設置一個默認值
-
刪除
def logout(request): ret = redirect('/login/') ret.delete_cookie('is_login') #刪除cookie return ret
參數:
-
key, 鍵 value='', 值
-
expires=None, IE 瀏覽器的超時時間
-
max_age=None 瀏覽器超時時間,沒有設置瀏覽器關閉就失效
ret.set_signed_cookie('is_login', '1000', '鹽',max_age=10) #超過10秒cookei失效!!
-
path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問
#針對某個目錄設置cookie地址,沒有cookie不能訪問 ret.set_signed_cookie('is_login', '1000', '鹽',path='/home/')
-
domain=None, Cookie生效的域名,默認對所有域名生效
-
secure=False, https傳輸 為true是有為https協議時候才傳輸cookei
-
httponly=False 只能http協議傳輸,無法被JavaScript獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)
Cookie應用示例:
-
網頁登錄:
from django.shortcuts import render,redirect,HttpResponse def login_required(func): def inner(request,*args,**kwargs): # is_login = request.COOKIES.get('is_login') #讀取cookies is_login = request.get_signed_cookie('is_login', default='', salt='鹽') if is_login != '1000': #沒cookies執行login,url上添加當前路徑 return redirect(f'/login/?returnurl={request.path_info}') ret = func(request,*args,**kwargs) #執行home或者index return ret return inner def login(request): if request.method == "POST": user = request.POST.get('user') pwd = request.POST.get('password') if user == 'haiyang' and pwd =='123': #效驗登錄狀態 returnurl = request.GET.get('returnurl') #獲取是否有要跳轉的地址 if returnurl: ret = redirect(returnurl) else: ret = redirect('/home/') #登錄成功要調轉的地址 # ret.set_cookie('is_login','1000') #設置cookie Set-Cookie: is_login=1; 添加響應頭 ret.set_signed_cookie('is_login', '1000', '鹽',path='/home/') return ret return render(request,'login.html',{'error':'用戶名錯誤'}) #密碼錯誤返回 return render(request,'login.html') @login_required #訪問home頁面調用裝飾器 def home(request): #home頁面 return HttpResponse("home ok") @login_required def index(request): #index頁面 return HttpResponse("index ok") def logout(request): #刪除cookie ret = redirect('/login/') ret.delete_cookie('is_login') return ret #前端代碼 <form action="" method="post"> {% csrf_token %} <p>用戶名:<input type="text" name="user"></p> <p>密碼:<input type="text" name="password"></p> <p style="color: red">{{ error }}</p> <button>登錄</button> </form>
session
-
session會話控制,保存在服務器上一組組鍵值對 ,但是必須依賴於cookie。
-
為什么session要就與cookie,沒有cookie,建立連接就生成一個session_id,那么打開幾個頁面就是幾個session,使用cookie就可以把session_id保存在cookie中,每次訪問攜帶
-
為什么要用session
-
session保存在服務器上,安全
-
cookie 保存在瀏覽器上 不安全
-
瀏覽器對cookie的大小有限制
-
-
session原理圖
-
session流程:
- 首先客戶端向服務端發送一個post請求,提交賬號密碼
- 服務器校驗賬號密碼,效驗正確,向瀏覽器返回
- 瀏覽器再次請求時,會自動攜帶session_id發送給服務器,服務器通過session_id拿取到值
-
數據庫中session標識
-
session表名:django_session
-
session_id
-
session_data
-
expire_date:超時時間,默認兩周
-
-
django中的操作:
-
設置session
request.session['is_login'] ='1000' request.session['is_login1'] ='1000' #可以設置多個鍵值對 request.session.setdefault('k1',123) #存在則不設置 del request.session['is_login'] #刪除鍵值對
-
獲取
#兩種獲取方式 request.session.get('is_login') request.session['is_login'] #可以當成字典使用 request.session.keys() request.session.values() request.session.items() #設置多個鍵值對可以都取到
-
刪除
request.session.delete() # 刪除session 數據 不刪除cookie request.session.flush() # 刪除session 數據 刪除cookie def logout(request): #刪除session ret = redirect('/login/') request.session.flush() return ret
-
session方法:
request.session.session_key #拿到數據庫中的key request.session.clear_expired() #將所有Session失效日期小於當前日期的數據刪除 request.session.exists("session_key") # 檢查會話session的key在數據庫中是否存在 request.session.set_expiry(value) # 設置會話Session和Cookie的超時時間 * 如果value是個整數,session會在些秒數后失效。 * 如果value是個datatime或timedelta,session就會在這個時間后失效。 * 如果value是0,用戶關閉瀏覽器session就會失效。 * 如果value是None,session會依賴全局session失效策略。
-
session設置:
#查看session配置 在settings中設置 from django.conf import global_settings #每次訪問保存session,默認為flase 設置后在兩周內在此訪問,延長兩周 SESSION_SAVE_EVERY_REQUEST = True SESSION_EXPIRE_AT_BROWSER_CLOSE = False #關閉瀏覽器cookie就失效了 1. 數據庫Session SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默認) 2. 緩存Session SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 # 使用的緩存別名(默認內存緩存,也可以是memcache),此處別名依賴緩存的設置 SESSION_CACHE_ALIAS = 'default' 3. 文件Session SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 # 緩存文件路徑,如果為None,則使用tempfile模塊獲取一個臨時地址tempfile.gettempdir() SESSION_FILE_PATH = None 4. 緩存+數據庫 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 5. 加密Cookie 把Session數據放在cookie里 SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 其他公用設置項: SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路徑(默認) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默認) SESSION_COOKIE_SECURE = False # 是否Https傳輸cookie(默認) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http傳輸(默認) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默認) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否關閉瀏覽器使得Session過期(默認) SESSION_SAVE_EVERY_REQUEST = False # 是否每次請求都保存Session,默認修改之后才保存(默認)
session代碼示例:
-
網頁登錄
from django.shortcuts import render,redirect,HttpResponse def login_required(func): def inner(request,*args,**kwargs): is_login = request.session.get('is_login') # 通過session去數據庫中拿取key print(is_login) if is_login != '1000': #沒session執行login,url上添加當前路徑 return redirect(f'/login/?returnurl={request.path_info}') ret = func(request,*args,**kwargs) #執行home或者index return ret return inner def login(request): request.session.clear_expired() # 設置清除已經失效的session數據 if request.method == "POST": user = request.POST.get('user') pwd = request.POST.get('password') if user == 'haiyang' and pwd =='123': #效驗登錄狀態 returnurl = request.GET.get('returnurl') #獲取是否有要跳轉的地址 if returnurl: ret = redirect(returnurl) else: ret = redirect('/home/') #登錄成功要調轉的地址 #設置session,使用cookie將session_id結果傳給瀏覽器 request.session['is_login'] = '1000' request.session.set_expiry(10) #設置超時時間 return ret return render(request,'login.html',{'error':'用戶名錯誤'}) #密碼錯誤返回 return render(request,'login.html') @login_required def home(request): return HttpResponse("home ok") @login_required def index(request): return HttpResponse("index ok") def logout(request): #刪除session ret = redirect('/login/') request.session.delete() return ret
中間件
-
django的中間件是用來處理django請求和響應,框架級別的鈎子(類似於裝飾器,添加新的內容),他是一個輕量,低級別的插件系統,也就是,我是用插件就改變,每個中間件組件都負責做一些特定的功能
-
添加插件,影響的是全局,謹慎使用,使用不當會影響性能,注意
-
django中間件就是一個類,五個方法,四個特點
定義中間件
-
settings 中間件配置
-
配置中注冊APP
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', #中間件沒有返回值走下面 'app01.mymiddleware.MD1', 'app01.mymiddleware.MD2',] #執行urls #執行視圖
-
process_request
-
執行時間:
- 在路由前面執行
-
參數:
- request:process_request中的request和views中接受的請求是同一個
-
順序:
- 多個process_request,按照中間件的注冊順序,順序執行
-
返回值:
-
None:為none是正常流程
-
HttpResponse:當前中間件中有return之后,路由匹配,視圖函數都不執行了,如果有response,直接執行當前中間件process_response的方法,倒序執行之前的process_response方法,最終返回給瀏覽器
-
process_response
-
執行時間:
- 在視圖函數后面執行
-
參數:
- request:process_request中的request和views中接受的請求是同一個
- response:視圖返回的response對象
-
順序:
- 按照中間件的注冊順序,倒序執行
-
返回值:
-
HttpResponse:必須return返回視圖的Response對象,返回值也可以自己返回
def process_response(self,request,response): #response響應對象 print("MD2 process_response") # return response return HttpResponse('ok98')
-
process_view
-
執行時間:
- 在路由匹配之后,視圖函數之前
-
參數:request response:
- request 請求都是同一個
- view_func:視圖函數 (function index at 0x31435345242134)
- view_args,:位置參數,urls中的分組信息
- vies_kwargs:關鍵字參數,命名分組
-
順序:
- 按照中間件的注冊順序,順序執行
-
返回值:
-
None:正常流程
-
HttpResponse:當前中間件之后的process_view不執行了,視圖函數都不執行了,倒序執行中間件中的process_response方法,最終返回給瀏覽器
-
process_exception
-
執行時間:
- 視圖層有錯誤異常觸發條件,執行
-
參數:request response:
- request 請求對象都是同一個
- exception:錯誤對象
-
順序:
- 按照中間件的注冊順序,在視圖函數之后都是倒序執行
-
返回值:
-
None:交給下一個中間件處理異常,所有的中間件都沒有處理的話,django最后進行處理
-
HttpResponse:中間件之前的process_exception不執行了,直接執行最后一個方法,倒序執行中間件中的process_response方法,最終返回給瀏覽器
-
process_template_response
-
執行時間:
- 視圖返回的reqponse是一個template_response 模板對象
-
參數:request response:
- request 請求都是同一個
- response:響應對象
-
順序:
- 按照中間件的注冊順序,倒序執行
-
返回值:
-
HttpResponse:必須返回process_template_response對象,返回的結果,以最后一個為准,倒序執行中間件中的process_response方法,最終返回給瀏覽器
-
中間件示例
-
代碼示例
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class MD1(MiddlewareMixin): def process_request(self,request): #處理請求,request和views請求一樣 print("MD1 process_request") # return HttpResponse("md1") def process_response(self,request,response): #response響應對象 print("MD1 process_response") return response def process_view(self,request,view_func,view_args,vies_kwargs): # print(view_func) # print(view_args) print("MD1 process_view") return HttpResponse("dudu") def process_exception(self,request,exception): print("MD1 process_exception") # return None def process_template_response(self,request,response): print("MD1 process_template_response") response.template_name = "index1.html" #替換新的頁面 response.context_data['user']='newhai' #更改返回的數據 return response class MD2(MiddlewareMixin): def process_request(self,request): #處理請求,request和views請求一樣 print("MD2 process_request") def process_response(self,request,response): #response響應對象 print("MD2 process_response") return response def process_view(self,request,view_func,view_args,vies_kwargs): print("MD2 process_view") return HttpResponse("xiix") def process_exception(self,request,exception): print("MD2 process_exception") # return HttpResponse("處理完了") def process_template_response(self,request,response): print("MD2 process_template_response") response.template_name = "index1.html" #替換新的頁面 response.context_data['user']='newhaiyang1' #更改返回的數據 return response #views from django.shortcuts import render,HttpResponse from django.template.response import TemplateResponse def index(request): print('indexa') # ret = HttpResponse("OK") # int("aaaaaa") # return ret return TemplateResponse(request,'index.html',{'uesr':'haiyang'}) #urls from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index),] #template <body> <h1>index{{ uesr }}</h1> </body>
django-請求生命周期圖
- 客戶端發送HTTP請求,wsgi接受http請求,將http請求封裝成request,傳送給process_request -->路由urls -->process_vies ---> 視圖 --->process-response
- 圖
csrf
csrf裝飾器:
-
csrf_exempt,csrf_protect使用
from django.shortcuts import render,redirect,HttpResponse from django.views.decorators.csrf import csrf_exempt,csrf_protect #csrf裝飾器 @csrf_exempt #設置csrf,訪問不需要進行csrf校驗 @csrf_protect #不設置csrf,必須要進行校驗 def form(request): return render(request,'form.html') #類中設置csrf from django.utils.decorators import method_decorator #引入裝飾器 from django.views import View @method_decorator(csrf_exempt,name='dispatch') class From(View): def get(self,request): return render(request,'form.html') def post(self,request): return render(request,'form.html')
csrf的中間件流程:
-
查看中間件的方法:
#找到中間件進行引用 from django.middleware.csrf import CsrfViewMiddleware
-
想要能通過csrf校驗的前提條件 必須要有csrftoken 的cookie
-
第一種:{% csrf_token %} 生成token
-
第二種:from django.views.decorators.csrf import ensure_csrf_cookie
@method_decorator(ensure_csrf_cookie) def get(self,request):
-
-
csrf流程:
-
從cookie獲取csrftoken的值 與 POST提交的數據中的csrfmiddlewaretoken的值做對比
-
如果從request.POST中獲取不到csrfmiddlewaretoken的值,會嘗試從請求頭中獲取x-csrftoken的值,並且拿這個值與csrftoken的值做對比,對比成功也能通過校驗。
-
-
源代碼:
class CsrfViewMiddleware(MiddlewareMixin): def _get_token(self, request): if settings.CSRF_USE_SESSIONS: #第二步:判斷session是否為flase try: return request.session.get(CSRF_SESSION_KEY) #獲取session except AttributeError: else: try: #第三部:獲取瀏覽器cookie對應的值 cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] except KeyError: return None csrf_token = _sanitize_token(cookie_token) #第四步:調用_sanitize_token if csrf_token != cookie_token: #判斷cookie是否匹配 request.csrf_cookie_needs_reset = True #cookie不相等,重新設置 return csrf_token #第五步返回token def _sanitize_token(token): #判斷cookie是否符合 if re.search('[^a-zA-Z0-9]', force_text(token)): return _get_new_csrf_token() #匹配token,獲取到新的token elif len(token) == CSRF_TOKEN_LENGTH: #匹配長度是64的直接返回 return token elif len(token) == CSRF_SECRET_LENGTH: return _salt_cipher_secret(token) return _get_new_csrf_token() def process_request(self, request): #第一步:調用_get_token函數,從cookie中獲取csrftoken的cookie值 csrf_token = self._get_token(request) if csrf_token is not None: request.META['CSRF_COOKIE'] = csrf_token #有結果,將獲取的cookie值放在請求頭部信息 def process_view(self, request, callback, callback_args, callback_kwargs): #csrf_processing_done等於true 返回none view為none正常流程 if getattr(request, 'csrf_processing_done', False): return None #在viws中函數加入csrf_exempt,值就會給true #callback從函數里面拿csrf_exempt豁免,如果等於true返回none, if getattr(callback, 'csrf_exempt', False): return None #不是這些請求的話返回,是post del if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): return self._accept(request) #self._accept 處理完成等於true返回none,正常流程 #不需要進行強制性效驗 if getattr(request, '_dont_enforce_csrf_checks', False): if request.is_secure(): #如果發的是https請求 csrf_token = request.META.get('CSRF_COOKIE')#之前代碼添加的,從字典里面獲取cookie值 if csrf_token is None: #如果請求csrf_token沒有值403 return self._reject(request, REASON_NO_CSRF_COOKIE) request_csrf_token = "" if request.method == "POST": #如果是post請求 #獲取瀏覽器頁面post提交的數據 request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') #比較加密過的數據是否效驗成功 if not _compare_salted_tokens(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) #從請求頭中獲取x-csrf_token,賦值html post數據 if request_csrf_token == "": request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') return self._accept(request)
ajax
-
簡介:
-
JAX(Asynchronous Javascript And XML)翻譯成中文就是“異步的Javascript和XML”。即使用Javascript語言與服務器進行異步交互,傳輸的數據為XML(當然,傳輸的數據不只是XML)。
-
-
ajax
-
js技術,給服務發送請求。
-
特點: 異步(發送請求,不等待結果)傳輸的數據量小 局部刷新(輸入框返回來的不存在信息)
-
ajax請求方式:
- 同步交互:客戶端發出一個請求后,需要等待服務器響應結束后,才能發出第二個請求
- 異步交互:客戶端發出一個請求后,無需等待服務器響應結束,就可以發出第二個請求
-
請求方式-同步請求,發送一個請求,等待響應,在發送下一個
-
瀏覽器地址欄輸入地址 get
-
a標簽 get
-
form表單 get/post
-
發送請求:
-
發送代碼示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% load static %} </head> <body> <input type="text" name="i1">+ <input type="text" name="i2">= <input type="text" name="i3"> <button id="b1">計算</button> <script src="{% static 'jquery-3.4.1.js' %}"></script> #使用ajax 引用js <script> $('#b1').click(function () { //發ajax請求 $.ajax({ url: '/calc/', #發送請求地址 type: 'post', #請求方式,默認get data:{ #發送的數據 'x1':$('[name="i1"]').val(), 'x2':$('[name="i2"]').val(), }, success:function (res) { #成功響應后的回調函數,res views響應return的數據 {#$('[name="i3"]').val(res)#} location.href=res #前端跳轉配置 } }) }) </script> </body> </html> #urls urlpatterns = [ url(r'^calc/', views.calc), ] #views def calc(request): i1 = request.POST.get('x1') i2 = request.POST.get('x2') return HttpResponse(i3) #html <button id="b2">參數的測試</button> <script> $('#b2').click(function () { //發ajax請求 $.ajax({ url: '/test/', type: 'post', data:{ 'name':'haiyang', 'age':28, {#'hobby':['台球','吃飯','俊麗']#} 'hobby':JSON.stringify(['台球','吃飯','俊麗']) }, success:function (res) { {#console.log(res)#} console.log(res.status) }, error:function (res) { console.log(res) location.href=res #前端定義跳轉月面,res為后端傳送的路徑 } }) }) </script>
使用json傳值
import json
def test(request):
print(request.POST)
# hobby = request.POST.getlist('hobby[]')
hobby = json.loads(request.POST.get('hobby'))
print(hobby,type(hobby))
# return HttpResponse("ok")
# int("aaa") #錯誤時候調用
return JsonResponse({'status':'ok','msg':'xxx'}) #回應請求頭,變成對象
### 使用ajax上傳文件
* 上傳文件代碼示例;
```js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
</head>
<body>
<input type="file" id="f1"> <button id="b3">上傳文件</button>
<script src="{% static 'jquery-3.4.1.js' %}"></script>
<script>
$('#b3').click(function () {
{##生成form鍵值對, FormData默認有編碼方式#}
var form = new FormData();
form.append('filname','xxx')
form.append('f1',$('#f1')[0].files[0]) #f1后端獲取參數
//發ajax請求
$.ajax({
url: '/upload/',
type: 'post',
data:form,
processData:false, // 不需要處理編碼方式
contentType:false, // 不需要處理contentType請求頭
success:function (res) {
{#console.log(res)#}
console.log(res.status)
},
})
})
</script>
</body>
</html>
#urls
urlpatterns = [
url(r'^upload/', views.upload),]
#views
def upload(request):
f1 = request.FILES.get('f1')
with open(f1.name,'wb') as f:
for i in f1.chunks():
f.write(i)
return JsonResponse({'status': '上傳成功'}) # 回應請求頭,變成對象
#瀏覽器訪問信息:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarygNy1hssp07PGlGbq
Form Data
filname: xx
f1: (binary)
ajax通過django的csrf校驗的方式
-
第一種方法:給post,data中加csrfmiddlewaretoken鍵值對,與瀏覽器中獲取的csrfcookie對比
data:{ 'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val(), 'x1':$('[name="i1"]').val(), 'x2':$('[name="i2"]').val(), }, #瀏覽器查詢 Form Data csrfmiddlewaretoken: VAfy6g35ZvuEh30J0wmKv9wNd02jlZkOetmPJ4PxpbQyv8IgAIXR4CnzPbzXNKT6
-
第二種方法:data中加請求頭 x-csrftoken鍵值對
headers:{ 'x-csrftoken':$('[name="csrfmiddlewaretoken"]').val(), },
-
自定義ajax全部添加:
#在status中創建文件,自己寫一個getcookie方法 function getCookie(name) { #給cookie名字按照格式分隔開 var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); #調用函數,返回具體值 function csrfSafeMethod(method) { #請求匹配 // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); #如果請求為POST 返回True } $.ajaxSetup({ #給全局的ajax進行配置 beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); #為post設置請求頭 } } }); #在html文件中導入引用 <script src="{% static 'ajax_setup.js' %}"></script>
sweetalert:
-
代碼示例:
#引入sweetalert <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script> <button id='del' class="btn btn-primary btn-danger" url='/student_del/?pk={{ obj.id }}'>刪除</button> <script> $('.btn-danger').click(function () { #獲取按鈕class觸發 swal({ title: "你確定要刪除嗎?", text: "刪除可就找不回來了哦!", icon: "warning", buttons: true, dangerMode: true, }) .then((willDelete) => { if (willDelete) { $.ajax({ url: $(this).attr('url'), #獲取del中url信息,傳到后端 success: (res) => { if (res) { $(this).parent().parent().remove(); swal("刪除成功!", "你可以准備跑路了!", { icon: "success", }); } else { swal("刪除失敗", "你可以再嘗試一下!", "error") } } }) } else { swal("你的選擇很正確!"); } }); }) </script>
Form表單
-
常用的字段:
CharField #input ChoiceField #select MultipleChoiceField #多選 DecimalField #時間 ModelMultipleChoiceField #可選數據放入數據庫,每次刷新頁面都會重新查詢 MultipleChoiceField #單選 #具體代碼見視圖標注
-
字段內的參數:
initial #初始值 label #中文提示 error_messages #錯誤提示 min_length #最小長度 max_length #最大長度 choices #可選擇的值 widget #更改插件樣式 PasswordInput #隱藏密碼 disabled #為true不可編輯 #具體代碼見視圖標注
視圖:
from django import forms
def check_user(value): #自定義效驗,寫函數
if 'haiyang' in value:
raise ValidationError('haiyang 不能注冊')
from django.core.validators import validate_email,RegexValidator #內置的效驗方法
class RegForm(forms.Form):
#CharField文本輸入框,disabled=True不可編輯,validators填寫自定義的效驗和內置的效驗方法
user = forms.CharField(label='用戶名',initial='胡海洋',validators=[validate_email])
pwd = forms.CharField( #PasswordInput密碼框
label='密碼', #label 輸入款前的提示
widget=forms.PasswordInput, #隱藏密碼
required=True, #默認是true,為false不用填寫,不校驗
min_length=6,
max_length=12,
error_messages={ #根據設置屬性,修改錯誤提示
"required":'該字段是必須要填寫',
'min_length':'密碼最少是6位',
'max_length':'長度過長'
})
re_pwd = forms.CharField( #PasswordInput密碼框
label='確認密碼', #label 輸入款前的提示
widget=forms.PasswordInput, #隱藏密碼
required=True, #默認是true,為false不用填寫,不校驗
min_length=6,
max_length=12,
error_messages={ #根據設置屬性,修改錯誤提示
"required":'該字段是必須要填寫',
'min_length':'密碼最少是6位',
'max_length':'長度過長'
})
gender = forms.ChoiceField( #ChoiceField單選
label='性別',
choices=((1,'男'),(2,'女'),(3,'xx')),
widget=forms.RadioSelect #更改樣式
)
# hobby = forms.MultipleChoiceField( #MultipleChoiceField多選
# label='愛好',
# initial=[1,2], #initial默認選擇多個
# # choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),),
# widget=forms.CheckboxSelectMultiple #好用鼠標勾選
# )
hobby = forms.ModelMultipleChoiceField( #多選|forms.MultipleChoiceField單選
label='愛好',
initial=[1,2],
queryset=models.Hobby.objects.all(),
widget=forms.CheckboxSelectMultiple
)
birth = forms.DecimalField()
def reg2(request):
from_obj = RegForm() #對象實例化
if request.method == 'POST':
from_obj = RegForm(data=request.POST) #POST又實例化一次
if from_obj.is_valid(): #做校驗
return HttpResponse('OK')
return render(request, 'reg2.html',{'from_obj':from_obj}) #將對象傳給前端
模板:
{{ form_obj.as_p }} # 展示所有的字段
{{ form_obj.user }} # input框
{{ form_obj.user.label }} # label標簽的中文提示,用戶名
{{ form_obj.user.id_for_label }} # input框的id user
{{ form_obj.user.errors }} # 一個字段的錯誤信息
{{ form_obj.user.errors.0 }} # 一個字段的第一個錯誤信息
{{ form_obj.errors }} # 所有字段的錯誤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" novalidate>
{% csrf_token %}
<p>
<label for="{{ from_obj.user.id_for_label }}">{{ from_obj.user.label }}:</label>
{{ from_obj.user }} <span>{{ from_obj.user.errors.0 }}</span>
</p>
<p>
<label for="{{ from_obj.pwd.id_for_label }}">{{ from_obj.pwd.label }}:</label>
{{ from_obj.pwd }} <span>{{ from_obj.pwd.errors.0 }}</span>
</p>
<p>
<label for="{{ from_obj.re_pwd.id_for_label }}">{{ from_obj.re_pwd.label }}:</label>
{{ from_obj.re_pwd }} <span>{{ from_obj.re_pwd.errors.0 }}</span>
</p>
<p>
<label for="{{ from_obj.gender.id_for_label }}">{{ from_obj.gender.label }}:</label>
{{ from_obj.gender }} <span>{{ from_obj.gender.errors.0 }}</span>
</p>
<p>
<label for="{{ from_obj.hobby.id_for_label }}">{{ from_obj.hobby.label }}:</label>
{{ from_obj.hobby }} <span>{{ from_obj.hobby.errors.0 }}</span>
</p>
<p>
<label for="{{ from_obj.phone.id_for_label }}">{{ from_obj.phone.label }}:</label>
{{ from_obj.phone }} <span>{{ from_obj.phone.errors.0 }}</span>
</p>
{# {{ from_obj.errors }}#}
<button>注冊</button>
</form>
</body>
</html>
校驗
required #前端輸入字段是瀏覽器自動添加的required,post后面添加novalidate去除效驗
min_length #校驗字段長度
自定義的校驗:
-
寫函數:
from django.core.exceptions import ValidationError def check_user(value): # 通過校驗 不做任何操作 # 不通過校驗 拋出ValidationError異常 if 'haiyang' in value: raise ValidationError('haiyang 不能注冊') #在user字段中使用validators=列表中填寫效驗函數 user = forms.CharField(label='用戶名',initial='胡海洋',validators=[check_user])
-
使用內置的校驗器:
#RegexValidator 使用內置效驗器過濾 from django.core.validators import validate_email, RegexValidator phone = forms.CharField(validators=[RegexValidator(r'^1[3-9]\d{9}$','手機號格式不正確')])
-
is_valid流程:
1. is_valid()中要執行full_clean(): #第一步、第四步 1. self.is_bound data數據是否傳送 and self._errors ={}#第二步重新定義一個存放錯誤信息的字典 2. self.cleaned_data = {} #第三步:定義一個存放有效的數據 2. 執行self._clean_fields() #第五步:清洗字段 1. 先執行內置的校驗和校驗器的校驗 #第六步: 2. 有局部鈎子,執行局部鈎子 3. 執行 self.clean() 全局鈎子
局部鈎子和全局鈎子
#局部鈎子對當前字段進行效驗
def clean_user(self):
v = self.cleaned_data.get('user')
# 局部鈎子
# 不通過校驗 拋出異常
# 通過校驗 必須返回當前字段的值
if v =='haiyang':
raise ValidationError("不配")
return v
#全局部鈎子對全部字段進行效驗
from django.core.exceptions import ValidationError
def clean(self):
# 全局鈎子
# 不通過校驗 拋出異常
# 通過校驗 必須返回所有字段的值 self.cleaned_data
pwd = self.cleaned_data.get('pwd')
re_pwd = self.cleaned_data.get('re_pwd')
if pwd != re_pwd:
self.add_error('re_pwd','兩次密碼不一致!')
raise ValidationError('兩次密碼不一致')
return self.cleaned_data