本節內容
- HTTP協議
- wsgi協議和wsgiref模塊
- Djano基本流程和配置
- Django的路由系統
- Django靜態文件
- template模板語言
一 HTTP協議
1 HTTP簡介
HTTP是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫。它的發展是萬維網協會(World Wide Web Consortium)和Internet工作小組IETF(Internet Engineering Task Force)合作的結果,(他們)最終發布了一系列的RFC,RFC 1945定義了HTTP/1.0版本。
其中最著名的就是RFC 2616。RFC 2616定義了今天普遍使用的一個版本——HTTP 1.1。
HTTP協議具有如下特點:
(1) 基于TCP的80端口
(2) 基于請求-響應模式
(3) 無狀態保存,不對請求和響應之間的通信狀態進行保存
(4) 無連接,限制每次連接至處理一個請求,處理完成后即斷開連接
2 HTTP中的方法
GET:從一個服務器獲取一個資源
HEAD:只從服務器獲取文檔響應首部,而不獲取響應內容
POST:向服務器發送要處理的數據
PUT:將請求的主體部分直接存儲在服務器上
DELETE:請求刪除服務器上指定的文檔
TRACE:追蹤請求到達服務器中間經過的代理服務器
OPTIONS:請求服務器返回對指定資源支持使用的請求方法
3 HTTP的報文格式
請求:request
響應:response
request報文
<method> <request-URL> <version>
<headers>
<entity-body>
resposene報文
<version> <status><reason-phrase>
<headers>
<entity-boday>
4 HTTP中的響應狀態碼
三位數字,如200,301,203,404,502,描述標記請求發生的狀況
狀態代碼:
1XX:純信息
2XX:成功類的信息
3xx:重定向類信息
4xx:客戶端錯誤類的信息
5xx:服務器端錯誤類信息
常用狀態碼:
200:成功,請求的所有數據通過響應報文entity-body部分發送
301:請求的URL資源已被刪除,但在響應報文中通過Location指明了資源現在的位置,Moved Permanently
302:與301相似,但在響應報文中通過Location指明資源現在所處臨時新位置
304:客戶端發出了條件式請求,但服務器的資源未曾發生改變,則通過響應此響應狀態碼通知客戶端:Not Modified
401:需要輸入賬號和密碼認證方能訪問資源:Unauthorized
403:請求被禁止;Forbidden
404:服務器無法找到客戶端請求的資源;Not Found
500:服務器內部錯誤;Internal Server Error
502:代理服務器從后端服務器收到了一條偽響應
5 HTTP請求頭部和響應頭部包含的內容
通用首部:
Date:報文的創建時間
Connetction:連接方式,如keep-alive,close
Via:顯示報文經過的中間節點
Cache-Control:控制緩存
請求首部
Accpet:通知服務器自己可接受的媒體類型
Accept-Charset:通知服務器自己可接受的字符集
Accept-Encoding:通知服務器自己接受的編碼格式,如GZIP
ACCPT-Language:接受的語言
Host:請求的服務器名稱和端口號
Rerferer:包含當前正在請求資源的上一級資源
User-Agent:客戶端代理
If-Modified-Since:自從指定的時間之后,請求的資源是否發生過修過
If-Unmodified-Since:自從指定的時間之后,請求的資源是否未過修過
If-None-Mathc:本地緩存中存儲的文檔的Etagb標簽是否與服務器ETG不匹配
Authorization:向服務器端發送認證信息,如賬號密碼
Cookie:客戶端向服務器端發送Cookie標識
Proxy-Authorization:向代理服務器認證
響應首部
Age:響應持續時長
Server:服務器程序的軟件名稱和版本
協商首部:某資源多種表示方法時使用
Accpet-Ranges:服務器可接受的請求范圍類型
Vary:服務器查看其它首部列表
安全響應首部:
Set-Cookie:向客戶端設置cookie
www.Authenticate:來自服務器對客戶端的質詢認證表單
6 Cookie和Session
Cookie和Session都為了用來保存狀態信息,都是保存客戶端狀態機制,都是為了解決HTTP無狀態的問題而產生
Session可以用Cookie來實現,也可以用URL回寫的機制來實現。用Cookie來實現的Session可以認為是對Cookie更高級的應用。
Cookie和Session有以下明顯的不同點:
(1) Cookie將狀態保存在客戶端,Session將狀態保存在服務端
(2) Cookie是服務器在本地機器上存儲的小段文本并隨每一個請求發送至同一個服務器,cookie最早在RFC2109中實現,后續RFC2965做了增強。網絡服務器用HTTP頭向客戶端發送cookies,在客戶終端,瀏覽器解析這些cookies并將它們保存為一個本地文件,它會自動將同一服務器的任何請求縛上這些cookies。Session并沒有在HTTP的協議中定義;
(3) Session是針對每一個用戶的,變量值保存在服務器上,用一個sessionID來區分是哪個用戶session變量,這個值是通過用戶的瀏覽器在訪問的時候返回給瀏覽器,當客戶禁用cookie時,這個值也可能設置為由get來返回給服務器。
(4) 就安全性來說:當你訪問一個使用session 的站點,同時在自己機子上建立一個cookie,建議在服務器端的SESSION機制更安全些.因為它不會任意讀取客戶存儲的信息。
關于session和Cookie在Django的具體使用請見:
7 通過sokcet 建立一個HTTP響應
def create_sk():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5)
return server
def handle_request(server):
print('server start....')
while 1:
conn,addr = server.accept()
data = conn.recv(1024)
print(data) # 接收到 http請求頭部
conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
conn.send(b'<h1>hello world</h1>')
def run():
sk = create_sk()
handle_request(sk)
if __name__ == '__main__':
run()
二 wsgi協議和wsgiref模塊
WSGI:Web Server Gateway Interface,是Python應用程序或框架和Web服務器之間的一種接口,而wsgiref模塊就是python基于wsgi協議開發的服務模塊。
自己定義一個web框架所需的幾個主要文件
1 urls.py:路徑與視圖函數映射關系 --- url控制器
2 views.py: 視圖函數,固定一個形式參數:environ
3 templates文件夾: html模板文件
4 models:在項目啟動前創建表結構
通過wsgire模塊自己搭建一個web服務器
main.py 主程序 :
from wsgiref.simple_server import make_server
import urls
def application(environ,start_response):
path = environ["PATH_INFO"] # 獲取當前請求路徑
print('server start')
start_response('200 OK', [('Content-Type', 'text/html')])
urls_list = urls.routers()
func = None
for i in urls_list:
if path == i[0]:
func = i[1]
if func:
return func()
else:
return [b'<h1>404 not found</h1>']
urls.py 控制器
from view import *
def routers():
urls_list = [
('/login', login),
]
return urls_list
view.py 視圖函數
def login():
with open('templates/login.html', 'rb') as f:
data = f.read()
return [data]
http_server = make_server('', 8080 ,application)
http_server.serve_forever()
三 Djano基本流程和配置
1 MVC與MTV模型
MVC
Web服務器開發領域里著名的MVC模式,所謂MVC就是把Web應用分為模型(M),控制器(C)和視圖(V)三層,他們之間以一種插件式的、松耦合的方式連接在一起,
模型負責業務對象與數據庫的映射(ORM),視圖負責與用戶的交互(頁面),控制器接受用戶的輸入調用模型和視圖完成用戶的請求
MTV
Django的MTV模式本質上和MVC是一樣的,也是為了各組件間保持松耦合關系,只是定義上有些許不同,Django的MTV分別是值:
M 代表模型(Model):負責業務對象和數據庫的關系映射(ORM)。
T 代表模板 (Template):負責如何把頁面展示給用戶(html)。
V 代表視圖(View):負責業務邏輯,并在適當時候調用Model和Template。
除了以上三層之外,還需要一個URL分發器,它的作用是將一個個URL的頁面請求分發給不同的View處理,View再調用相應的Model和Template
2 創建一個django project
danjgo-admin.py startproject mysite
manage.py ----- Django項目里面的工具,通過它可以調用django shell和數據庫等
settings.py ---- 包含樂項目默認設置, 包括數據庫信息,調試標志及其他一些工作的表露
urls.py ---- 負責把URL模式映射到應用程序
3 Django的其他常用命令
python manage.py runserver ip:port (啟動服務器,默認ip和端口為http://127.0.0.1:8000/)
python manage.py startapp appname (新建 app)
python manage.py syncdb (同步數據庫命令,Django 1.7及以上版本需要用以下的命令)
python manage.py makemigrations (顯示并記錄所有數據的改動)
python manage.py migrate (將改動更新到數據庫)
python manage.py createsuperuser (創建超級管理員)
python manage.py dbshell (數據庫命令行)
python manage.py (查看命令列表)
四 Django的路由系統
每一個Django項目中都會有一個urls.py的文件,它的作用是根據用戶請求的url調用不同視圖函數來給用戶返回不同頁面
urlpatterns = [
path(url,view函數,參數,別名)
repath(正則表達式,view函數,參數,別名)
]
參數說明:
(1) 一個正則表達式字符串,或者一個url
(2) 一個可以調用的對象,通常稱為一個視圖函數,
(3) 可選的要傳遞給視圖函數的默認參數(字典形式)
(4) 一個可選的name參數
1 示例:
from django.contrib import admin from django.urls import path,re_path import views urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), re_path(r'^article/2001$', views.article_year), # 匹配正則匹配到的路徑 re_path(r'^article/([0-9]{4})', views.article_year), re_path(r'^article/([0-9]{4})/([0-9]{2})/$', views.article_month), re_path(r'^article/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})',views.article_month), # 正則命名,視圖函數的形參要與正則命名的一致 ]
注意事項:
(1) 要捕獲URL中的值,用括號括起來,會當作參數傳入視圖函數
(2) 沒有必要添加一個斜線,因為每個URL都有。例如:^articles不是^/articles。
(3) 在'r'前面的每個正則表達式字符串中是可選的,但建議。它告訴Python字符串是“原始” -沒有什么字符串中應該進行轉義。
2 多個APP進行路由分發(Including)
如果一個Django項目里面有多個APP那么大家共用一個url路由很容易造成混淆,于是使用include分發讓每個APP擁有自己單獨的url
from django.conf.urls import re_path,include
from django.contrib import admin
from app1 import views
urlpatterns = [
re_path(r'^app1/',include('app1.urls')),
re_path(r'^app2/',include('app1.urls')), #注意include的是字符串形式的 文件路徑;
re_path(r'^',views.error),
]
3 使用url別名的示例
url中還支持name參數的配置,如果配置了name屬性,在模板的文件中就可以使用name值來代替相應的url值.
urlpatterns = [
re_path(r'^index',views.index,name='bieming'),
re_path(r'^admin/', admin.site.urls),
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/([0-9]{4})/$', views.year_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
###################
def index(req):
if req.method=='POST':
username=req.POST.get('username')
password=req.POST.get('password')
if username=='alex' and password=='123':
return HttpResponse("登陸成功")
return render(req,'index.html')
#####################
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{# <form action="/index/" method="post">#}
{# 這里只要使用bieming即可代替/index #}
<form action="{% url 'bieming' %}" method="post">
用戶名:<input type="text" name="username">
密碼:<input type="password" name="password">
<input type="submit" value="submit">
</form>
</body>
</html>
4 名稱空間
名稱空間是表示標識符的可見范圍,一個標識符可在多個命名空間中定義,它在不同命名空間中的含義是互不相干的,
在一個新的命名空間中可定義任何標識符,它們不會與任何已有的標識符發生沖突,因為已有的定義都處于其他命名 空間。
由于name沒有作用域,Django在反解析URL時,會在項目全局順序搜索,當查找到第一個name指定URL時,立即返回
我們在開發項目時,會經常使用name屬性反解除URL,當不小心在不同的app的urls中定義相同的name時,可能會導致URL反解錯誤,為了避免這種情況引入了命名空間
project的urls.py:
urlpatterns = [
re_path(r'^app1/',include(('app1.urls',"app01")),
re_path(r'^app2/',include(('app2.urls',"app02")),
]
app01.urls
urlpatterns = [
re_path(r'^index/',index,name="index"),
]
app02.urls
urlpatterns = [
re_path(r'^index/',index,name="index"),
]
app01.views
from django.core.urlresolvers import reverse
def index(request)
return HttpResponse(reverse("app01:index"))
app02.views
from django.core.urlresolvers import reverse
def index(request)
return HttpResponse(reverse("app02:index")
5 django2.0中 path的用法
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/, views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/',views.moutn_archive),
path('articles/<int:year>/<int:month>/<slug>/, views.article_detail),
]
基本規則:
(1) 使用尖括號(<>)從url中捕獲值。
(2) 捕獲值中可以包含一個轉化器類型(converter type),比如使用<int:name>捕獲一個整數變量,如果沒有轉化器,將匹配任何字符串,也包括'/'字符
(3) 無需添加前倒斜杠
Django默認支持的轉化器
(1) str,匹配除了路徑分隔符(/)之外的飛車空字符串,這是默認的形式
(2) int,匹配正整數,包含0
(3) slug,匹配字母,數字及橫杠,下劃線組成的字符串
(4) uuid,匹配格式化uuid,如0745194d3-6885-417e-a8a8-6c931e272f00
(5) path,匹配任何非空字符串,包含了路徑分隔符
自定義轉換器
對于一些復雜的使用,可以自定義裝換器。轉換器是一個類或接口,它的要求有三點:
regex類屬性,字符串類型
to_python(self,value)方法,value是由類屬性regex所匹配到的字符串,返回具體的Python變量值,以供Django傳遞到對應的視圖函數中
to_url(self,value)方法,和to_python相反,value是一個具體的Python變量值,返回其字符串,通常用于url反向引用
示例:
class FourDigYearConverter: regex = '[0-9]{4}' def to_python(self,value): return int(value) def to_url(self,value): return '%04d' %value 使用register_converter將其注冊到URL配置中 from django.urls import register_converter,path from . import converters, views # 自己寫的類模塊導入盡量 register_converter(converters.FourDigYearConverter, 'yyyy') urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<yyyy:year>', views.path_month) ]
五 Django靜態文件
1 static靜態文件
我們在項目中一般情況下html模板都需要引入js css等靜態文件來渲染網頁,下面會介紹Django引入這些靜態文件的方式:
1 首先先要在項目中創建一個名叫static的文件夾
2 在Django的settings中添加一行配置, 拼接static文件夾的路徑
STATICFILES_DIRS = [
os.path.join(BASE_DIR,"static")
]
3 在html直接引入static路徑下的內容
<script src="/static/jquery-3.3.1.js"></script>
2 media靜態文件
Media配置
在項目的應用下生成一個media文件夾
在settings下配置:MEDIA_ROOT = os.path.join(BASE_DIR,"media")
如果用戶一旦配置了MEDIA_ROOT:
avatar_obj=request.FIELS.get("avatar")
user_obj = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar_obj)
Django會將文件對象下載到MEDIA_ROOT中的avatar文件夾中(如果沒有avatar,Django會自動創建), user_obj的avatar存的是文件對象
Meida 配置值MEDIA_URL:
讓瀏覽器直接訪問到media中的數據
在settings.py里配置MEDIA_URL="/meida"
# media配置
from django.views.static import serve
from cnblog import settings
re_path(r"media/(?P<path>.*)$", serve, {"document_root":settings.MEDIA_ROOT})
六 template模板語言
1 模板之過濾器
語法
{{ obj|filter_name:param }}
default
如果一個變量是false或者為空,使用給定的默認值,否則使用變量的值.
{{ value|default:"nothing" }}
length
返回值的長度,它對字符串和列表都起作用。例如
{{ value|length }}
如果value是['a','b','b','d']那么輸出4
filesizeformat
將格式化為一個"人類可讀的"文件尺寸(例如13kb,4.1mb,1024bytes等待).例如:
{{ value|filesizeformat }}
如果value是123456789,輸出將會是117.7MB
date
如果 value = datetime.datetime.now()
{{ value|date:"Y-m-d" }}
slice
如果 value = "hello world"
{{ value|slice:"2-1" }}
truncatechars
如果字符串字符多于指定的字符數量,那么會被截斷。截斷字符串可以翻譯的省略號序列("...")結尾
參數:要截斷的字符數
{{ value|truncatechars:9 }}
safe
Django的模板中會對HTML標簽和JS等語法進行自動轉義,但是有的時候我們不希望這些HTML元素被轉義,
在Django中關系HTML自動轉義有兩種方式,如果是一個單獨的變量我們可以通過過濾"|safe"的方式告訴Django這段代碼是安全的不必轉義
value = "<a href="">點擊</a>
{{ value|safe }}
2 模板之標簽
標簽看起來是這樣的:{% tag %}。標簽比變量更加復雜:一些在輸出中創建文件,一些通過循環或邏輯來控制流程,
一些加載其后的變量將使用到的額外信息添加到模板中。
for標簽 遍歷每一個元素 {% for person in person_list %} <p>{{ pserson.name }} <p> { % endfor %} 可以利用{% for obj in list reversed %}反向完成循環。 遍歷一個字典: {% for key,val in dic.items %} <p>{{ key }}:{{ val }}<p> {% endfor % } for ... empty for標簽帶有一個可選的{% empt %}從句,以便在給出的組是空的或者沒有找到時,可以有所操作 {% for person in pserson_list %} <p>{{ person.name }}</p> {% enpty %} <p>sorry,no person here</p> {% endfor %} if 標簽 {% if %}會對一個變量求值,如果他的值是True(存在,不為空且不是boolean類型的false值),對應的內容塊會輸出 {% if num >100 or num <0 %} <p>無效</p> {% elif num > 80 and num < 100 %} <p>優秀</p> {% else %} <p>湊合吧</p> {% endif %} with 使用一個簡單的名字緩存一個復雜的變量。 例如: {% with total=business.employees.count %} {{ total }} employee{{ total|pluralize }} {% endwith %}
3 自定義標簽
(1) 在應用目錄下創建一個templatetags目錄(必須)
(2) 創建任意.py文件, 如:add100.py
from django import template from django.utils.safestring import mark_safe register = template.Library() # register是固定變量名,不能改變 @register.simple_tag def my_add100(value1) return value1 + 100 @register.simple_tag def my_input(id,arg): result = "<input type="text" id='%s' class='%s'/>" %id(id,arg,) return make_server(result) @register.filter # 自定義過濾器 def multi_filter(x,y): return x*y
#注意:自定義標簽和自定義過濾器的區別在于標簽可以傳多個參數而過濾器最多只能傳2個但是過濾器可以使用if語句
(3) 在使用自定義simple_tag的html文件導入之前創建add100.py文件夾
{% load add100 %}
(4) 使用simple_tag
{% my_add100 5%}
{% my_input 'id_username' 'hide'%}
(5) 在setting中配置當前app, 不然django無法找到自定義的simple_tag
INSTALLED_APPS = "app01".
4 模板之繼承
(1) 首先先將一些公共的版塊寫成一個基礎模板, 如果模一塊內容需要擴展,{% block contnet %} {% endblock %},相當于留了一些空間等待子模板重寫里面的內容
(2) 在子模板的第一行引入模板{% extends 'base.html' %}
(3) 在子模塊引入{% block contnet %} {% endblock %}然后重寫里面的內容