16 . PythonWeb框架之Django

Web框架簡介

Web開發是Python語言應用領域的重要部分,也是目前最主要的Web開發語言之一,在其二十多年的歷史中出現了數十種Web框架,好比Django、Tornado、Flask、Twisted、Bottle和Web.py等,有的歷史悠久,有的發展迅速,還有的已經中止維護.php

Django:css

發佈於2003年,是當前Python世界裏最負盛名且最成熟的Web框架,最初被用來製做在線新聞的Web站點,Django的各模板之間結合得比較緊密,因此在功能強大的同時又是一個相對封閉的系統(依然能夠自定義的),可是其健全的在線文檔和開發社區,使開發者遇到問題能找到解決辦法.html

Tornado:前端

一個強大的、支持協程、高效併發且可擴展的Web服務器,發佈於2009年9月,應用於FriendFeed、Facebook等社交網站。它的強項在於能夠利用異步協程機制實現高併發的服務。python

Flask:mysql

Python Web框架家族裏比較年輕的一個,發佈於2010年,它吸取了其餘框架的優勢而且把本身的主要領域定義在了微小項目上,以短小精幹,簡潔明瞭著稱。jquery

Twisted:linux

一個有着十多年曆史的開源事件驅動框架。它不像前三種着眼於Web應用開發,而是適用從傳輸層到自定義應用協議的全部類型的網絡程序的開發,並能在不一樣的操做系統上提供很高的運行效率。可是,目前對Python3的支持有限,建議使用Python2.7。nginx

MVC和MTV框架

MVCgit

Web服務器開發領域裏著名的MVC模式,所謂MVC就是把Web應用分爲模型(M),控制器(C)和視圖(V)三層,他們之間以一種插件式的、鬆耦合的方式鏈接在一塊兒,模型負責業務對象與數據庫的映射(ORM),視圖負責與用戶的交互(頁面),控制器接受用戶的輸入調用模型和視圖完成用戶的請求,其示意圖以下所示

img

MTV

Django的MTV模式本質上和MVC是同樣的,也是爲了各組件間保持鬆耦合關係,只是定義上有些許不一樣,Django的MTV分別是值:

# M 表明模型(Model): 負責業務對象和數據庫的關係映射(ORM)。
# T 表明模板 (Template):負責如何把頁面展現給用戶(html)。
# V 表明視圖(View):   負責業務邏輯,並在適當時候調用Model和Template。

除了以上三層以外,還須要一個URL分發器,它的做用是將一個個URL的頁面請求分發給不一樣的View處理,View再調用相應的Model和Template,MTV的響應模式以下所示:

  img

通常是用戶經過瀏覽器向咱們的服務器發起一個請求(request),這個請求回去訪問視圖函數,(若是不涉及到數據調用,那麼這個時候視圖函數返回一個模板也就是一個網頁給用戶),視圖函數調用模型,模型去數據庫查找數據,而後逐級返回,視圖函數把返回的數據填充到模板中空格中,最後返回網頁給用戶。

選擇框架的原則
  • 選擇更主流的框架。由於它們的文檔更齊全,技術積累更多,社區更繁盛,能獲得更好的幫助和支持。
  • 選擇更活躍的框架。關注項目在GitHub等環境中的更新頻率、Issue和Pull Request的響應狀況。若是一個項目長期沒有更新,或者有一堆的問題須要解決可是沒有獲得響應,就不該該是你學習的對象。
  • 選擇可以知足需求的框架。沒有最好的框架,只有更合適的框架。你所選擇的Web框架不只須要知足當前的需求,還要充分考慮項目發展一段時間後的狀況,即前瞻性,避免盲目選擇而致使未來推倒重來的狀況。
  • 選擇時效性好的框架。在學習和使用框架的時候常常須要查閱和參考各類網絡上的文章、博客和教程,可是須要注意他們的發表時間。有些框架的相關文章已經很老了,好久沒更新了,應該放棄這種框架;有的框架一直以來都有不斷的新文章、新博客出現,就是比較不錯的選擇。
  • 選擇入門友好的框架。這條只對新手適用。詳細的框架文檔、官方教程對新手來講都是極大的幫助和鼓勵.
爲何選擇Django?

首先介紹一下Django,Django具備如下特色:

  • 功能完善、要素齊全:該有的、能夠沒有的都有,自帶大量經常使用工具和框架,無須你自定義、組合、增刪及修改。
  • 完善的文檔:通過十多年的發展和完善,Django有普遍的實踐案例和完善的在線文檔。開發者遇到問題時能夠搜索在線文檔尋求解決方案。
  • 強大的數據庫訪問組件:Django的Model層自帶數據庫ORM組件,使得開發者無須學習其餘數據庫訪問技術(SQL、pymysql、SQLALchemy等)。
  • 靈活的URL映射:Django使用正則表達式管理URL映射,靈活性高。新版的2.0,進一步提升了URL編寫的優雅性。
  • 豐富的Template模板語言:相似jinjia模板語言,不但原生功能豐富,還能夠自定義模板標籤,而且與其ORM的用法很是類似。
  • 自帶後臺管理系統admin:只須要經過簡單的幾行配置和代碼就能夠實現一個完整的後臺數據管理控制平臺。
  • 完整的錯誤信息提示:在開發調試過程當中若是出現運行錯誤或者異常,Django能夠提供很是完整的錯誤信息幫助定位問題。

根據前面的選擇原則咱們逐條對比一下:

# 1. 主流、活躍程序:  
# 從GitHub的數據來看,Django的開發很是活躍,迭代速度也很是快  
  
# 2. 是否能夠知足需求:  
# Django以要素齊全、工具豐富、框架龐大著稱,基本上別的框架有的他有,別的框架沒有的他也有,  
# 若是Django知足不了需求,那麼別的框架同時也同樣.  
  
# 3. 時效性:  
# Django有很長的開發喝實踐過程,或早或晚的文檔、教程、幫助、博客等等很是多,資料更新速度也很快.  
  
# 4. 入門友好程度  
# 一個框架可否流行起來,對新手入門友好很是關鍵.Django在這一點作的很是好.
Django的不足

Django也有一些缺點:
框架龐大,被認爲不夠精簡,捆綁的內容太多
首先,對於新手,Django集成好的工具和部件,讓你無須再費腦力去學習如何安裝、調試、集成、兼容別的工具,Django幫你都集成好了,並且保證兼容性,可用性和方便性,就比如聯想一體機,開機即用,效率也高,而一些如flask的框架,雖然精簡,可是你要本身安裝各類工具、ORM、插件等等,比如DIY電腦,在用以前,要知道買什麼配件,怎麼搭配,怎麼組裝,怎麼配置效率才高,將新手的熱情消耗在非關鍵性的內容上.
其次,對於老手,Django也是開放的,你徹底能夠關閉沒必要要的功能,忽略不使用的組件,或者自定義但願的組件,包括ORM和Template在內,均可以自由選擇.

在異步通訊方面略有欠缺
從本質上來說,Tornado在異步協程機制實現高併發的服務上要強一些,Django在這方面有追趕的目標,但這不是說Django就差到不能用了.

基於Python進行Web開發的技術棧

想要熟練地使用Django進行Web開發,設計生產環境可用的,可以應付必定規模訪問量的Web應用,開發者要學會的遠遠不止Django自己,Python基礎、環境搭建、前端語言、API設計、網站架構、系統管理、持續集成、服務化數據處理、併發處理等等,都是相關的只是領域,包括但不限於如下的內容:

# 熟悉Python語言  
# 對前端的HTML\CSS\JavaScript比較熟悉  
# 對網絡基礎,好比HTTP、TCP/IP等比較熟悉  
# 熟悉數據庫、緩存、消息隊列等技術的使用場景和使用方法  
# 平常能使用Linux或Mac系統工做(Windows標配)  
# 有性能優化經驗,能快速定位問題  
# 除此以外,還要對業務有深入理解,可以寫出可維護性足夠高的代碼,固然,以上都是對經驗豐富的開發者而言,  
# 對於新手剛入門者,咱們朝着這個目標努力學習就好  
# 下面是基於Python的Web開發技術棧  
# 一、web應用
# 運行在瀏覽器上的應用

# 二、c/s b/s 架構
# client/server:客戶端服務器架構,C++
# brower/server:瀏覽器服務器架構,Java、Python
# 底層均是基於socket

# 三、Python Web框架
# a.socket b.頁面路由 c.模板渲染
# Django 	a用的wsgiref b本身寫的 c本身寫的 功能全面
# Flask 	a用的第三方 b本身寫的 c本身寫的 小而輕
# Tornado   a本身寫的 b本身寫的 c本身寫的 支持高併發

Django簡介

Django是什麼?

Django 是一個高級的 Python 網絡框架,能夠快速開發安全和可維護的網站。由經驗豐富的開發者構建,Django負責處理網站開發中麻煩的部分,所以你能夠專一於編寫應用程序,而無需從新開發。
它是免費和開源的,有活躍繁榮的社區,豐富的文檔,以及不少免費和付費的解決方案。

Django能夠使你的應用具備如下優勢
完備性

Django遵循「功能完備」的理念,提供開發人員可能想要「開箱即用」的幾乎全部功能。由於你須要的一切都是一個」產品「的一部分,它們均可以無縫結合在一塊兒,遵循一致性設計原則,而且具備普遍和最新的文檔

通用性

Django 能夠(並已經)用於構建幾乎任何類型的網站—從內容管理系統和維基,到社交網絡和新聞網站。它能夠與任何客戶端框架一塊兒工做,而且能夠提供幾乎任何格式(包括 HTML,Rss源,JSON,XML等)的內容。你正在閱讀的網站就是基於Django。

在內部,儘管它爲幾乎全部可能須要的功能(例如幾個流行的數據庫,模版引擎等)提供了選擇,可是若是須要,它也能夠擴展到使用其餘組件。

安全性

Django 幫助開發人員經過提供一個被設計爲「作正確的事情」來自動保護網站的框架來避免許多常見的安全錯誤。例如,Django提供了一種安全的方式來管理用戶帳戶和密碼,避免了常見的錯誤,好比將session放在cookie中這種易受攻擊的作法(取而代之的是cookies只包含一個密鑰,實際數據存儲在數據庫中)或直接存儲密碼而不是密碼哈希。

密碼哈希是經過密碼散列函數發送密碼而建立的固定長度值。 Django 能經過運行哈希函數來檢查輸入的密碼-就是-將輸出的哈希值與存儲的哈希值進行比較是否正確。然而因爲功能的「單向」性質,即便存儲的哈希值受到威脅,攻擊者也難以解決原始密碼。(但其實有彩虹表-譯者觀點)

默認狀況下,Django能夠防範許多漏洞,包括SQL注入,跨站點腳本,跨站點請求僞造和點擊劫持

可擴展

Django使用基於組件的"無共享"架構(架構的每一部分獨立於其餘架構,所以能夠根據須要進行替換或更改)在不用部分之間有明確的分隔意味着它能夠經過任何級別添加硬件來擴展服務: 緩存服務器,數據庫服務器或應用程序服務器,一些最繁忙的網站已經成功地縮放了Django,以知足他們的需求(例如Instagram和Disqus,僅舉兩個例子,可自行添加)

可維護性

Django代碼編寫時遵守設計原則和模式,鼓勵建立可維護和重複使用的代碼,特別是他用了不要重複本身(DRY)原則,因此沒有沒必要要的重複,減小了代碼的數量.Django還將相關功能分組到可重用的"應用程序"中,而且在較低程序級別將相關代碼分組

靈活性

Django使用Python編寫的,他在許多平臺上運行,意味着你不受任務特定的服務器平臺的限制,而且能夠在許多種類的Linux,Windows和Mac OsX上運行應用程序,此外,Django獲得許多網絡託管商的好評,他們常常提供特定的基礎設施和託管Django網站的文檔.

Django的出生

Django最初由2003年到2005年間由負責建立和維護報紙網站的網絡團隊開發。在建立了許多網站後,團隊開始考慮並重用許多常見的代碼和設計模式。這個共同的代碼演變一個通用的網絡開發框架,2005年7月被開源「Django」項目。

Django不斷髮展壯大——從2008年9月的第一個里程碑版本(1.0)到最近發佈的(1.11)-(2017)版本。每一個版本都添加了新功能和錯誤修復,從支持新類型的數據庫,模版引擎和緩存,到添加「通用」視圖函數和類(這減小了開發人員必須編寫的代碼量)一些編程任務。

Django有多歡迎

服務器端框架的受歡迎程度沒有任何可靠和明確的測量(儘管Hot Frameworks網站 嘗試使用諸如計算每一個平臺的GitHub項目數量和StackOverflow問題的機制來評估流行度)。一個更好的問題是Django是否「足夠流行」,以免不受歡迎的平臺的問題。它是否繼續發展?若是您須要幫助,能夠幫您嗎?若是您學習Django,有機會得到付費工做嗎?

基於使用Django的流行網站數量,爲代碼庫貢獻的人數以及提供免費和付費支持的人數,那麼是的,Django是一個流行的框架

使用Django的流行網站包括:Disqus,Instagram,騎士基金會,麥克阿瑟基金會,Mozilla,國家地理,開放知識基金會,Pinterest和開放棧.

Django是特定?

Web框架一般將本身稱爲"特定" 或"無限制".
特定框架是對處理任何特定任務的"正確方法" 有意見的框架,他們常常支持特定領域的快速發展(解決特定類型的問題),由於正確的作法是一般被很好的理解和記錄在案,然而,他們在解決其主要領域以外的問題可能不那麼靈活,而且傾向於爲能夠使用那些組件和方法提供較少的選擇.

相比之下,無限制的框架對於將組件粘合在一塊兒以實現目標或甚至應使用那些組件的最佳方式限制較少,它們使開發人員更容易使用最合適的工具來完成特定任務,儘管您須要本身查找這些組件。

Django「有點有意義」,所以提供了「兩個世界的最佳」。它提供了一組組件來處理大多數Web開發任務和一個(或兩個)首選的使用方法。然而,Django的解耦架構意味着您一般能夠從多個不一樣的選項中進行選擇,也能夠根據須要添加對全新的支持

原生Socket服務
demo1
	-- index.html
	-- server.py

基礎socket服務

import socket
# 利用socket創建服務器對象
server = socket.socket()
# 設置ip和端口
server.bind(('127.0.0.1', 8001))
# 設置監聽
server.listen(5)
print('服務器設置成功')
print('瀏覽器訪問:http://127.0.0.1:8001')
while True:
    # 阻塞等待客戶端數據
    client, address = server.accept()
    # 接收數據
    data = client.recv(1024)
    print('接收到數據: ', data)
    # 返回數據
    client.send(b'Normal Socket Web')
    # 關閉鏈接(必須關閉每一次鏈接)
    client.close()

瀏覽器錯誤:發送的響應無效,緣由:響應不知足http協議

# 請求發來的數據
b'GET / HTTP/1.1\r\n
Host: 127.0.0.1:8001\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
Cookie: csrftoken=szfYLDVuqvRhlveNpNE2rp1GYOcI5x7mRNfvkRWTMRNRwWxXMZWOhL1MqknYJ7jg; sessionid=3pphvmw2icub0bea7nn02u6wev17k4uw\r\n
\r\n'
http協議

什麼是http協議

# HTTP(HyperText Transport Protocol)是超文本傳輸協議
# 基於TCP/IP協議基礎上的應用層協議,底層實現仍爲socket
# 基於請求-響應模式:通訊必定是從客戶端開始,服務器端接收到客戶端必定會作出對應響應
# 無狀態:協議不對任何一次通訊狀態和任何數據作保存
# 無鏈接:一次鏈接只完成一次請求-響應,請求-響應完畢後會當即斷開鏈接

http工做原理(事務)

# 一次http操做稱之爲一個事務,工做過程可分爲四步
# 1.客戶端與服務端創建鏈接
# 2.客戶端發生一個http協議指定格式的請求
# 3.服務器端接收請求後,響應一個http協議指定格式的響應
# 4.客戶端將服務器的響應顯示展示給用戶

請求報文

POST / HTTP/1.1\r\n
Host: 127.0.0.1:8001\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n
\r\n
usr=abc&pwd=123

響應報文

# 響應行  響應頭  響應體
HTTP/1.1 200 OK\r\n
Content-type:text/html\r\n
\r\n
Login Success

修改返回數據,完善響應體

# 字符串
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'\r\n')
client.send(b'Normal Socket Web')
# html代碼,請求頭要設置支持html代碼
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
client.send(b'<h1>Normal Socket Web</h1>')
# html文件(同級目錄創建一個index.html頁面)
client.send(b'HTTP/1.1 200 OK\r\n')
client.send(b'Content-type:text/html\r\n')
client.send(b'\r\n')
# 利用文件方式讀取頁面
with open('index.html', 'rb') as f:
    dt = f.read()
client.send(dt)

修改接受數據,模擬後臺路由

# 分析接收到的數據
data = client.recv(1024)
# 保證接收到的數據做爲字符串進行如下處理
data = str(data, encoding='utf-8')
# 拆分出地址位
route = data.split('\r\n')[0].split(' ')[1]
# 匹配地址,作出不一樣的響應
if route == '/index':
    with open('index.html', 'rb') as f:
    	dt = f.read()
elif route == '/login':  # 新建login頁面
    with open('login.html', 'rb') as f:
    	dt = f.read()
else:
    dt = b'404'
client.send(dt)

狀態碼

# 1打頭:消息通知
# 2打頭:請求成功
# 3打頭:重定向
# 4打頭:客戶端錯誤
# 5打頭:服務器端錯誤
框架演變

目錄結構

part2
	-- favicon.ico
	-- index.html
	-- manage.py=

manage.py

import socket
import pymysql
# 響應頭
RESP_HEADER = b'HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n'

# 請求處理
def index():
    # 以字節方式讀取文件
    with open('index.html', 'rb') as f:
        dt = f.read()
    return dt
def ico():
    with open(favicon.jpeg, 'rb') as f:
        dt = f.read()
    return dt
def user():
    # 數據庫操做
    conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
    cur = conn.cursor(pymysql.cursors.DictCursor)
    cur.execute('select * from user')
    users = cur.fetchall()
    print(users)
    users = '''%d:%s
    %d:%s''' % (users[0]['id'], users[0]['name'], users[1]['id'], users[1]['name'])
    return users.encode('utf-8')

# 設置路由
urls = {
    # 請求路徑與請求處理函數一一對應
    '/index': index,
    favicon.jpeg: ico,
    '/user': user
}

# 設置socket
def serve(host, port):
    server = socket.socket()
    server.bind((host, port))
    print('start:http://' + host + ':' + str(port))
    server.listen(5)
    while True:
        sock, addr = server.accept()
        data = sock.recv(1024)
        data = str(data, encoding='utf-8')
        print(data)
        route = data.split('\r\n')[0].split(' ')[1]

        resp = b'404'
        if route in urls:
            resp = urls[route]()

        sock.send(RESP_HEADER)
        sock.send(resp)
        sock.close()

# 啓服務
if __name__ == '__main__':
    serve('127.0.0.1', 8002)
項目演變

目錄結構

03_proj
	-- template
		-- index.html
		-- user.html
	favicon.ico
	start.py
	urls.py
	views.py

index.html

<h1>{{ name }}</h1>

user.html

<table border="1">
    <tr>
        <th>id</th>
        <th>name</th>
        <th>password</th>
    </tr>
    {% for user in users%}
    <tr>
        <td>{{user.id}}</td>
        <td>{{user.name}}</td>
        <td>{{user.password}}</td>
    </tr>
    {% endfor %}
</table>

start.py

from wsgiref.simple_server import make_server
from urls import urls


def app(env, response):
    print(env)
    # 設置響應頭
    response("200 OK", [('Content-type', 'text/html')])
    route = env['PATH_INFO']
    print(route)
    data = urls['error']()
    if route in urls:
        data = urls[route]()
    # 返回二進制響應體
    return [data]


if __name__ == '__main__':
    server = make_server('127.0.0.1', 8003, app)
    print('start:http://127.0.0.1:8003')
    server.serve_forever()

urls.py

from views import *
urls = {
    '/index': index,
    '/favicon.ico': ico,
    '/user': user,
    'error': error
}

views.py

import pymysql
# 利用jinja2來渲染模板,將後臺數據傳給前臺
from jinja2 import Template

def index():
    with open('templates/index.html', 'r') as f:
        dt = f.read()
    tem = Template(dt)
    resp = tem.render(name='主頁')
    return resp.encode('utf-8')

def ico():
    with open('favicon.ico', 'rb') as f:
        dt = f.read()
    return dt

def user():
    # 數據庫操做
    conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
    cur = conn.cursor(pymysql.cursors.DictCursor)
    cur.execute('select * from user')
    users = cur.fetchall()
    print(users)

    with open('templates/user.html', 'r') as f:
        dt = f.read()
    tem = Template(dt)
    resp = tem.render(users=users)

    return resp.encode('utf-8')

def error():
    return b'404'
Django代碼是什麼樣的?

在傳統的數據驅動網站中,Web應用程序會等待來自Web瀏覽器(或其餘客戶端)的HTTP請求,當接收到請求時,應用程序根據URL和可能的Post數據或GET數據中的信息肯定須要的內容,根據須要,能夠從數據庫讀取或寫入信息,或執行知足需求所需的其餘任務,而後,該應用程序將返回對Web瀏覽器的響應,一般將檢索到的數據插入HTML模板中的佔位符來動態建立用於瀏覽器顯示的HTML頁面.

Django網絡應用程序一般將處理每一個步驟的代碼分組到單獨的文件中.

URLs: 雖然能夠經過單個功能來吹每一個URL的請求,可是編寫單獨的視圖函數來處理每一個資源是更加可維護的.URL映射器用於根據URL將HTTP請求重定向到相應的視圖,URL映射器還能夠匹配出如今URL中的字符串或數字的特寫模式,並將其做爲數據傳遞給視圖功能.

View: 視圖是一個請求處理函數,他接受HTTP請求並返回HTTP響應,視圖經過模型訪問知足請求所需的數據,並將響應的格式委託給模板.

Models: 模型是定義應用程序數據結構的Python對象,並提供在數據庫中管理(添加,修改,刪除)和查詢記錄的機制.

Templates: 模板是定義文件(例如HTML頁面)的結構或佈局的文本文件,用於表示實際內容的佔位符,一個視圖能夠使用HTML模板,從數據填充他動態地建立一個HTML頁面模型,能夠使用模板來定義任何類型的文件的結構: 不必定是HTML.

此種組織被Django稱爲"模型視圖模板(MVT)"架構,他與更加熟悉的Model View Controller架構有許多類似之處.

1.將請求發送到正確的視圖(urls.py)

URL映射器一般存儲在名爲urls.py的文件中,在下面示例中,mapper(urlpatterns)定義了特定URL模式和相應視圖函數之間的映射列表,若是接收到具備與指定模式匹配的URL(例如 r'&$',下面)的HTTP請求,則將調用相關聯的視圖功能(例如views.index)並傳遞請求.

urlpatterns = [  
 url(r'^$', views.index),  
 url(r'^([0-9]+)/$', views.best),  
]  
  
# 注意:  
# 該urlpatterns對象的列表url()功能,在Python中,使用方括號定義列表,項目以逗號分隔,  
# 並可能有一個可選的逗號,例如:[item1,item2,item3,].  
  
# 該模式的奇怪的語法稱爲正則表達式  
  
# 第二個參數url()是當模式匹配時,將被調用的另外一個函數,符號views.index表示該函數被調用,  
# index()而且能夠在被調用的模塊中找到views(即在一個名爲views.py的文件中.)

2.處理請求(views.py)

視圖是web應用程序的核心,從web客戶端接收HTTP請求並返回HTTP響應,在二者之間,他們編制框架的其餘資源來訪問數據庫,渲染模板等.
下面例子顯示了一個最小的視圖功能index(),這能夠經過咱們的URL映射器在上一節調用.
像全部視圖函數同樣,他接受一個HttpRequest對象做爲參數(request)並返回一個HttpResponse對象,在這種狀況下,咱們對請求不作任何事情,咱們的響應只是返回一個硬編碼的字符串,咱們會向您顯示一個請求.

## filename:views.py(Django視圖函數)  
  
從django.http導入HttpResponse  
  
def index(request):  
 #Get HttpRequest-request參數  
 #使用請求中的信息執行操做。  
 #Return HttpResponse  
 return HttpResponse('你好,Django!')  
  
# 注意:  
# Python模塊是函數的'庫',存儲在單獨的文件中,咱們可能想在咱們的代碼塊中使用他們,  
# 在這裏咱們只從django.http模塊導入了HttpResponse對象,使咱們能夠在視圖中使用它:  
  
# from django.http import HttpResponse  
# 還有其餘方法能夠從模塊導入一些或全部對象.  
  
# 如上所示,使用def關鍵字聲明函數,在函數名稱後面的括號列出命名參數: 整行以冒號結尾,  
# 注意下一行是否都進行了縮進,縮進很重要,由於他指定代碼行在該特定塊內(強制縮進是Python的一個關鍵特徵),  
# 也是Python代碼很容易閱讀的一個緣由).  
  
# 視圖一般存儲在一個名爲views.py的文件中

3.定義數據模型(models.py)

Django Web應用程序經過被稱爲模型的Python對象來管理和查詢數據,模型定義存儲數據的結構,包括字段類型以及字段可能的最大值,默認值,選擇列表選項,文檔幫助文本,表單的標籤文本等,模型的定義與底層數據庫無關,你能夠選擇其中一個做爲項目設置的一部分,一旦你選擇了要使用的數據庫,你就不須要直接與之交談,只需編寫模型結構和其餘代碼,Django能夠處理與數據庫通訊的全部辛苦的工做.

下面的代碼片斷爲Team對象展現了一個很是簡單的Django模型,本Team類是從Django的類派生models.Model,他將團隊名稱和團隊級別定義爲字符字段,併爲每一個記錄指定了要存儲的最大字符數,team_level能夠是幾個值中的一個,所以,咱們將其定義爲一個選擇片斷,並在被展現的數據和被存儲的數據之間創建映射,並設置一個默認值.

# filename: models.py  
  
from django.db import models   
  
class Team(models.Model):   
 team_name = models.CharField(max_length=40)   
  
 TEAM_LEVELS = (  
 ('U09', 'Under 09s'),  
 ('U10', 'Under 10s'),  
 ('U11', 'Under 11s'),  
 ...  #list other team levels  
 )  
 team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')  
  
# 注意:  
# Python支持'面向對象編程',這是一種編程風格,咱們將代碼組織到對象中,  
# 其中包括用於對該對象進行操做的相關數據和功能,對象也能夠從其餘對象繼承/擴展/派生,  
# 容許相關對象之間的共同行爲被共享,在Python中,咱們使用關鍵字Class定義對象的‘藍圖’,  
# 咱們能夠根據類中的模型建立類型的多個特定實例.  
  
# 例如,咱們有個Team類,它來自於Model類,這意味着他是一個模型,而且將包含模型的全部方法,  
# 可是咱們也能夠給他本身的專門功能,在咱們的模型中,咱們定義了數據庫須要存儲咱們的數據字段,  
# 給出他們的具體名稱,Django使用這些定義(包括字段名稱)來建立底層數據庫.

4.查詢數據(views.py)

Django模型提供了一個而用於搜索數據庫的簡單查詢API,這能夠使用不一樣的標準(例如,精確,不區分大小寫,大於等等)來匹配多個字段,而且能夠支持複雜語句(例如,您能夠在擁有一個團隊的U11團隊上指定搜索名稱爲以'Fr'開頭或以'al'結尾).

代碼片斷顯示了一個視圖函數(資源處理程序),用於顯示咱們全部的U09團隊,粗體顯示如何使用模型查詢API過濾全部記錄,其中該team_level字段具備正確的文本'U09'(請注意,該條件如何filter()做爲參數傳遞給該函數,該字段名稱和匹配類型由雙下劃線: team_level_exact)

## filename: views.py  
  
from django.shortcuts import render  
from .models import Team   
  
def index(request):  
 list_teams = Team.objects.filter(team_level__exact="U09")  
 context = {'youngest_teams': list_teams}  
 return render(request, '/best/index.html', context)

此功能使用render()功能建立HttpResponse發送回瀏覽器的功能,這個函數是一個快捷方式: 他經過組合指定的HTML模板和一些數據來插入模板(在名爲'context'的變量中提供)來建立一個HTML文件.

5.呈現數據(HTML模板)

模板系統容許你指定輸出文檔的結構,使用佔位符{% if youngest_teams%}來生成頁面填寫的數據,模板一般用於建立HTML,但也能夠建立其餘類型的文檔,Django支持其原生模板系統和另外一種流行的Python庫(稱爲jinja2)開箱即用(若是須要,也能夠支持其餘系統).

代碼片斷顯示render()了上一節函數調用的HTML模板的外觀,這個模板已經被寫入這樣的想法,即他將被訪問一個列表變量,youngest_teams當他被渲染時:

## filename: best/templates/best/index.html  
  
<!DOCTYPE html>  
<html lang="en">  
<body>  
  
 {% if youngest_teams %}  
 <ul>  
 {% for team in youngest_teams %}  
 <li>{{ team.team_name }}</li>  
 {% endfor %}  
 </ul>  
{% else %}  
 <p>No teams are available.</p>  
{% endif %}  
  
</body>  
</html>

前面部分顯示了幾乎每一個Web應用程序將使用的主要功能: URL映射,視圖,模型和模板.
Django提供的其餘內容呢包括:

# 表單: HTML表單用於收集用戶數據以便在服務器上進行處理,Django簡化了表單建立,驗證和處理.  
  
# 用戶身份驗證和權限: Django包含了一個強大的用戶身份驗證和權限系統,該系統已經構建了安全性.  
  
# 緩存: 與提供靜態內容相比,動態建立內容須要更大的計算強度(也更緩慢),DJango提供靈活的緩存,  
# 以便你能夠存儲全部或部分的頁面,如無必要,不會從新呈現網頁.  
  
# 管理網站: 當你使用基本骨架建立應用時,就已經默認包含了一個Django管理站點,它十分輕鬆建立了一個管理頁面,  
# 使網站管理員可以建立、編輯和查看站點中的任何數據模型.  
  
# 序列化數據:  Django能夠輕鬆的將數據序列化,並支持XML或JSON格式,  
# 這會有助於建立一個Web服務(Web服務指數據純粹爲其餘應用程序或站點所用,  
# 並不會在本身的站點中顯示),或是有助於建立一個由客戶端代碼處理和呈現全部數據的網站

6. MVC和MTV
Example1

tree mvc_demo/   
mvc_demo/   
├── Controller   
│   ├── account.py   
│   └── __pycache__   
│       └── account.cpython-36.pyc   
├── Model   
├── Socket_Demo11.py   
└── View   
 └── index.html

Controller/account.py

cat mvc_demo/Controller/account.py   
#!/usr/bin/env python3   
#-*- coding:utf-8 -*-   
# 2020/1/17 10:38   
    
def handle_index():   
 import time   
 v = str(time.time())   
 f = open('View/index.html',mode='rb')   
 data=f.read()   
 f.close()   
 data = data.replace(b'@u',v.encode('utf-8'))   
 return [data,]   
    
def handle_date():   
 return ['<h1>Hello,Date!</h1>'.encode('utf-8'), ]

View/index.html

cat mvc_demo/View/index.html   
<!DOCTYPE html>   
<html lang="en">   
<head>   
 <meta charset="UTF-8">   
 <title>Title</title>   
</head>   
<body>   
 <h1>INDEX @u</h1>   
</body>   
</html>

Socket_Demo1.py

cat mvc_demo/Socket_Demo11.py   
from wsgiref.simple_server import make_server   
from Controller import account   
    
URL_DICT = {   
 "/index": account.handle_index,   
 "/date":  account.handle_date,   
}   
def RunServer(environ, start_response):   
 # environ 封裝了客戶端發來的全部數據   
 # start_response 封裝要返回用戶的數據,響應頭、狀態碼   
 start_response('200 OK', [('Content-Type', 'text/html')])   
 current_url = environ['PATH_INFO']   
 func = None   
 if current_url in URL_DICT:   
 func = URL_DICT[current_url]   
 if func:   
 return func()   
 else:   
 return ['<h1>404</h1>'.encode('utf-8')]   
    
if __name__ == '__main__':   
 httpd = make_server('', 8001, RunServer)   
 print("Serving HTTP on port 8000...")   
 httpd.serve_forever()

訪問測試

cd mvc_demo/   
python3 Socket_Demo11.py   
Serving HTTP on port 8000...   
127.0.0.1 - - [17/Jan/2020 11:13:10] "GET /index HTTP/1.1" 200 161   
    
curl localhost:8001/index   
<!DOCTYPE html>   
<html lang="en">   
<head>   
 <meta charset="UTF-8">   
 <title>Title</title>   
</head>   
<body>   
 <h1>INDEX 1579230790.0229425</h1>   
</body>   
</html>   
# MVC   
# Model			View			Controller   
# 數據庫			模板文件		業務處理   
    
# MTV   
# Model			Template		View   
# 數據庫			模板文件		業務處理
Django生命週期
# 用戶請求 ----> URL對應關係(匹配)  ---->  視圖函數 ----> 返回用戶字符串
# 用戶請求 ----->URL對應關係(匹配)  ----> 視圖函數 ----> 打開一個HTML文件,讀取內容

# 1.瀏覽器發送請求
# 2.wsgi服務器接收到請求,將請求解析交給Django
# 3.Django中間件過濾請求信息,交給路由匹配,若是匹配成功
# 4.路由完成業務邏輯的分發,到指定app下views中指定的視圖函數,能夠去數據庫裏面取數據,
# 5.視圖函數完成具體的業務邏輯,和模板渲染,返回字符串響應結果
# 6.將處理結果經過服務器返回給瀏覽器

Django簡單部署操做

CMD: Django安裝與項目的建立
# 安裝:pip3 install django==2.2
# 查看版本號:django-admin --version
# 新建項目:1.前往目標目錄  2.django-admin startproject proj_name

# 項目目錄,包含項目最基本的一些配置
# 項目名:
   |--項目同名文件夾
	|---- __init__.py  模塊的配置文件
	|---- settings.py  配置總文件
	|---- urls.py      url配置文件,主路由
	|---- wsgi.py      (web server gateway interface),
# 服務器網關接口,python應用與web服務器直接通訊的接口
# templates:模板文件夾,存放html文件的(頁面),支持使用Django模板語言(DTL),也能夠使用第三方(jinja2)
# manage.py:項目管理器,與項目交互的命令行工具集的入口,查看支持的全部命令python3 manage.py

若是是windows電腦會發現pythonx.exe的目錄下多了一個django_admin.exe
咱們能夠cd到那個目錄,使用下面命令建立第一個django項目.
django-admin.exe startproject mysite
而後咱們能夠使用下面命令將此項目運行起來
python.exe C:\Users\You-Men\AppData\Local\Programs\Python\Python37-32\Scripts\mysite\manage.py runserver

瀏覽器訪問上面提示信息的地址就會出現下面頁面了.
若是以爲使用django-admin工具麻煩能夠加入環境變量.
將django-admin那個目錄加入到系統環境變量的PATH裏面,注意用;分號隔開.

app應用的建立

1.Django是面向應用開發,在應用中完成具體的業務邏輯
2.什麼是應用app: 就比如項目中的一個功能模塊,一個項目能夠擁有多個功能模塊,但至少得有一個,Django稱之爲app
3.如何建立app(在項目目錄下):python3 manage.py startapp app01
migrations:數據遷移(移植)模塊,內容都是由Django自動生成

# __init__.py
# admin.py:應用的後臺管理系統配置
# apps.py:django 1.9後,本應用的相關配置
# models.py:數據模型模塊,使用ORM框架,相似於MVC模式下的Model層
# tests.py:自動化測試模塊,能夠寫自動化測試腳本
# views.py:執行相應的邏輯代碼模塊
Django目錄介紹
django-admin startproject mysite  
  
tree  mysite/  
mysite/  
├── manage.py  
└── mysite  
 ├── __init__.py  
 ├── settings.py  
 ├── urls.py  
 └── wsgi.py  
  
  
# 各文件和目錄解釋:  
# 外層的mysite/目錄與Django無關,只是你項目的容器,能夠任意重命名,[工程名稱]  
# manage.py:  一個命令行工具,用於與Django進行不一樣方式的交互腳本,很是重要  
# 內部的mysite/目錄是真正的項目文件包裹目錄,他的名字是你引用內部文件的包名,例如: mysite.urls.  
  
# mysite/__init__.py: 一個定義包的空文件  
# mysite/setting.py: 項目的主配置文件,很是重要.  
# mysite/urls.py: 路有文件,全部的任務都是從這裏開始分配,至關於Django驅動站點的內容表格,很是重要.  
# mysite/wsgi.py:  一個基於WSGI的web服務器進入點,提供底層的網絡通訊功能,一般不用關心,  
# 可是上線時候不要使用wsgi,使用uwsgi,一般再配合nginx。  
  
python3 manage.py startapp cmdb  
tree cmdb/  
cmdb/  
├── admin.py  
├── apps.py  
├── __init__.py  
├── migrations  
│   └── __init__.py  
├── models.py  
├── tests.py  
└── views.py  
# app  
# migrations:   數據修改表結構  
# admin:	Django爲咱們提供的後臺管理  
# apps: 	配置當前app  
# models: ORM寫指定的類,經過命令建立數據庫結構  
# test:	單元測試  
# views:	業務邏輯代碼
Django初始化配置

配置數據庫

# 安裝pymysql
sudo wget https://bootstrap.pypa.io/get-pip.py  --no-check-certificate
sudo python get-pip.py 
sudo pip install pymysql
sudo pip3 install pymysql

# 數據庫
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'youmen_db',
        'USER':'root',
        'PASSWORD':'ZHOUjian.20',
        'HOST':'116.196.83.113',
        'PORT':'3306',
    }
}

靜態文件

# 新建一個與templates同級文件夾,配置文件添加
STATICFILES_DIRS=os.path.join(BASE_DIR,'static'),
# 靜態文件檢索的文件夾

# 語言和時區
LANGUAGE_CODE = 'zh-hans' 
TIME_ZONE = 'Asia/Shanghai'  #
USE_I18N = True   #internationalization  國際化 支持多語言
USE_L10N = True	  #本地化,好比農曆
USE_TZ = True

#修改: 配置文件:
ALLOWED_HOSTS = ['*']  #容許全部訪問
ALLOWED_HOSTS = ['192,168,0.114','127.0.0.1']  #列表爲了防止黑客入侵,只容許列表中的ip地址訪問 
#表示此Django站點能夠投放的主機/域名的字符串列表。這是防止HTTP主機頭部攻擊的安全措施



# (若是須要調用js,css文件能夠配置一下settings.py,html文件就能夠調用static裏面的css,js文件了)
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)

啓動項目

# 終端: python3 manage.py runserver 127.0.0.1:8801
python manage.py runserver                # 啓動
python manage.py runserver 0.0.0.0:8080   #啓動改變端口號和外部訪問:
python manage.py  makemigrations   		  # 生成遷移文件
python manage.py migrate		   		  # 執行遷移,存到數據庫
django-admin startapp xxx          		  # 新建一個app
Django後臺管理(admin)界面

uls.py

from django.contrib import admin
from django.urls import path
urlpatterns = [
    url('admin/',admin.site.urls),
]

app/models.py

from django.db import models

# Create your models here.
class UserType(models.Model):
    name = models.CharField(max_length=32)

class UserInfo(models.Model):
    username=models.CharField(max_length=32)
    pwd=models.CharField(max_length=32)
    email=models.CharField(max_length=32)
    user_type=models.ForeignKey(UserType,on_delete=models.CASCADE)

admin.py

from django.contrib import admin
# Register your models here.
from cmdb import models
admin.site.register(models.UserInfo)

項目運行起來,訪問IP:PORT/admin便可訪問登陸頁面,可是須要先建立一個超級用戶

# 先將以前models.py文件的表建立出來**
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
Username (leave blank to use 'you-men'): youmen
Email address: youmen@163.com
Password:
Password (again):
Django示例之登陸頁面
定義路由規則

url.py

"login"  --> 函數名
# Example
from django.conf.urls import url
from django.contrib import admin
from cmdb import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login',views.login),
    url(r'^home',views.home)
]
定義視圖函數
# app下views.py
# 獲取用戶請求的發來數據
# def func(request):
# 	request.method GET / POST
# 	http://127.0.0.1:8000/home?pid=123&name=nginx
# 	request.GET.get('',None)	# 獲取用戶請求發來的數據
# 	request.POST.get('',None)
#	request.FILES.get('files')	# 此處files對應<input type="file" name="files" />
# 	checkbox等多選內容(值)
# 	request.POST.getlist()

# GET獲取數據     POST: 提交數據

# 返回用戶數據
# return HttpResponse('字符串')
# return render(request,"HTTP模板的路徑")
# return redirect('url路徑')   # return redirect('/login')  # 此處/代指前面的地址.
# Example
from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.shortcuts import redirect

# Create your views here.
from django.shortcuts import HttpResponse

def login(request):
    # 包含用戶提交的全部信息
    # 獲取用戶提交方法
    # print(request.method)
    error_msg = ""
    if request.method == "POST":
        # 用戶經過POST提交過來的數據經過request.GET.get('',None)獲取
        user = request.POST.get('user',None)
        pwd = request.POST.get('pwd',None)
        if user == 'root' and pwd == '123':
            # 去跳轉到
            return redirect(home);
        else:
            error_msg = "用戶名或密碼錯誤"
            # 用戶密碼不匹配
    return render(request,'login.html',{'error_msg':error_msg})

USER_LIST = [
    {'username':'alex','email': 'Tom','gender':'男'},
    {'username':'alex','email': 'Tom','gender':'男'},
    {'username':'alex','email': 'Tom','gender':'男'},
]
# for index in range(20):
#     temp = {'username':'alex' + str(index) ,'email': 'Tom','gender':'男'}
#     USER_LIST.append(temp)
def home(request):
    if request.method == "POST":
        # 獲取用戶提交的數據 POST請求中
        u = request.POST.get('username')
        e = request.POST.get('email')
        g = request.POST.get('gender')
        temp = {'username':u, "email":e,'gender':g}
        USER_LIST.append(temp)
    return render(request,'home.html',{'user_list': USER_LIST})

# def home(request):
模板渲染

特殊的模板語言
分別對應前面view.py當中的變量
home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body style="margin: 0;">
    <div style="height: 48px; background: #dddddd;"></div>
    <div>
        <div>
            <form action="/home" method="post">
                <input type="text" name="username" placeholder="用戶名"/>
                <input type="text" name="email" placeholder="郵箱"/>
                <input type="text" name="gender" placeholder="性別" />
                <input type="submit" value="添加" />
            </form>
        </div>
        <table>
            {% for row in user_list %}
            <tr>
                <td>{{ row.username }}</td>
                <td>{{ row.email }}</td>
                <td>{{ row.gender }}</td>
            </tr>
            {% endfor %}
        </table>
    </div>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/commons.css">
</head>
    <style>
        .label{
            width: 80px;
            text-align: right;
            display: inline-block;
        }
    </style>
<body>
    <form action="/login" method="post">
        <p>
            <label for="username">用戶名:</label>
            <input id="user" name="user" type="text">
        </p>

        <p>
            <label for="password">密碼:</label>
            <input id="pwd" name="pwd" type="password">
            <input type="submit"  value="提交">
            <span style="color: red;">{{ error_msg }}</span>
        </p>
    </form>
    <script src="/static/jquery.min.js"></script>

</body>
</html>
Django示例之註冊頁面

registerd功能頁面
views.py

from django.shortcuts import render,HttpResponse,redirect
import os
# Create your views here.
def index(request):
    return HttpResponse('index')

error_masg = ""
def registered(request):
    if request.method == "GET":
        print(request.method)
        return render(request,'registered.html')
    elif request.method == "POST":
        obj = request.FILES.get('files')
        print(obj,type(obj),obj.name)
        file_path = os.path.join('upload',obj.name)
        f = open(file_path,mode='wb')
        for i in obj.chunks():
            f.write(i)
        f.close()
        return render(request,'registered.html')
# 文件上傳功能須要在from標籤註明enctype="multipart/form-data"

registerd.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/registered" method="POST"  enctype="multipart/form-data">
        <p>
            <input type="text" name="user" placeholder="用戶名">
        </p>

        <p>
            <input type="password" name="pwd" placeholder="密碼" />
        </p>
        <p>
            男: <input type="radio" name="gender" value="1" />
            女: <input type="radio" name="gender" value="2" />
            張揚: <input type="radio" name="gender" value="3" />
        </p>
        <p>
            男: <input type="checkbox" name="favor" value="11" />
            女: <input type="checkbox" name="favor" value="22" />
            張揚: <input type="checkbox" name="favor" value="33" />
        </p>
        <p>
            <select name="area" multiple>
                <option  value="sh">上海</option>
                <option  value="bj">北京</option>
                <option  value="tj">天津</option>
                <option  value="gz">廣州</option>
            </select>
        </p>
        <p>
            <input type="file" name="files">
        </p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

URL路由系統

URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL與要爲該URL調用的視圖函數之間的映射表。你就是以這種方式告訴Django,對於這個URL調用這段代碼,對於那個URL調用那段代碼。

基本格式
from django.conf.urls import url
#循環urlpatterns,找到對應的函數執行,匹配上一個路徑就找到對應的函數執行,就再也不往下循環了,並給函數傳一個參數request,和wsgiref的environ相似,就是請求信息的全部內容
urlpatterns = [
     url(正則表達式, views視圖函數,參數,別名),
]

# 參數說明
# 正則表達式: 一個正則表達式字符串
# views視圖函數: 一個可調用對象,一般爲一個視圖函數或一個指定視圖函數路徑的字符串

# 參數: 可選的要傳遞給視圖函數的默認參數(字典形式)

# 別名: 一個可選的name參數

注意

from django.urls import path

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    path('articles/<int:year>/', views.year_archive),
    path('articles/<int:year>/<int:month>/', views.month_archive),
    path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]
無名分組

場景設置

假如咱們作了圖書管理系統,圖書管理系統是這樣設置的,首頁給咱們展現滿世界排名靠前面的書,當你想看那一年的書,你的url就應該拼接上哪一年,而且將此年份傳遞給後端邏輯,也就是對應的views函數中。好比你的首頁爲127.0.0.1:8000/book/,當你想看2003年有哪些書時,你訪問的url就對應爲127.0.0.1:8000/book/2003/,這也是先後端間接傳遞數據,那麼這種需求如何完成呢?咱們先寫一個views函數,與對應的html。

URL配置

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.login),

    # 無名分組 (給應用視圖函數傳遞位置參數)
    url(r'books/(\d{4})/', views.year_books),  # 徹底匹配
    url(r'^books/(\d{4})/(\d{2})/', views.year_mouth_books),
    url(r'^books/(\d{4})/(\d{2})/(\d{2})', views.year_mouth_day_books),
]

我爲何不寫2003?而寫\d{4}? 你直接寫2003就至關於寫死了,若是用戶想看200四、200五、2006....等,你要寫一堆的url嗎,是否是在articles後面寫一個正則表達式/d{4}/就行啦。

可是此時你寫的仍是有問題的,我以前說過,你此時寫的url路由匹配是模糊匹配,你若是是這樣寫的url,當從瀏覽器輸入127.0.0.1:8000/book/2003/12/08它仍是映射到year_books這個函數的,由於url遵循輪詢機制:

因此針對上面的狀況,咱們應該將這些URL設定爲徹底匹配

注意事項

# 1. urlpatterns中的元素按照書寫順序從上往下逐一匹配正則表達式,一旦匹配成功則再也不繼續。
# 2. 若要從URL中捕獲一個值,只須要在它周圍放置一對圓括號(分組匹配)。
# 3. 不須要添加一個前導的反斜槓(也就是寫在正則最前面的那個/),由於每一個URL 都有。例如,應該是^books而不是 ^/books。
# 4. 每一個正則表達式前面的'r' 是可選的可是建議加上。
# 5. ^books&  以什麼結尾,以什麼開頭,嚴格限制路徑。


# 是否開啓URL訪問地址後面沒有/跳轉至帶有/的路徑的配置項
APPEND_SLASH=True

Django settings.py配置文件中默認沒有 APPEND_SLASH 這個參數,但 Django 默認這個參數爲 APPEND_SLASH = True。 其做用就是自動在網址結尾加'/'。其效果就是:咱們定義了urls.py:

from django.conf.urls import url
from app01 import views

urlpatterns = [
        url(r'^blog/$', views.blog),
]

# 訪問 http://www.example.com/blog 時,默認將網址自動轉換爲 http://www.example/com/blog/ 。

# 若是在settings.py中設置了 APPEND_SLASH=False,此時咱們再請求 http://www.example.com/blog 時就會提示找不到頁面。

# 注意:無名分組傳遞給views函數的爲位置參數。
有名分組

上面的示例使用簡單的正則表達式分組匹配(經過圓括號)來捕獲URL中的值並以位置參數形式傳遞給視圖。

在更高級的用法中,能夠使用分組命名匹配的正則表達式組來捕獲URL中的值並以關鍵字參數形式傳遞給視圖。

在Python的正則表達式中,分組命名正則表達式組的語法是(?P<name>pattern),其中name是組的名稱,pattern是要匹配的模式。

URL配置

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
#某年的,(?P<year>[0-9]{4})這是命名參數(正則命名匹配還記得嗎?),
# 那麼函數year_archive(request,year),形參名稱必須是year這個名字。
# 並且注意若是你這個正則後面沒有寫$符號,即使是輸入了月份路徑,也會被它攔截下拉,由於它的正則也能匹配上
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    #某年某月的
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), 
    # 某年某月某日的
]

# 這個實現與前面的示例徹底相同,只有一個細微的差異:捕獲的值做爲關鍵字參數而不是位置參數傳遞給視圖函數

Views配置

def year_article(request, year=2000):
    print(year)
    return HttpResponse(f'出版年限:{year}')


def year_mouth_article(request, year, mouth):
    print(year, mouth)
    return HttpResponse(f'出版年限:{year},出版月份:{mouth}')

def year_mouth_day_article(request, year, mouth, day):
    print(year, mouth, day)
    return HttpResponse(f'出版年限:{year},出版月份:{mouth},出版天數:{day}')

注意: 命名分組是url設置的組名對應views函數的參數,這是關鍵字參數,也能夠對應views函數的默認值參數。

在實際應用中,使用分組命名匹配的方式可讓你的URLconf 更加明晰且不容易產生參數順序問題的錯誤,可是有些開發人員則認爲分組命名組語法太醜陋、繁瑣。

至於究竟應該使用哪種,你能夠根據本身的喜愛來決定。

URLconf匹配的位置

URLconf 在請求的URL 上查找,將它當作一個普通的Python 字符串。不包括GET和POST參數以及域名。

  例如,http://www.example.com/myapp/ 請求中,URLconf 將查找myapp/。

  在http://www.example.com/myapp/?page=3 請求中,URLconf 仍將查找myapp/

  URLconf 不檢查請求的方法。換句話講,全部的請求方法 —— 同一個URL的POSTGETHEAD等等 —— 都將路由到相同的函數.

捕獲的參數永遠是字符串

每一個在URLconf中捕獲的參數都做爲一個普通的Python字符串傳遞給視圖,不管正則表達式使用的是什麼匹配方式。例如,下面這行URLconf 中:

url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

傳遞到視圖函數views.year_archive()中的year參數永遠是一個字符串類型

視圖函數中指定默認值

# urls.py中
from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^blog/$', views.page),
    url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
]

# views.py中,能夠爲num指定默認值
def page(request, num="1"):
    pass

在上面的例子中,兩個URL模式指向相同的view - views.page - 可是第一個模式並無從URL中捕獲任何東西。

若是第一個模式匹配上了,page()函數將使用其默認參數num=「1」,若是第二個模式匹配,page()將使用正則表達式捕獲到的num值。

URL分發

建立項目urls.py

from django.conf.urls import url,include
from app01 import views

urlpatterns = [
    # 首頁
    url(r'^$',views.base),
    url('^app01/',include('app01.urls')),
]

aap01應用的urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url('^login/',views.login),
]

app01應用的views.py

from django.shortcuts import render
from django.shortcuts import redirect

def login(request):
    return render(request,'login1.html')


def base(request):
    return render(request,'base.html')
傳遞額外參數到視圖函數

URLconfs 具備一個鉤子,讓你傳遞一個Python 字典做爲額外的參數傳遞給視圖函數。

  django.conf.urls.url() 函數能夠接收一個可選的第三個參數,它是一個字典,表示想要傳遞給視圖函數的額外關鍵字參數。

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
    
# 注意,這就像一個命名分組同樣,你的函數裏面的必須有一個形參,形參必須叫作foo才行,若是是命名分組的url,那麼foo參數寫在函數的哪一個位置都行,若是不是命名分組,那麼都是將這個形參寫在參數的最後。
]

Views視圖函數

一個視圖函數(類),簡稱視圖,是一個簡單的Python 函數(類),它接受Web請求而且返回Web響應。

  響應能夠是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片。

  不管視圖自己包含什麼邏輯,都要返回響應。代碼寫在哪裏也無所謂,只要它在你當前項目目錄下面。除此以外沒有更多的要求了——能夠說「沒有什麼神奇的地方」。爲了將代碼放在某處,你們約定成俗將視圖放置在項目(project)或應用程序(app)目錄中的名爲views.py的文件中。

一個簡單的視圖

下面是一個以HTML文檔的形式返回當前日期和時間的視圖

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

首先,咱們從django.http模塊引入了HttpResponse類,以及Python的datetime庫.

接着,咱們定義了current_datetime函數,他就是視圖函數,每一個視圖函數都使用HttpRequest對象做爲第一個參數,而且一般稱之爲request.

注意,視圖函數的名稱並不重要,不須要用一個統一的命名方式來命名,以便讓Django識別它,咱們將其命名爲current_datetime,是由於這個名稱可以比較準確地反映出他實現的功能.

這個視圖會返回一個HttpResponse對象,其中包含生成的響應,每一個視圖函數都負責返回一個HttpResponse對象

DJango使用功能請求和響應對象來經過系統傳遞狀態.

當瀏覽器向服務端請求一個頁面時,Django建立一個HttpRequest對象,該對象包含關於請求的元數據,而後, Django加載相應的視圖,將這個HttpRequest對象做爲第一個參數傳遞給視圖函數.

每一個視圖負責返回一個HttpResponse對象

12

請求對象

簡單過程

當一個頁面被請求時,Django就會建立一個包含本次請求源信息(請求報文中的請求行,首部信息,內容主體等)

請求相關的經常使用值

# path_info		返回用戶訪問url,不包括域名
# method		請求中使用的HTTP方法的字符串表示,全大寫表示
# GET			包含全部HTTP GET參數的類字典對象
# POST			包含全部HTTP POST參數的類字典對象
# body			請求體,byte類型 request.POST的數據就是從body裏面提取到的

上傳文件示例

def upload(request):
    """
    保存上傳文件前,數據須要存放在某個位置。默認當上傳文件小於2.5M時,django會將上傳文件的所有內容讀進內存。從內存讀取一次,寫磁盤一次。
    但當上傳文件很大時,django會把上傳文件寫到臨時文件中,而後存放到系統臨時文件夾中。
    :param request: 
    :return: 
    """
    if request.method == "POST":
        # 從請求的FILES中獲取上傳文件的文件名,file爲頁面上type=files類型input的name屬性值
        filename = request.FILES["file"].name
        # 在項目目錄下新建一個文件
        with open(filename, "wb") as f:
            # 從上傳的文件對象中一點一點讀
            for chunk in request.FILES["file"].chunks():
                # 寫入本地文件
                f.write(chunk)
        return HttpResponse("上傳OK")

方法

1.HttpRequest.get_host()
  根據從HTTP_X_FORWARDED_HOST(若是打開 USE_X_FORWARDED_HOST,默認爲False)和 HTTP_HOST 頭部信息返回請求的原始主機。
   若是這兩個頭部沒有提供相應的值,則使用SERVER_NAME 和SERVER_PORT,在PEP 3333 中有詳細描述。

  USE_X_FORWARDED_HOST:一個布爾值,用於指定是否優先使用 X-Forwarded-Host 首部,僅在代理設置了該首部的狀況下,才能夠被使用。
  例如:"127.0.0.1:8000"
  注意:當主機位於多個代理後面時,get_host() 方法將會失敗。除非使用中間件重寫代理的首部。

2.HttpRequest.get_full_path()
  返回 path,若是能夠將加上查詢字符串。
  例如:"/music/bands/the_beatles/?print=true"
 

3.HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

  返回簽名過的Cookie 對應的值,若是簽名再也不合法則返回django.core.signing.BadSignature。

  若是提供 default 參數,將不會引起異常並返回 default 的值。

  可選參數salt 能夠用來對安全密鑰強力攻擊提供額外的保護。max_age 參數用於檢查Cookie 對應的時間戳以確保Cookie 的時間不會超過max_age 秒。

        >>> request.get_signed_cookie('name')
        'Tony'
        >>> request.get_signed_cookie('name', salt='name-salt')
        'Tony' # 假設在設置cookie的時候使用的是相同的salt
        >>> request.get_signed_cookie('non-existing-cookie')
        ...
        KeyError: 'non-existing-cookie'    # 沒有相應的鍵時觸發異常
        >>> request.get_signed_cookie('non-existing-cookie', False)
        False
        >>> request.get_signed_cookie('cookie-that-was-tampered-with')
        ...
        BadSignature: ...    
        >>> request.get_signed_cookie('name', max_age=60)
        ...
        SignatureExpired: Signature age 1677.3839159 > 60 seconds
        >>> request.get_signed_cookie('name', False, max_age=60)
        False

4.HttpRequest.is_secure()
  若是請求時是安全的,則返回True;即請求通是過 HTTPS 發起的。

5.HttpRequest.is_ajax()

  若是請求是經過XMLHttpRequest 發起的,則返回True,方法是檢查 HTTP_X_REQUESTED_WITH 相應的首部是不是字符串'XMLHttpRequest'。

  大部分現代的 JavaScript 庫都會發送這個頭部。若是你編寫本身的 XMLHttpRequest 調用(在瀏覽器端),你必須手工設置這個值來讓 is_ajax() 能夠工做。

  若是一個響應須要根據請求是不是經過AJAX 發起的,而且你正在使用某種形式的緩存例如Django 的 cache middleware, 
   你應該使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 裝飾你的視圖以讓響應可以正確地緩存。

Example1

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

def index(request):
    print(request.method) #請求方式
    print(request.path)   #請求路徑,不帶參數的
    print(request.POST)   #post請求數據  字典格式
    print(request.GET)    #get的請求數據  字典格式
    print(request.META)   #請求頭信息,未來用到哪一個我們再說哪一個
    print(request.get_full_path())   #獲取請求路徑帶參數的,/index/?a=1
    print(request.is_ajax())   #判斷是否是ajax發送的請求,True和False
    '''
        Django必定最後會響應一個HttpResponse的示例對象
        三種形式:
HttpResponse('字符串') 最簡單

render(頁面)   最重要
                2.1 兩個功能
                    -- 讀取文件字符串
                    -- 嵌入變量(模板渲染) html裏面:{{ name }} , {'name':'太白'}做爲render的第三個參數,想寫多個變量{'name':'太白','hobby':['籃球','羽毛球']....}

redirect() 重定向  最難理解,某個網站搬家了,網址變了,訪問原來的網址就重定向到一個新網址,就叫作重定向,網站本身作的重定向,你訪問仍是訪問的你以前的,
你本身啥也不用作,瀏覽器發送請求,而後服務端響應,而後服務端告訴瀏覽器,你直接跳轉到另一個網址上,那麼瀏覽器又自動發送了另一個請求,發送到服務端,
服務端返回一個頁面,包含兩次請求,登錄成功後跳轉到網站的首頁,網站首頁的網址和你login登錄頁面的網址是不用的。
                
    '''

    return render(request,'index.html',{'name':'太白'})
    # return HttpResponse('ok')
響應對象HttpResponse

與Django自動建立的HttpRequest對象相比,HttpResponse對象使咱們的職責範圍了,咱們寫的每一個視圖都須要實例化,填充和返回一個HttpResponse.

HttpResponse類位於django.http模塊中

簡單實用

傳遞字符串

from django.http import HttpResponse
response = HttpResponse("Here's the text of the Web page.")
response = HttpResponse("Text only, please.", content_type="text/plain")

設置或刪除響應頭信息

response = HttpResponse()
response['Content-Type'] = 'text/html; charset=UTF-8'
del response['Content-Type']

# 屬性
# HttpResponse.content;		響應內容
# HttpResponse.charset;		響應內容的編碼
# HttpResponse.status_code; 響應的狀態碼
具體響應方法

響應對象主要有三種形式

HttpResponse()
render()
redirect()

HttpResponse

HttpResponse()括號內直接跟一個具體的字符串做爲響應體,比較直接很簡單,通常使用後面兩種形式.

render

結合一個給定的模板和一個指定的上下文字典,並返回一個渲染後的HttpResponse對象

from django.shortcuts import render

def my_view(request):
    # 視圖的代碼寫在這裏
    return render(request, 'myapp/index.html', {'foo': 'bar'})

# 上面的代碼等同於
from django.http import HttpResponse
from django.template import loader

def my_view(request):
    # 視圖代碼寫在這裏
    t = loader.get_template('myapp/index.html')
    c = {'foo': 'bar'}
    return HttpResponse(t.render(c, request))

redirect

好比登陸成功以後,跳轉到別的頁面,這不是你操做的而是後臺幫你操做的,爲的是用戶體驗。還有一種狀況:web網站每一段時間代碼就須要更新,可是有時候更新不了了,就須要從新寫一個頁面,這樣就會自動給你跳轉到新的url上,老得網站不會維護了,還有你常常訪問的網址因爲某種不可描述的緣由有危險了,這是須要咱們重定向新的網址 哈哈

那麼爲何不給他新的?原來的老用戶只是知道你的老網站。固然這只是一些狀況,其實redirect具體用法還有不少,他的參數能夠是:

# 1. 一個模型:將調用模型的get_absolute_url() 函數
# 2.一個視圖,能夠帶有參數:將使用urlresolvers.reverse 來反向解析名稱
# 3.一個絕對的或相對的URL,將原封不動的做爲重定向的位置。
# 默認返回一個臨時的重定向;傳遞permanent=True 能夠返回一個永久的重定向。

咱們能夠用多種方式使用redirect()函數

1.傳遞一個具體的ORM對象

# 將調用ORM對象的get_absolute_url()方法來獲取重定向的URL;

from django.shortcuts import redirect
 
def my_view(request):
    ...
    object = MyModel.objects.get(...)
    return redirect(object)

2. 傳遞一個視圖的名稱

# views 示例
def my_view(request):
    ...
    return redirect('home')

def home(request):
  return HttpResponse('跳轉成功!')
 
# urls示例:
urlpatterns = [
    url(r'home/', views.home, name='home'),  # 起別名,這樣之後不管url如何變化,訪問home直接能夠映射到home函數
]

3.傳遞要重定向到的一個具體的網址

def my_view(request):
    ...
    return redirect('/index')

4. 固然也能夠是一個完整的網址

def my_view(request):
    ...
    return redirect('http://example.com/')

重定向狀態碼

# 301和302的區別。

# 301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器返回的這個狀態碼後會自動跳轉到一個新的URL地址,這個地址能夠從響應的Location首部中獲取
# (用戶看到的效果就是他輸入的地址A瞬間變成了另外一個地址B)——這是它們的共同點。

# 他們的不一樣在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了),搜索引擎在抓取新內容的同時也將舊的網址交換爲重定向以後的網址;

# 302表示舊地址A的資源還在(仍然能夠訪問),這個重定向只是臨時地從舊地址A跳轉到地址B,搜索引擎會抓取新的內容而保存舊的網址。 SEO302好於301

 

# 重定向緣由:
#(1)網站調整(如改變網頁目錄結構);
#(2)網頁被移到一個新地址;
#(3)網頁擴展名改變(如應用須要把.php改爲.Html或.shtml)。
#  這種狀況下,若是不作重定向,則用戶收藏夾或搜索引擎數據庫中舊地址只能讓訪問客戶獲得一個404頁面錯誤信息,訪問流量白白喪失;再者某些註冊了多個域名的
# 網站,也須要經過重定向讓訪問這些域名的用戶自動跳轉到主站點等。

# 簡單來講就是:
# 響應狀態碼:301,爲臨時重定向,舊地址的資源仍然能夠訪問。 
# 響應狀態碼:302,爲永久重定向,舊地址的資源已經被永久移除了,這個資源不可訪問了。 
# 對普通用戶來講是沒什麼區別的,它主要面向的是搜索引擎的機器人。
# A頁面臨時重定向到B頁面,那搜索引擎收錄的就是A頁面。
# A頁面永久重定向到B頁面,那搜索引擎收錄的就是B頁面。

Django的CBV和FBV

FBV(function base views) 就是在視圖裏使用函數處理請求。以前都是FBV模式寫的代碼,因此就不寫例子了。

  CBV(class base views) 就是在視圖裏使用類處理請求。

  Python是一個面向對象的編程語言,若是隻用函數來開發,有不少面向對象的優勢就錯失了(繼承、封裝、多態)。因此Django在後來加入了Class-Based-View。可讓咱們用類寫View。這樣作的優勢主要下面兩種:

  1. 提升了代碼的複用性,能夠使用面嚮對象的技術,好比Mixin(多繼承)
  2. 能夠用不一樣的函數針對不一樣的HTTP方法處理,而不是經過不少if判斷,提升代碼可讀性
CBV演示簡單登陸流程

views視圖函數

from django.shortcuts import render,HttpResponse
from django.views import View  # 從django.views模塊中引用View類

class Login(View):
    """
    本身定義get post方法,方法名不能變。這樣只要訪問此url,get請求自動執行get方法,post請求自動執行post方法,與咱們寫的FBV 
if request.method == 'GET' or 'POST' 同樣。
    """
    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username.strip() == 'taibai' and password.strip() == '123':
            return HttpResponse('登陸成功')

        return render(request, 'login.html')

url路由

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^login/', views.Login.as_view()),
    # 引用views函數重的Login類,而後調用父類的as_view()方法
]

# CBV傳參,和FBV相似,有名分組,無名分組
# url寫法:無名分組的
# url(r'^cv/(\d{2})/', views.Myd.as_view(),name='cv'),
# url(r'^cv/(?P<n>\d{2})/', views.Myd.as_view(name='xxx'),name='cv'),
# 若是想給類的name屬性賦值,前提你的Myd類裏面必須有name屬性(類屬性,
# 定義init方法來接受屬性行不通,可是能夠自行研究一下,看看如何行通,意義不大),
# 而且以前類裏面的name屬性的值會被覆蓋掉

templates的html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好 歡迎來到plus會所,請先登陸</h1>

<form action="" method="post">
    <input type="text" name="username">
    <p></p>
    <input type="text" name="password">
    <p></p>
    <input type="submit">
</form>

</body>
</html>
視圖函數的裝飾器

面試有問過,若是你用CBV模式,你的get或者post方法是從哪裏執行的? 可否在get執行以前或者以後作一些特殊的操做?

你的get或者post方法都是在源碼的dispatch方法中執行的,咱們能夠利用重寫父類的dispatch方法,就可以對get和post請求搞事情了。可是若是我想單獨對某個請求方法搞事情,那麼只能加上裝飾器了。

FBV裝飾器

對於FBV這種開發方式,加上裝飾器很簡單,就是咱們以前講過的方式,這種比較簡單,這裏就直接展現view視圖函數的代碼便可

def wrapper(func):

    def inner(*args,**kwargs):
        print('請求來了!')
        ret = func(*args,**kwargs)
        print('請求走了!')
        return ret
    return inner

@wrapper
def home(request):
    print('執行home函數')
    return render(request, 'home.html')
CBV裝飾器

方法一: 直接加裝飾器

由於template,urls很簡單,此處只展現views函數

views視圖函數

def wrapper(func):

    def inner(*args,**kwargs):
        print('請求來了!')
        ret = func(*args,**kwargs)
        print('請求走了!')
        return ret
    return inner


class Login(View):

    @wrapper
    def get(self, request):
        print('get 方法')
        return render(request, 'login.html')

    def post(self, request):

        username = request.POST.get('username')
        password = request.POST.get('password')

        if username.strip() == 'taibai' and password.strip() == '123':
            return HttpResponse('登陸成功')

        return render(request, 'login.html')

'''
執行流程:
請求來了!
執行home函數
請求走了!
'''

方法二: 藉助method_decorator模塊

from django.utils.decorators import method_decorator
def wrapper(func):

    def inner(*args,**kwargs):
        print('請求來了!')
        ret = func(*args,**kwargs)
        print('請求走了!')
        return ret
    return inner


class Login(View):

    @method_decorator(wrapper)
    def get(self, request):
        print('get 方法')
        return render(request, 'login.html')

    def post(self, request):

        username = request.POST.get('username')
        password = request.POST.get('password')

        if username.strip() == 'taibai' and password.strip() == '123':
            return HttpResponse('登陸成功')

        return render(request, 'login.html')

方式三: 給全部方法都加上裝飾器

在咱們的源碼dispatch方法。咱們能夠在子類中重寫父類的dispatch方法,由於不管執行什麼請求方法(post,get,push,delete等等)都是dispatch方法利用反射調用的。因此,咱們給此方法加上裝飾器便可。

class Login(View):
   
    @wrapper
    def dispatch(self, request, *args, **kwargs):
        ret = super().dispatch(request, *args, **kwargs)
        return ret

方式四: 直接在類上加裝飾器(不經常使用)

注意

# 注意csrf-token裝飾器的特殊性,在CBV模式下它只能加在dispatch上面(後面再說)
# 下面這是csrf_token的裝飾器:
# @csrf_protect,爲當前函數強制設置防跨站請求僞造功能,即使settings中沒有設置csrfToken全局中間件。
# @csrf_exempt,取消當前函數防跨站請求僞造功能,即使settings中設置了全局中間件。
# 注意:from django.views.decorators.csrf import csrf_exempt,csrf_protect

Django的模板系統

什麼是模版系統?這裏作一個簡單解釋。要想明白什麼是模版系統,那麼咱們得先分清楚靜態頁面和動態頁面。咱們以前學過的都是靜態頁面,所謂的靜態頁面就是瀏覽器向後端發送一個請求,後端接收到這個請求,而後返回給瀏覽器一個html頁面,這個過程不涉及從數據庫取出數據渲染到html頁面上,只是單純的返回一個頁面(數據所有在html頁面上)。而動態頁面就是在給瀏覽器返回html頁面以前,須要後端與數據庫之間進行數據交互,而後將數據渲染到html頁面上在返回給瀏覽器。言外之意靜態頁面不涉及數據庫,動態頁面須要涉及從數據庫取出數據。那麼模版系統是什麼呢?若是你只是單純的寫靜態頁面,也就沒必有必要用模版系統了,只用動態頁面才須要模版系統。

簡單來講,模版系統就是在html頁面想要展現的數據庫或者後端的數據的標籤上作上特殊的佔位(相似於格式化輸出),經過render方法將這些佔位的標籤裏面的數據替換成你想替換的數據,而後再將替換數據以後的html頁面返回給瀏覽器,這個就是模版系統。

模板渲染的官方文檔

關於模板渲染你只須要記兩種特殊符號(語法):

{{ }}和 {% %}

變量相關的用{{}},邏輯相關的用{%%}。

變量

Example1

url

from django.conf.urls import url,include
from app01 import views

urlpatterns = [
    # 首頁
    url(r'^$',views.base),
    url('^app01/',include('app01.urls')),
]

# Django_Demo1/app01/urls.py  (第二個app應用的urls.py)
from django.conf.urls import url
from app01 import views

urlpatterns = [
    url('^login/',views.login),
]

views

render第三個參數接受一個字典的形式,經過字典的鍵值對index.html頁面進行渲染,這也就是模板渲染

from django.shortcuts import render
from django.shortcuts import redirect


def login(request):
    name = "幽夢"
    age = 20
    name_list = ['幽夢', 'flying', ]
    dic = {'幽夢': 18, 'flying': 21}
    return render(request, 'login1.html', {'name': name, 'age': age, 'name_list': name_list, 'dic_class': dic})

login1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1 {
            height: 48px;
            background: #dddddd;
            text-align: center
        }
    </style>
</head>
<body style="margin: 0;">
<div class="c1">歡迎來到You-Men博客</div>

<ul>
    <li>{{ name }}</li>
    <li>{{ age }}</li>
    <li>{{ name_list }}</li>
    <li>{{ dic_class }}</li>
</ul>
</body>
</html>

語法

在Django的模板語言中按此語法使用: {{ 變量名 }}

當模板引擎遇到一個變量,他將計算這個變量,而後用結果替換掉他自己, 變量的命名包括任何字母數字以及下劃線("_")的組合. 變量名稱中不能有空格或標點字符.

深度查詢點符(.)在模板語言中有特殊的含義,當模板系統遇到點("."),他將以這樣的順序查詢:

# 字典查詢(Dictionary lookup)
# 屬性或方法查詢(Attribute or method lookup)
# 數字索引查詢(Numeric index lookup)
萬能的點

經過簡單示例咱們已經知道模板系統對於變量的渲染是如何作到的,很是簡單,下面將演示一下深刻的渲染,咱們不想將整個列表或字典渲染到html,而是將列表裏的元素,或者字典的某個值渲染到html頁面中,就能夠經過萬能的點

views

from django.shortcuts import render
from django.shortcuts import redirect


def home(request):
    name = "幽夢"
    age = 20
    name_list = ['幽夢', 'flying', '紅玫瑰','紫玫瑰','白玫瑰']
    dic = {'幽夢': 18, 'flying': 21,"黃玫瑰":22}
    return render(request, 'home.html', locals())


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

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1 {
            height: 48px;
            background: #dddddd;
            text-align: center
        }
    </style>
</head>
<body style="margin: 0;">
<div class="c1">歡迎來到You-Men博客</div>

<ul>
    <li>{{ name }}</li>
    <li>{{ age }}</li>
    <li>{{ name_list.2 }}</li>
    <li>{{ dic.黃玫瑰 }}</li>
</ul>
</body>
</html>

1590912608642

過濾器

1. 什麼是過濾器?

有的時候咱們經過render渲染到html的數據並非咱們最終想要的數據,好比後端向前端傳遞的數據爲hello,但咱們想要hello顯示爲HELLO,這個能夠後端提早處理的, 諸如此類需求咱們能夠經過過濾器來解決.

2. 語法

# 過濾器的語法:  {{ value | filter_name:參數 }}
# 使用管道符"|"來應用過濾器
# 例如: {{ name|lower }}會將name變量應用lower過濾器以後再顯示他的值,lower在這裏的做用就是將文本所有都變爲小寫.

# 注意事項:
# 1. 過濾器支持"鏈式"操做,即一個過濾器的輸出做爲另外一個過濾器的輸入.
# 2. 過濾器能夠接受參數,例如: {{ ss|truncatewords:30 }},這將顯示ss的前三十個詞
# 3. 過濾器參數包含空格的話,必須用引號包裹起來,好比使用逗號和空格去鏈接一個一個列表中的元素, 如{{ list|join:',' }}
# 4. '|'左右沒有空格!沒有空格 ! 沒有空格
# Django的模板語言中提供了大約六十個內置過濾器

經常使用過濾器

1. default: 若是一個變量是false或者爲空,或者給定的默認值,不然,使用變量的值

views:
a = ''  # 沒有變量a或者變量a爲空


html:  # 顯示啥也沒有
{{ value|default:"啥也沒有"}}

2. length: 返回值的長度,做用於字符串和列表

views:
name_list = ['幽夢', 'flying', '紅玫瑰','紫玫瑰','白玫瑰']
return render(request, 'home.html', { 'name_list':name_list})

html:
{{ name_list|length }}  # 顯示爲5

3. filesizeformat: 將值格式化爲一個"人類可讀的"文件尺寸(例如'13kb','4.1M','102bytes')

views:
value = 1048576

html:
{{ value|filesizeformat }}  # 顯示爲1.0MB

4. slice: 切片,支持python中能夠用切片的全部數據類型

views:
name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']
s = '太白金星講師'

html:
{{ name_list|slice:'1:3' }}  # ['天琪', '傻強']
{{ s|slice:'1::2' }}  # '白星師'

5. date: 時間格式化

# views
time = datetime.datetime.now()
return render(request, 'home.html', { 'time':time })    
    

# html    
<li>{{ time|date:"Y-m-d H:i:s" }}</li>

1590916015425

truncatechars: 若是字符串字符多餘指定的字符數量,那麼會被截斷,截斷的字符串將以可翻譯的省略號序列("...")結尾

# views:
describe = '1999年3月,馬雲正式辭去公職,後來被稱爲18羅漢的馬雲團隊回到杭州,湊夠50萬元人民幣'

# html:
{{ describe|truncatechars:9 }}  # 1999年3...

# 截斷9個字符,三個點也算三個字符
# 咱們瀏覽網頁能夠常常看到一個連接下面有一段註釋,而後就是...

truncatewords: 在必定數量的字後截斷字符串,是截多少個單詞

views:
words = 'i love you my country china'

html:
{{ words|truncatewords:3 }}  # i love you...

cut: 移除value中全部的與給出變量相同的字符串

views:
words = 'i love you my country china'

html:
{{ words|cut:3 }}  # iloveyou

join: 設定鏈接符將可迭代對象的元素鏈接在一塊兒與字符串的join方法相同

views:
name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']
dic = {'name':'太白','age': 18}
tu = (1, 2, 3)

html:
<li>{{ name_list|join:'_'}}</li>
<li>{{ dic|join:','}}</li>
<li>{{ tu|join:'+'}}</li>
'''
王闊_天琪_傻強_志晨_健身哥
name,age
1+2+3
'''

safe

Django的模板中在進行模板渲染的時候會對HTML標籤和JS等語法標籤進行自動轉義,緣由顯而易見,這樣是爲了安全,django擔憂這是用戶添加的數據,好比若是有人給你評論的時候寫了一段js代碼,這個評論一提交,js代碼就執行啦,這樣你是否是能夠搞一些壞事兒了,寫個彈窗的死循環,那瀏覽器還能用嗎,是否是會一直彈窗啊,這叫作xss攻擊,因此瀏覽器不讓你這麼搞,給你轉義了。可是有的時候咱們可能不但願這些HTML元素被轉義,好比咱們作一個內容管理系統,後臺添加的文章中是通過修飾的,這些修飾多是經過一個相似於FCKeditor編輯加註了HTML修飾符的文本,若是自動轉義的話顯示的就是保護HTML標籤的源文件。爲了在Django中關閉HTML的自動轉義有兩種方式,若是是一個單獨的變量咱們能夠經過過濾器「|safe」的方式告訴Django這段代碼是安全的沒必要轉義。

  咱們去network那個地方看看,瀏覽器看到的都是渲染以後的結果,經過network的response的那個部分能夠看到,這個a標籤所有是特殊符號包裹起來的,並非一個標籤,這都是django搞得事情。

不少網站,都會對你提交的內容進行過濾,一些敏感詞彙、特殊字符、標籤、黃賭毒詞彙等等,你一提交內容,人家就會檢測你提交的內容,若是包含這些詞彙,就不讓你提交,其實這也是解決xss攻擊的根本途徑,例如博客園:

timesnce

將日期格式設爲自該日期起的時間(例如, "四天,5小時")

採用一個可選參數,他是一個包含用做比較點的日期的變量(不帶參數,比較點爲如今)。 例如,若是since_12是表示2012年6月28日日期實例,而且comment_date是2018年3月1日日期實例:

views:
  year_12 = datetime.datetime.now().replace(year=2012, month=6, day=28)
  year_18 = datetime.datetime.now().replace(year=2018, month=3, day=1)

html:
 <li>{{ year_12|timesince:year_18}}</li>

'''
5 years, 8 months
'''
# 若是year_18不寫,默認就是距如今的時間段

timeuntil

似於timesince,除了它測量從如今開始直到給定日期或日期時間的時間。 例如,若是今天是2006年6月1日,而conference_date是保留2006年6月29日的日期實例,則{{ conference_date | timeuntil }}將返回「4周」。

  使用可選參數,它是一個包含用做比較點的日期(而不是如今)的變量。 若是from_date包含2006年6月22日,則如下內容將返回「1周」:

{{ conference_date|timeuntil:from_date }}

更多內置過濾器(此連接頁面最下面的內置過濾器):https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#filters

標籤Tags

如今咱們已經能夠從後端經過模版系統替換掉前端的數據了,可是若是隻是替換掉數據,確實不夠靈活,好比,咱們要想在前端頁面展現可迭代對象name_list = ['王闊', '天琪', '傻強', '志晨', '健身哥']每一個元素,你如和展現呢?

# 前端頁面
<ul>
    <li>{{ name_list.0 }}</li>
    <li>{{ name_list.1 }}</li>
    <li>{{ name_list.2 }}</li>
    <li>{{ name_list.3 }}</li>
</ul>

這樣寫明顯很low,咱們要是能夠用上for循環就行了。Django這麼強大的框架,不可能想不到這點的,這裏模版系統給咱們提供了另外一種標籤,就是能夠在html頁面中進行for循環以及if判斷等等。

標籤看起來像是這樣的: {% tag %}。標籤比變量更加複雜:一些在輸出中建立文本,一些經過循環或邏輯來控制流程,一些加載其後的變量將使用到的額外信息到模版中。與python語法不一樣的是:一些標籤須要開始和結束標籤 (例如{% tag %} ...標籤 內容 ... {% endtag %})。

學習下面幾種標籤以前,咱們要從新寫一個url、views以及html,方便分類學習不與上面的變量產生衝突。

for標籤

基本語法

{% for 變量 in render的可迭代對象 %}
        {{ 變量 }}
{% endfor %}

# 例如:
{% for foo in name_list %}
        {{ foo }}
{% endfor %}

view

import datetime
from django.shortcuts import render
from django.shortcuts import redirect

def home(request):
    name = "幽夢"
    age = 20
    size = 2342314
    time = datetime.datetime.now()
    name_list = ['幽夢', 'flying', '紅玫瑰','紫玫瑰','白玫瑰']
    dic = {'幽夢': 18, 'flying': 21,"黃玫瑰":22}
    return render(request, 'home.html', { 'name_list':name_list })

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1 {
            height: 48px;
            background: #dddddd;
            text-align: center
        }
    </style>
</head>
<body style="margin: 0;">
<div class="c1">歡迎來到You-Men博客</div>

<li>{{ name }}</li>
<li>{{ age }}</li>
<ul>
    {% for i in name_list %}
        <li>{{ i }}</li>
    {% endfor %}
</ul>
<li>{{ dic }}</li>
</body>
</html>

遍歷一個列表

<ul>
    {% for foo in name_list %}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

反向遍歷一個列表

{% for foo in name_list reversed %}
        <li>{{ foo }}</li>
{% endfor %}

遍歷一個字典: 有items,keys,values參數

<ul>

    {% for key,value in dic.items %}
        <li>{{ key }}: {{ value }}</li>
    {% endfor %}

    {% for key in dic.keys %}
        <li>{{ key }}</li>
    {% endfor %}

    {% for value in dic %}
        <li>{{ value }}</li>
    {% endfor %}

</ul>

forloop

模版系統給咱們的for標籤還提供了forloop的功能,這個就是獲取循環的次數,有多種用法:

forloop.counter            # 當前循環的索引值(從1開始),forloop是循環器,經過點來使用功能
forloop.counter0           # 當前循環的索引值(從0開始)
forloop.revcounter         # 當前循環的倒序索引值(從1開始)
forloop.revcounter0        # 當前循環的倒序索引值(從0開始)
forloop.first              # 當前循環是否是第一次循環(布爾值)
forloop.last               # 當前循環是否是最後一次循環(布爾值)
forloop.parentloop         # 本層循環的外層循環的對象,再經過上面的幾個屬性來顯示外層循環的計數等


<ul>
    {% for foo in name_list %}

        <li>{{ forloop.counter }} {{ foo }}</li>
    {% endfor %}

    {% for foo in name_list %}
        {{ forloop.counter0 }}
        <li>{{ foo }}</li>
    {% endfor %}
    
    {% for foo in name_list %}
    <li>{{ forloop.revcounter }} {{ foo }}</li>
 	{% endfor %}

	{% for foo in name_list %}
        <li>{{ forloop.first }} {{ foo }}</li>
	{% endfor %}
</ul>

for..empty..組合

若是遍歷的可迭代對象是空的或者就沒有這個對象,利用這個組合能夠提示用戶

<ul>        
    {% for foo in aaa %}
        <li>{{ foo }}</li>
    {% empty %}
        <li>查詢的內容啥也沒有</li>
    {% endfor %}

    {% for foo in value %}
        <li>{{ foo }}</li>
    {% empty %}
        <li>查詢的內容啥也沒有</li>
     {% endfor %}
</ul>

IF標籤

基本語法

{% ``if %}會對一個變量求值,若是它的值是「True」(存在、不爲空、且不是boolean類型的false值),對應的內容塊會輸出。

{% if 條件 %}
    結果  <!--不知足條件,不會生成這個標籤-->
{% elif 條件 %}
    結果
{% else %}  <!--也是在if標籤結構裏面的-->
    結果
{% endif %}

elif和else必定要在if endif裏面,設置多個elif或者沒有elif,有沒有else均可以

{% if dic.age > 18 %}
    <p>能夠乾點兒該作的事兒了~</p>
{% elif dic.age < 18 %}
    <p>小孩子,懂什麼</p>
{% else %}
    <p> 風華正茂的年齡~</p>
{% endif %}

條件也能夠與過濾功能配合

% if name_list|length > 4 %}
    <p>列表元素超過4個</p>
{% else %}
    <p>列表元素太少!</p>
{% endif %}

條件也能夠加邏輯運算符

if語句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判斷,注意條件兩邊都有空格。

with

使用一個簡單地名字緩存一個複雜的變量,多用於給一個複雜的變量起別名,當你須要一個昂貴的方法(好比訪問數據庫)不少次的時候是很是有用的,記住 ! 等號的左右不要加空格! !

{% with total=business.employees.count %}
    {{ total }} <!--只能在with語句體內用-->
{% endwith %}

{% with business.employees.count as total %}
    {{ total }}
{% endwith %}

forloop.first, forloop.last,forloop.parentloop

forloop.first

{% for foo in name_list %}
    {% if forloop.first %}
        {{ foo }}
    {% else %}
        <p>只有第一次循環打印</p>
    {% endif %}
{% endfor %}

forloop.parenloop: 必定注意! 他是返回本此循環的外層循環對象,這個對象能夠調用forloop的各類方法進行獲取相應的數據

測試此方法,咱們要在views函數加一個數據類型: lis = [['A', 'B'], ['C', 'D'], ['E', 'F']]

{% for i in lis %}
     {% for j in i %}
{#         <p>{{ forloop.parentloop }}</p>#}
        <p>{{ forloop.parentloop.counter }} {{ forloop.counter }} {{ j }}</p>
     {% endfor %}
{% endfor %}

注意事項

# 1. Django的模板語言不支持連續判斷,即不支持如下寫法:
{% if a > b > c %}
...
{% endif %}

# 2. Django的模板語言中屬性的優先級大於方法
def xx(request):
    d = {"a": 1, "b": 2, "c": 3, "items": "100"}
    return render(request, "xx.html", {"data": d})
# 如上,咱們在使用render方法渲染一個頁面的時候,傳的字典d有一個key是items而且還有默認的 d.items() 方法,此時在模板語言中:
{{ data.items }}
# 默認會取d的item key的值
csrf_token標籤

這個標籤是否是很是熟悉?以前咱們以post方式提交表單的時候,會報錯,還記得咱們在settings裏面的中間件配置裏面把一個csrf的防護機制給註銷了啊,自己不該該註銷的,而是應該學會怎麼使用它,而且不讓本身的操做被forbiden,經過這個標籤就能搞定。

當咱們加上此標籤以後,再次發出get請求,render返回給咱們的頁面中多了一個隱藏的input標籤,而且這個標籤裏面有個一鍵值對:

鍵:name,值:隨機的一堆密文。 那麼這個是幹什麼用的呢?其實他的流程是這樣的:

第一次發送get請求,在views視圖函數返回給你login.html頁面以前,render會將csrf_token標籤替換成一個隱藏的input標籤,此標籤的鍵值對就是上面那個鍵值對而且Django將這個鍵值對保存在內存;當你再次進行post請求時,他會驗證你的form表單裏面的隱藏的input標籤的鍵值對是否與我內存中存儲的鍵值對相同,若是相同,你是合法的提交,容許經過;若是不相同,則直接返回給你forbidden頁面。這就比如說,第一次get請求,他返回你一個蓋戳的文件,當你在進行post請求時他會驗證是否是那個蓋戳的文件。他的目的就是你提交post請求時,必須是從我給你的get請求返回的頁面提交的。爲何這麼作呢?是由於有人登陸你的頁面時是能夠繞過的get請求返回的頁面直接進行post請求登陸的,好比說爬蟲。直接經過requests.post('/login/')直接請求的,這樣是沒有csrftoken的,這樣就直接拒絕了。

說了這麼多,目的就是一個:驗證當你post提交請求時,是否是從我給你(你經過get請求的)頁面上提交的數據。

咱們能夠寫一個簡單爬蟲驗證如下

import requests

ret = requests.post('http://127.0.0.1:8000/login/',
                    data={'username': 'taibai', 'password': '123'}
                    )

print(ret.content)
# 若是你保留這這個驗證,則經過爬蟲是登陸不成功的,只能返回你一個forbidden的html頁面。

# 若是你將settings那個中間件註釋掉,那麼就能夠成功訪問了:
模板繼承

模版就是django提供的用於html發送瀏覽器以前,須要在某些標籤進行替換的系統,而繼承咱們立馬就會想到這是面向對象的三大特性之一。其實模版繼承就是擁有這兩個特性的模版的高級用法。

通常管理系統都是這樣的佈局,這個就是固定的導航條和側邊欄。不管我點擊側邊欄裏的那個按鈕,這兩部分不會更換隻會改變中間的內容。

那麼接下來咱們實現一個這樣的佈局。

咱們要準備4個html頁面:base.html、menu1.html、menu2.html、menu3.html,這四個頁面的導航條與左側側邊欄同樣,每一個頁面對應一個url。而且每一個頁面的左側側邊欄菜單1、菜單2、菜單三能夠實現跳轉:跳轉到menu1.html、menu2.html、menu3.html三個頁面。而頂端導航條只是樣式便可。接下來藉助於Django,咱們實現這四個頁面並對應urls能夠跑通流程。

urls

urlpatterns = [
    url(r'^base/', views.base),
    url(r'^menu1/', views.menu1),
    url(r'^menu2/', views.menu2),
    url(r'^menu3/', views.menu3),

]

views

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


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


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


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

html

base.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於咱們</a>
    <a href="">預定電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">
    首頁
</div>


</body>
</html>


menu1.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於咱們</a>
    <a href="">預定電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">
    菜單一首頁
</div>


</body>
</html>

menu2.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於咱們</a>
    <a href="">預定電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">
    菜單2首頁
</div>


</body>
</html>

menu3.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於咱們</a>
    <a href="">預定電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">
    菜單三首頁
</div>


</body>
</html>

base menu1 menu2 menu3

上面個人的需求雖然完成了可是有沒有什麼沒問題?你會發現html重複代碼太多了,若是領導不瞎,最晚後天你就能夠領盒飯了。

2. 母版繼承示例

因此針對與我上面的需求,很顯然你如今所擁有的知識點已經解決不了了。那麼接下來就是本節的重點:模版繼承。根據這個知識點名字的特色,咱們應該想到,咱們可不能夠創建一個父類,而後讓全部的子孫類都繼承個人父類,這樣我就能夠節省不少代碼了,讓個人代碼很是的清新、簡單。這裏的父類就不叫父類了他有一個專有名詞:母版。

接下來咱們先建立一個母版。(最好不要將base頁面直接做爲母版,母版就是隻設置公用的部分,其餘一律不要。)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於咱們</a>
    <a href="">預定電話</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜單一</a></li>
        <li><a href="/menu2/">菜單二</a></li>
        <li><a href="/menu3/">菜單三</a></li>
    </ul>
</div>

<div class="menu">

</div>
</body>
</html>

接下來,咱們將base menu1 menu2 menu3這四個頁面所有清空,而後在每一個頁面的最上面加上這麼一行代碼:接下來,咱們將base menu1 menu2 menu3這四個頁面所有清空,而後在每一個頁面的最上面加上這麼一行代碼:

{% extends 'master_edition.html' %}

這個就實現了我要繼承母版master_edition.html。

3. 自定製效果

如今已經完成了繼承母版,這個只是減小了重複代碼,尚未實現每一個頁面自定製的一些內容,若是你想要實現自定製的內容怎麼作?相似於模版系統你是否是應該在母版的具體位置作一個標識,而後在本身的頁面對應的地方進行自定製?那麼這個相似於%佔位符的特定的標識叫作鉤子。這幾個頁面只是在menu div不一樣,因此咱們就在這裏作一個鉤子就好了。

在母版的html對應位置:

<div class="menu">
    {% block content %}
    {% endblock %}
</div>

block endblock就是對應的鉤子,content是此鉤子的名字。

而後在base menu1 menu2 menu3的頁面上(此時就以base頁面舉例):

{% block content %}
     base頁面首頁
{% endblock %}

這樣你的代碼是否是很是的簡單了?

那麼咱們不只能夠在對應的html標籤設置鉤子,還能夠在css、js設定對應的鉤子。因此母版繼承中通常設定鉤子的地方就是三部分: html、css、js。

以css舉例:

咱們將base頁面的頂端導航條的背景顏色設置成紅色:

首先如今母版頁面對應的位置設置鉤子:

<style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }
        {% block nav %}
        {% endblock %}
    </style>

而後找到base頁面:

{% block nav %}
    .nav{
    background-color: red;
    }
{% endblock %}

這樣你的base頁面的導航條就變成紅色啦!

4. 保留母版內容並添加新特性

還有一個狀況咱們也會遇到,就是我既要留住母版的內容,又要在本身的html添加一些新的標籤。這個咱們在面向對象時是否是也遇到過?當時用什麼方法既執行父類方法又能夠執行子類方法?super!在這裏咱們也用super!

母版html:

<div class="menu">

    {% block content %}
        <div>這是母版測試頁面</div>
    {% endblock %}
</div>

base.html

{% block content %}
    {{ block.super }}
     base頁面首頁
{% endblock %}

在鉤子裏面加上{{ block.super }}便可

注意

  • 若是你在模版中使用 {% extends %} 標籤,它必須是模版中的第一個標籤。其餘的任何狀況下,模版繼承都將沒法工做,模板渲染的時候django都不知道你在幹啥。
  • 在base模版中設置越多的 {% block %} 標籤越好。請記住,子模版沒必要定義所有父模版中的blocks,因此,你能夠在大多數blocks中填充合理的默認內容,而後,只定義你須要的那一個。多一點鉤子總比少一點好。
  • 若是你發現你本身在大量的模版中複製內容,那可能意味着你應該把內容移動到父模版中的一個 {% block %} 中。
  • If you need to get the content of the block from the parent template, the {{ block.super }} variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using {{ block.super }} will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template. 將子頁面的內容和繼承的母版中block裏面的內容同時保留。
  • 不能在一個模版中定義多個相同名字的 block 標籤。
  • 結束一個鉤子時能夠標註此鉤子的名字,好比 {% endblock content%}這樣就能夠結束此鉤子不至於將其餘的block鉤子一併結束。
組件

組件就是將一組經常使用的功能封裝起來,保存在單獨的html文件中,(如導航條,頁尾信息等)其餘頁面須要此組功能時,按以下語法導入便可。 這個與繼承比較相似,可是‘格局’不一樣,繼承是須要寫一個大的母版,凡是繼承母版的一些html基本上用的都是母版頁面的佈局,只有一部分是本身頁面單獨展示的,這比如多以及排布好的多個組件。

{% include 'xx.html' %}

好比咱們寫一個nav導航條組件

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆兒堂</a>
    <a href="">局部護理</a>
    <a href="">關於咱們</a>
    <a href="">預定電話</a>
    <input type="text">搜索
</div>



</body>
</html>

而後咱們在建立流程去使用咱們的組件

urls:
url(r'^component/', views.component),


views:
def component(request):
    return render(request,'component.html')

component頁面:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
{% include 'nav.html' %}
<h1>你好,世界!</h1>
<div>我是componet頁面</div>


</body>
</html>

組件和插件的區別

# 組件是提供某一完整功能的模塊,如: 編輯器組件,QQ空間提供的關注組件等

# 而插件更傾向封閉某一個功能方法的函數

# 這二者的區別在JavaScript裏區別很小,組件這個名詞用的很少,通常統稱插件

Django之ORM操做

web開發的分工模式
  1. DBA(數據庫管理員)+應用層開發。

    通常中大型公司(或者數據量巨大、讀取數據的需求頻繁而且追求極致效率的公司)會有專門的DBA管理數據庫,編寫sql語句,對於應用層開發來講,不用寫sql語句,直接調用他寫的接口就行。因此在這種公司通常來講,開發人員應該'供'着DBA,由於你想寫入或者取出的數據須要依賴於DBA去執行,或者是你寫的比較複雜的sql語句須要讓DBA幫你看一下,效率行不行、是否是須要優化等等,這就須要看大家的交情或者其心情了。哈哈(開個玩笑)。

  2. 應用程序開發+sql語句編寫。

    這種狀況多存在於小公司,沒有專門設置DBA崗位,要求開發人員什麼都會一些,linux、數據庫、前端等等,這樣成本下降而且減小因爲部門之間的溝通帶來的損失,提升工做流程效率。

  3. 應用程序開發+ORM。

    這種模式sql不用你寫,你直接寫類、對象,應爲你對這些更加遊刃有餘。而後經過你寫的類、對象,等等經過相應的轉換關係直接轉化成對應的原生sql語句,這種轉化關係就是ORM:對象-關係-映射。你直接寫一個類就是建立一張表,你實例化一個對象就是增長一條數據,這樣以來,既能夠避開寫sql語句的麻煩,並且能夠提高咱們的開發效率。

Django的ORM簡介

MTV或者MVC框架中包括一個重要的部分,就是ORM,它實現了數據模型與數據庫的解耦,即數據模型的設計不須要依賴於特定的數據庫,經過簡單的配置就能夠輕鬆更換數據庫,這極大的減輕了開發人員的工做量,不須要面對因數據庫變動而致使的無效勞動.

ORM是「對象-關係-映射」的簡稱。(Object Relational Mapping,簡稱ORM)(未來會學一個sqlalchemy,是和他很像的,可是django的orm沒有獨立出來讓別人去使用,雖然功能比sqlalchemy更強大,可是別人用不了)

類對象--->sql--->pymysql--->mysql服務端--->磁盤,orm其實就是將類對象的語法翻譯成sql語句的一個引擎,明白orm是什麼了,剩下的就是怎麼使用orm,怎麼來寫類對象關係語句。

這樣開發效率確定是提高了,可是也有一點點缺陷就是經過ORM轉化成的sql語句雖然是準確的,可是不必定是最優的。

原生SQL和Python的ORM代碼對比
#sql中的表                                                      

 #建立表:
     CREATE TABLE employee(                                     
                id INT PRIMARY KEY auto_increment ,                   
                name VARCHAR (20),                                   
                gender BIT default 1,                                 
                birthday DATA ,                                       
                department VARCHAR (20),                             
                salary DECIMAL (8,2) unsigned,                       
              );


  #sql中的表紀錄                                                  

  #添加一條表紀錄:                                                     
      INSERT employee (name,gender,birthday,salary,department)       
             VALUES   ("alex",1,"1985-12-12",8000,"保潔部");            
  #查詢一條表紀錄:                                                    
      SELECT * FROM\\ employee WHERE age=24;                         

  #更新一條表紀錄:                                                     
      UPDATE employee SET birthday="1989-10-24" WHERE id=1;              
  #刪除一條表紀錄:                                                    
      DELETE FROM employee WHERE name="alex"                             

#python的類
class Employee(models.Model):
     id=models.AutoField(primary_key=True)
     name=models.CharField(max_length=32)
     gender=models.BooleanField()
     birthday=models.DateField()
     department=models.CharField(max_length=32)
     salary=models.DecimalField(max_digits=8,decimal_places=2)


 #python的類對象
      #添加一條表紀錄:
          emp=Employee(name="alex",gender=True,birthday="1985-12-12",epartment="保潔部")
          emp.save()
      #查詢一條表紀錄:
          Employee.objects.filter(age=24)
      #更新一條表紀錄:
          Employee.objects.filter(id=1).update(birthday="1989-10-24")
      #刪除一條表紀錄:
          Employee.objects.filter(name="alex").delete()

ORM單表操做

1 . 建立一個django項目

2 . 經過類建立數據表

從django.db 引入models模塊,建立表經過構建一個類去設定,數據庫中不區分大小寫,因此你的UserInfo在數據庫中直接編譯成了userinfo,此類必須繼承models.Model類,經過設定類的靜態屬性就會轉化成sql語句。

注意裝mysql須要遠程受權

grant all privileges on *.* to admin@"%" identified by 'ZHOUjian.21' with grant option;
flush privileges;

3 . 修改settings.py配置文件

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'cmdb',
        'USER': 'admin',
        'PASSWORD': 'ZHOUjian.20',
        'HOST': '121.36.43.223',
        'PORT': '3306',
    }
}

# 使用pymysql注意到__init.py加入如下兩行代碼
import pymysql
pymysql.install_as_MySQLdb()

注意

注意:NAME即數據庫的名字,在mysql鏈接前該數據庫必須已經建立,而上面的sqlite數據庫下的db.sqlite3則是項目自動建立 USER和PASSWORD分別是數據庫的用戶名和密碼。設置完後,再啓動咱們的Django項目前,咱們須要激活咱們的mysql。接下來,咱們要提早先給mysql建立cmdb的數據庫

4 . 經過類建立數據表

從django.db 引入models模塊,建立表經過構建一個類去設定,數據庫中不區分大小寫,因此你的UserInfo在數據庫中直接編譯成了userinfo,此類必須繼承models.Model類,經過設定類的靜態屬性就會轉化成sql語句。

from django.db import models
# Create your models here.


class UserInfo(models.Model):
    """
    下面幾個類的屬性經過ORM映射就對應成了:
    create table userinfo(
        id int primary key auto_increment,
        name varchar(16),
        age int,
        current_date date)
    """
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16)
    age = models.IntegerField()
    current_date = models.DateField()

5 . 在對應的數據庫中生成表結構

上面咱們已經經過類構建了一個表,可是尚未對應的生成真實的數據庫中的表結構,因此咱們要將上面的類生成真生的數據庫中的表結構。對應只有行代碼。

在terminal輸入指令:

python manage.py makemigrations

接下來咱們會發現migrations出現一個0001_initial.py的文件,這個文件是執行上述命令以後產生的腳本文件,這個文件就是一個記錄

這個指令其實就是執行第一個指令生成的記錄也就是那個腳本文件,而後就會在你對應的數據庫中生成一個真正的表,生成的表名字前面會自帶應用的名字,例如:你的userinfo表在數據表裏面叫作:cmdb_userinfo。

同步執行指令的原理

在執行 python manager.py makemigrations時
Django 會在相應的 app 的migrations文件夾下面生成 一個python腳本文件 
    
在執行 python manager.py migrate 時 Django纔會生成數據庫表,那麼Django是如何生成數據庫表的呢?

Django是根據 migrations下面的腳本文件來生成數據表的
    每一個migrations文件夾下面有多個腳本,那麼django是如何知道該執行那個文件的呢,django有一張django-migrations表,表中記錄了已經執行的腳本,那麼表中沒有的就是還沒執行的腳本,則 執行migrate的時候就只執行表中沒有記錄的那些腳本。
    有時在執行 migrate 的時候若是發現沒有生成相應的表,能夠看看在 django-migrations表中看看 腳本是否已經執行了,
    能夠刪除 django-migrations 表中的記錄 和 數據庫中相應的 表 , 而後從新 執行

Django的ORM系統體如今框架內就是模型層。想要理解模型層的概念,關鍵在於理解用Python代碼的方式來定義數據庫表的作法!一個Python的類,就是一個模型,表明數據庫中的一張數據表!Django奉行Python優先的原則,一切基於Python代碼的交流,徹底封裝SQL內部細節。

Example1

1.建立django程序:

# 終端命令:  django-admin startproject sitename
# IDE建立django程序本質上都是自動那個上述命令

# 其餘經常使用命令
python manage.py runserver 0.0.0.0
python manage.py startapp appname
python manage.py syncdb
python manage.py makemigrations
python manage.py migrate
python manage.py createuperuser

2.建立cmdbapp應用

python manage.py startapp cmdb

3.模板

TEMPLATE_DIRS = (
     'DIRS': [os.path.join(BASE_DIR,'templates')],
    )

4.靜態文件

STATICFILES_DIRS = (
	os.path.join(BASE_DIR,'static')
)

5. 修改數據庫引擎
settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'cmdb',
]

6.建立一個模型

一個模型(model)就是一個單獨的、肯定的數據的信息源,包含了數據的字段和操做方法。一般,每一個模型映射爲一張數據庫中的表。

基本的原則以下:

# 每一個模型在Django中的存在形式爲一個Python類
# 每一個模型都是django.db.models.Model的子類
# 模型的每一個字段(屬性)表明數據表的某一列
# Django將自動爲你生成數據庫訪問API

/Django_ORM_Demo/cmdb/models.py

from django.db import models
# Create your models here.


class UserInfo(models.Model):
    """
    下面幾個類的屬性經過ORM映射就對應成了:
    create table userinfo(
        id int primary key auto_increment,
        name varchar(16),
        age int,
        current_date date)
    """
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16)
    age = models.IntegerField()
    current_date = models.DateField()


python manage.py migrate
python manage.py makemigrations

注意

# 表名`myapp_person`由Django自動生成,默認格式爲「項目名稱+下劃線+小寫類名」,你能夠重寫這個規則。
# Django默認自動建立自增主鍵`id`,固然,你也能夠本身指定主鍵。
# 上面的SQL語句基於`PostgreSQL`語法。

一般,咱們會將模型編寫在其所屬app下的models.py文件中,沒有特別需求時,請堅持這個原則,不要本身給本身添加麻煩。

建立了模型以後,在使用它以前,你須要先在settings文件中的INSTALLED_APPS 處,註冊models.py文件所在的myapp。看清楚了,是註冊app,不是模型,也不是models.py。若是你之前寫過模型,可能已經作過這一步工做,可跳

ORM字段
<1> CharField
        字符串字段, 用於較短的字符串.
        CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所容許的最大字符數.
 
<2> IntegerField
       #用於保存一個整數.
 
<3> DecimalField
        一個浮點數. 必須 提供兩個參數:
         
        參數    描述
        max_digits    總位數(不包括小數點和符號)
        decimal_places    小數位數
                舉例來講, 要保存最大值爲 999 (小數點後保存2位),你要這樣定義字段:
                 
                models.DecimalField(..., max_digits=5, decimal_places=2)
                要保存最大值一百萬(小數點後保存10位)的話,你要這樣定義:
                 
                models.DecimalField(..., max_digits=17, decimal_places=10) #max_digits大於等於17就能存儲百萬以上的數了
                admin 用一個文本框(<input type="text">)表示該字段保存的數據.
 
<4> AutoField
        一個 IntegerField, 添加記錄時它會自動增加. 你一般不須要直接使用這個字段;
        自定義一個主鍵:my_id=models.AutoField(primary_key=True)
        若是你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.
 
<5> BooleanField
        A true/false field. admin 用 checkbox 來表示此類字段.
 
<6> TextField
        一個容量很大的文本字段.
        admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框).
 
<7> EmailField
        一個帶有檢查Email合法性的 CharField,不接受 maxlength 參數.
 
<8> DateField
        一個日期字段. 共有下列額外的可選參數:
        Argument    描述
        auto_now    當對象被保存時(更新或者添加都行),自動將該字段的值設置爲當前時間.一般用於表示 "last-modified" 時間戳.
        auto_now_add    當對象首次被建立時,自動將該字段的值設置爲當前時間.一般用於表示對象建立時間.
        (僅僅在admin中有意義...)
 
<9> DateTimeField
         一個日期時間字段. 相似 DateField 支持一樣的附加選項.
 
<10> ImageField
        相似 FileField, 不過要校驗上傳對象是不是一個合法圖片.#它有兩個可選參數:height_field和width_field,
        若是提供這兩個參數,則圖片將按提供的高度和寬度規格保存.    
<11> FileField
     一個文件上傳字段.
     要求一個必須有的參數: upload_to, 一個用於保存上載文件的本地文件系統路徑. 這個路徑必須包含 strftime #formatting,
     該格式將被上載文件的 date/time
     替換(so that uploaded files don't fill up the given directory).
     admin 用一個<input type="file">部件表示該字段保存的數據(一個文件上傳部件) .
 
     注意:在一個 model 中使用 FileField 或 ImageField 須要如下步驟:
            (1)在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件.
            (出於性能考慮,這些文件並不保存到數據庫.) 定義MEDIA_URL 做爲該目錄的公共 URL. 要確保該目錄對
             WEB服務器用戶賬號是可寫的.
            (2) 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django
             使用 MEDIA_ROOT 的哪一個子目錄保存上傳文件.你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT).
             出於習慣你必定很想使用 Django 提供的 get_<#fieldname>_url 函數.舉例來講,若是你的 ImageField
             叫做 mug_shot, 你就能夠在模板中以 {{ object.#get_mug_shot_url }} 這樣的方式獲得圖像的絕對路徑.
 
<12> URLField
      用於保存 URL. 若 verify_exists 參數爲 True (默認), 給定的 URL 會預先檢查是否存在( 即URL是否被有效裝入且
      沒有返回404響應).
      admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框)
 
<13> NullBooleanField
       相似 BooleanField, 不過容許 NULL 做爲其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項
       admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes" 和 "No" ) 來表示這種字段數據.
 
<14> SlugField
       "Slug" 是一個報紙術語. slug 是某個東西的小小標記(短籤), 只包含字母,數字,下劃線和連字符.#它們一般用於URLs
       若你使用 Django 開發版本,你能夠指定 maxlength. 若 maxlength 未指定, Django 會使用默認長度: 50.  #在
       之前的 Django 版本,沒有任何辦法改變50 這個長度.
       這暗示了 db_index=True.
       它接受一個額外的參數: prepopulate_from, which is a list of fields from which to auto-#populate
       the slug, via JavaScript,in the object's admin form: models.SlugField
       (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.
 
<13> XMLField
        一個校驗值是否爲合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema #的文件系統路徑.
 
<14> FilePathField
        可選項目爲某個特定目錄下的文件名. 支持三個特殊的參數, 其中第一個是必須提供的.
        參數    描述
        path    必需參數. 一個目錄的絕對文件系統路徑. FilePathField 據此獲得可選項目.
        Example: "/home/images".
        match    可選參數. 一個正則表達式, 做爲一個字符串, FilePathField 將使用它過濾文件名. 
        注意這個正則表達式只會應用到 base filename 而不是
        路徑全名. Example: "foo.*\.txt^", 將匹配文件 foo23.txt 卻不匹配 bar.txt 或 foo23.gif.
        recursive可選參數.要麼 True 要麼 False. 默認值是 False. 是否包括 path 下面的所有子目錄.
        這三個參數能夠同時使用.
        match 僅應用於 base filename, 而不是路徑全名. 那麼,這個例子:
        FilePathField(path="/home/images", match="foo.*", recursive=True)
        ...會匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif
 
<15> IPAddressField
        一個字符串形式的 IP 地址, (i.e. "24.124.1.30").
<16> CommaSeparatedIntegerField
        用於存放逗號分隔的整數值. 相似 CharField, 必需要有maxlength參數.
ORM參數
(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,每次更新數據記錄的時候會更新該字段,標識這條記錄最後一次的修改時間。
ORM字段與實際的Mysql字段對應關係
'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)',
Example2

1.建立一個應用app01

python manage.py startapp app01

# 記得在settings.py註冊app01

2.先到Django_ORM_Demo1/urls.py作一個分發路由

from django.conf.urls import url,include
urlpatterns = [
    url(r'^cmdb/', include("app01.urls")),
]

3.到Django_ORM_Demo1/app01/urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^login/',views.login),
    url(r'^orm/',views.orm),
]

4.經過orm建立module表

from django.db import models

from django.db import models

class UserInfo(models.Model):
    name = models.CharField(max_length=16)
    password = models.CharField(max_length=20)

python manage.py makemigrations
python manage.py migrate
ORM增刪改查入門應用

5.寫app01/view.py邏輯文件

from django.shortcuts import render,HttpResponse,redirect
from cmdb import models

def login(request):
    if request.method == "GET":
        return render(request, 'login.html')
    elif request.method == "POST":
        return render(request, 'login.html')
    else:
        return  redirect('/index/')

def orm(request):
    # 增長數據有三種
    # 建立1
    # models.UserInfo.objects.create(name='root',password='123')

    # 建立2
    # obj = models.UserInfo(name='tom',password='456')
    # obj.save()

    # 建立3
    # dic = {'username':'tom','password':'999'}
    # models.UserInfo.objects.create(**dic)

    # 查詢全部
    # result = models.UserInfo.objects.all()
    # for row in result:
    #     print(row.id,row.username,row.password)
    # print(result)
    # where(filter)查詢
    # result = models.UserInfo.objects.filter(username='root')
    # 若是在'root',後面加上password='123'等同於and條件
    # for row in result:
    #     print(row.id,row.username,row.password)
    # print(result)

    # 刪除id=10的行
    # models.UserInfo.objects.filter(id=10).delete()

    # 更新全部
    # models.UserInfo.objects.all().update(password=1314520)
    # return HttpResponse('orm')

    # 更新指定
    models.UserInfo.objects.filter(id=8).update(password='WuNai')
    return HttpResponse('orm')

6./Django_ORM_Demo1/templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="POST"  enctype="multipart/form-data">
        <p>
            <input type="text" name="user" placeholder="用戶名">
        </p>

        <p>
            <input type="password" name="pwd" placeholder="密碼" />
        </p>
        <p>
            男: <input type="radio" name="gender" value="1" />
            女: <input type="radio" name="gender" value="2" />
            張揚: <input type="radio" name="gender" value="3" />
        </p>
        <p>
            男: <input type="checkbox" name="favor" value="11" />
            女: <input type="checkbox" name="favor" value="22" />
            張揚: <input type="checkbox" name="favor" value="33" />
        </p>
        <p>
            <select name="area" multiple>
                <option  value="sh">上海</option>
                <option  value="bj">北京</option>
                <option  value="tj">天津</option>
                <option  value="gz">廣州</option>
            </select>
        </p>
        <p>
            <input type="file" name="files">
        </p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

7.運行django項目

python manage.py runserver

當你每次對模型進行增、刪、修改時,請務必執行命令python manage.py migrate,讓操做實際應用到數據庫上。這裏能夠選擇在執行migrate以前,先執行python manage.py makemigrations讓修改動做保存到記錄文件中,方便github等工具的使用。

ORM增刪改查中級應用

在python中orm的對應關係有三種:

    類 ----------> 表

    類的對象 ----------> 行(記錄)

    類的屬性 ----------> 表的字段(重點)

咱們要想操做一行的數據,就應該將相應的類引入,而後經過實例化對象增長數據。接下來,咱們從新建立一個數據庫爲orm01,建立一個student表結構,從這個表中去研究單表的操做

增: 添加表記錄

# 咱們先去module.py定義表結構
class Student(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16)
    age = models.IntegerField()

# 生成記錄並建立表結構
python manage.py makemigrations
python manage.py migrate

# 方式一
# 在index函數中操做:
def index(request):
    # 實例化一個對象這就是一條記錄
    student_obj = models.Student(
        name='王闊',
        age=18
    )
    # 將此記錄增長到數據表中
    student_obj.save()
    return render(request,'index.html')
    
# 方式二
# 咱們經過方式1給student表中添加了一條表記錄,可是上面那種方法不靈活,接下來咱們介紹一種更加靈活的方法。
models.Student.objects.create(
        name='ZHOU',
        age=20
    )
    
new_obj = models.Student.objects.create(
        name='ZHOU',
        age=19
    )
print(new_obj)  # Student object
print(new_obj.name)  # 志遠


# 批量建立
# 好比依次插入三十條數據
obj_list = [models.Student(name=f'幽夢{i}', age=20) for i in range(1, 31)]
models.Student.objects.bulk_create(obj_list)


# 更新和增長
# orm還有一種方法是對一條表記錄進行更新或者增長的操做,有則更新無則增長,相似於字典的這個操做dic['name']='barry'
models.Student.objects.update_or_create(
        name='喬妹兒1',  # 篩選條件
        defaults={  # 須要更新或者增長的值
            'age': 1000,
        }
)

# 簡單的查詢
# 1. all()所有取出
# 經過all從orm取出來的是一個QuerySet類型,這裏面有不少個Student類的對象也就是model對象,這個QuerySet相似於列表,可是不一樣與列表,在這裏咱們知道能夠循環遍歷取值便可.
all_objs = models.Student.objects.all()
    print(all_objs)  # QuerySet類型 這裏面有不少個Student類的對象也就是model對象。
    # 經過遍歷能夠獲取每一個對象的name屬性
    for i in all_objs:
        print(i.name)
        
# filter(條件)查詢
# 經過條件查詢獲取結果,返回的也是QuerySet類型,若是查詢不到內容不會報錯,返回一個空的QuerySet集合。
objs = models.Student.objects.filter(age=20)
print(objs)
objs = models.Student.objects.filter(id=2)
print(objs)  # <QuerySet [<Student: 傻強>]>
print(objs[0].id)  # 2 能夠經過索引取值

objs = models.Student.objects.filter(name='太白')
print(objs) 


# get(條件)查詢
# 這個get比較特殊,它返回的是model對象,經過get條件查詢,查詢的結果有且只有1個。
# 若是超過一個則報錯爲:get() returned more than one Student -- it returned 2(有幾個顯示幾個)!
# 若是沒有則報錯爲:Student matching query does not exist.

# obj = models.Student.objects.get(name='太白')  # 報錯:Student matching query does not exist.
# obj = models.Student.objects.get(age=20)  # get() returned more than one Student -- it returned 20!
obj = models.Student.objects.get(id=1)
print(obj)  # model對象
# 通常使用get條件查詢,前提一要肯定你的條件鎖定的是一行記錄.


# exclude排除
# 經過object對象或者QuerySet集合調用,返回QuserySet集合。
# 下面咱們演示了上面說到的鏈式點操做,能夠一直進行點的操做,由於此方法能夠QuerySet集合調用,而且返回的仍是QuerySet集合。
objs = models.Student.objects.exclude(id=1)
print(objs)

# 排除id爲1的行記錄,將剩下全部的返回
objs = models.Student.objects.filter(age=20).exclude(name='齊佳樂')
print(objs)  # <QuerySet [<Student: 喬妹兒1>, <Student: 喬妹兒1>, <Student: 健身哥>, <Student: 張雨薇>]>
objs = models.Student.objects.filter(age=20).exclude(name='齊佳樂').exclude(name='健身哥')
print(objs)  # <QuerySet [<Student: 喬妹兒1>, <Student: 喬妹兒1>,  <Student: 張雨薇>]>


# order_by排序
# 經過object對象或者QuerySet集合調用,返回QuserySet集合
# object對象調用
objs = models.Student.objects.order_by('age')  # 經過姓名升序排列
print(objs)

# queryset集合調用
objs = models.Student.objects.all().order_by('age')  # 經過姓名升序排列
print(objs)

# 經過年齡升序排列,相同年齡的按照id降序排列
objs = models.Student.objects.all().order_by('age', '-id')
print(objs)


# reverse反轉
# 經過order_by返回的QuerySet集合調用,返回一個QuerySet集合。
objs = models.Student.objects.reverse()
print(objs)  # 這樣沒有做用,all filter等都沒有做用
objs = models.Student.objects.order_by('id').reverse()  # 只能經過order_by返回的QuerySet集合調用
print(objs)


# count計數
# 經過QuerySet集合調用,返回一個元素個數
num = models.Student.objects.all().count()
print(num)  # 12
num = models.Student.objects.filter(age=20).count()
print(num)  # 5


# first返回第一個model對象
# 經過QuerySet集合調用,返回第一個model對象
obj = models.Student.objects.filter(age=20).first()
print(obj)


# last返回最後一個model對象
# 經過QuerySet集合調用,返回最後一個model對象
obj = models.Student.objects.filter(age=20).last()
print(obj)


# exists判斷是否存在
# 經過QuerySet集合調用,返回bool值
flag = models.Student.objects.filter(age=25).exists()
print(flag)  # False
# 這裏咱們要說一句,他的效率是高的,尤爲是做爲if判斷的條件。
# 雖說你能夠經過下面的方式判斷,可是若是這個集合一下取出上百萬條數據,那麼你的效率就很低的,
# 而exists翻譯成sql語句是limit=1,也就是說它只看第一個對象存不存在就返回bool值了。
if Queryset集合:
    pass
    
# 咱們也能夠經過配置settings,每次執行orm語句時都將相應的sql語句展現出來,這樣能夠查看一下exists().

# settings配置:  
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

# values_list
# 經過QuerySet集合調用,返回一個QuerySet集合
# 可是這個QuerySet集合比較特殊,這個裏面的元素不是model對象,而是元組的形式
query_tuple = models.Student.objects.filter(age=20).values_list()
print(query_tuple)
# <QuerySet [(4, '喬妹兒1', 20), (24, '喬妹兒1', 20), (27, '健身哥', 20), (28, '張雨薇', 20), (32, '齊佳樂', 20)]>
query_tuple = models.Student.objects.filter(age=20).values_list().exclude(name='齊佳樂')
print(query_tuple)
# < QuerySet[(4, '喬妹兒1', 20), (24, '喬妹兒1', 20), (27, '健身哥', 20), (28, '張雨薇', 20)] >
# 還能夠指定想獲取的字段
query_tuple = models.Student.objects.filter(age=20).values_list('name','age')
print(query_tuple)
# <QuerySet [('喬妹兒1', 20), ('喬妹兒1', 20), ('健身哥', 20), ('張雨薇', 20), ('齊佳樂', 20)]>


# values
# 經過QuerySet集合調用,返回一個QuerySet集合
# 可是這個QuerySet集合比較特殊,這個裏面的元素不是model對象,而是字典的形式
query_dict = models.Student.objects.filter(age=19).values()
print(query_dict)  # <QuerySet [{'id': 3, 'name': '志遠', 'age': 19}, {'id': 25, 'name': '董偉華', 'age': 19}]>
query_dict = models.Student.objects.filter(age=19).values('name', 'age')
print(query_dict)  # <QuerySet [{'name': '志遠', 'age': 19}, {'name': '董偉華', 'age': 19}]>


# distinct去重
# 經過QuerySet集合調用,返回一個QuerySet集合
# 這裏要注意一點:不管是all仍是filter 你對整個對象去重是沒有意義的,只要有一個字段不一樣,都不是重複的。
query_objs = models.Student.objects.filter(age=20).distinct()
print(query_objs)
# <QuerySet [<Student: id:4 name:喬妹兒1 age:20>, <Student: id:24 name:喬妹兒1 age:20>, <Student: id:27 name:健身哥 age:20>, <Student: id:28 name:張雨薇 age:20>, <Student: id:32 name:齊佳樂 age:20>]>
# 因此這個去重通常都用於values或者values_list
query_objs = models.Student.objects.filter(age=20).values('age').distinct()
print(query_objs) # <QuerySet [{'age': 20}]>




# 基於雙下劃線的模糊查詢
# 咱們在使用filter方法時,一直在使用 = 條件,可是沒有使用過> < >=等條件,這是由於ORM不支持這種寫法,不用着急,咱們能夠根據另外一種寫法去實現。
query_objs = models.Student.objects.filter(age__gt=19)  # 大於
query_objs = models.Student.objects.filter(age__gte=19)  # 大於等於
query_objs = models.Student.objects.filter(age__lt=20)  # 小於
query_objs = models.Student.objects.filter(age__lte=20)  # 小於等於
query_objs = models.Student.objects.filter(age__range=[18, 20])  # 範圍 左右都包含
query_objs = models.Student.objects.filter(name__contains='xiao')  # 針對字符串類型,內容含有
query_objs = models.Student.objects.filter(name__icontains='xiao')  # 針對字符串類型,內容含有 不區分大小寫
query_objs = models.Student.objects.filter(name__startswith='x')  # 匹配以x開頭
query_objs = models.Student.objects.filter(name__istartswith='x')  # 匹配以x開頭,不區分大小寫
query_objs = models.Student.objects.filter(name__endswith='o')  # 匹配以x結尾
query_objs = models.Student.objects.filter(name__iendswith='o')  # 匹配以x結尾,不區分大小寫



# 日期
# 日期這個字段查詢起來比較特殊,如今咱們要從新建立一個表。
class Birthday(models.Model):

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16)
    date = models.DateField()

    def __str__(self):
        return self.name

# 注意date字段,這個字段要求的是date類型,咱們若是存放的是datetime類型也是能夠的,只不過只是顯示年月日。 
# 接下來咱們給表中插入一些數據:
import datetime

    models.Birthday.objects.create(name='太白1', date=datetime.datetime.now())
    models.Birthday.objects.create(name='太白2', date='2000-04-25')
    models.Birthday.objects.create(name='太白3', date='2000-06-25')
    models.Birthday.objects.create(name='太白4', date='2000-08-26')
    models.Birthday.objects.create(name='太白5', date='2000-12-28')
    models.Birthday.objects.create(name='太白6', date='2001-03-26')
    models.Birthday.objects.create(name='太白7', date='2001-08-30')
    models.Birthday.objects.create(name='太白8', date='2003-01-13')
    models.Birthday.objects.create(name='太白9', date='2005-10-01')

# 查詢2000年出生的人,這樣寫就會報錯了
query_objs = models.Birthday.objects.filter(date='2000')
 print(query_objs)  
 # "'2000' value has an invalid date format. It must be in YYYY-MM-DD format."]
 
 
 # 查詢2000年出生的人
query_objs = models.Birthday.objects.filter(date__year='2000')
print(query_objs)  # <QuerySet [<Birthday: 太白2>, <Birthday: 太白3>, <Birthday: 太白4>, <Birthday: 太白5>]>


# 查詢2000年4月出生的人
query_objs = models.Birthday.objects.filter(date__year='2000',date__month='04')
print(query_objs)  # <QuerySet [<Birthday: 太白2>]>


# 查詢2000年大於4月小於12月出生的人
query_objs = models.Birthday.objects.filter(date__year='2000', date__month__gt='04',date__month__lt='12')
print(query_objs)  # <QuerySet [<Birthday: 太白3>, <Birthday: 太白4>]>



# 刪: 刪除行記錄
# 從對象性質來區分刪除有兩種

# 1. 調用model對象刪除
# 這樣就是刪除單條記錄
models.Student.objects.get(id=20).delete()


# 2. 調用QuerySet集合刪除
# 這樣就是批量刪除記錄,知足條件記錄都會被刪除
models.Student.objects.filter(age=20).delete()

b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
# 要注意的是: delete() 方法是 QuerySet 上的方法,但並不適用於 Manager 自己。這是一種保護機制,
# 是爲了不意外地調用 Entry.objects.delete() 方法致使 全部的 記錄被誤刪除。若是你確認要刪除全部的對象,那麼你必須顯式地調用:
Entry.objects.all().delete()

# 若是不想級聯刪除,能夠設置爲:
pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)


# 改: 更新行記錄
# 更新記錄的方法只有一個就是update,此方法只能用於QuerySet集合,model對象是不能夠使用的。也就是說update能夠進行批量更新的操做,而且他會返回一個更新的行記錄數量的返回值。

# 我先增長了一條name同樣的行記錄 
'''
# models.Student.objects.create(
       name='喬妹兒1',
       age=100    )
'''
count = models.Student.objects.filter(name='喬妹兒1').update(age=20)
print(count)  # 2 更新了2條
ORM增刪改查進階操做
# 獲取個數
#
# models.Tb1.objects.filter(name='seven').count()

# 大於,小於
# models.Tb1.objects.filter(id__gt=1)              # 獲取id大於1的值
# models.Tb1.objects.filter(id__gte=1)              # 獲取id大於等於1的值
# models.Tb1.objects.filter(id__lt=10)             # 獲取id小於10的值
# models.Tb1.objects.filter(id__lte=10)             # 獲取id小於10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 獲取id大於1 且 小於10的值

        # in
# models.Tb1.objects.filter(id__in=[11, 22, 33])   # 獲取id等於十一、2二、33的數據
# models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
# isnull
# Entry.objects.filter(pub_date__isnull=True)

# contains
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
# models.Tb1.objects.exclude(name__icontains="ven")

# range
# models.Tb1.objects.filter(id__range=[1, 2])   # 範圍bettwen and

# 其餘相似
# startswith,istartswith, endswith, iendswith,
# order by
#
# models.Tb1.objects.filter(name='seven').order_by('id')    # asc
# models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

# group by
#
# from django.db.models import Count, Min, Max, Sum
# models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

# limit 、offset
# models.Tb1.objects.all()[10:20]

# regex正則匹配,iregex 不區分大小寫
# Entry.objects.get(title__regex=r'^(An?|The) +')
# Entry.objects.get(title__iregex=r'^(an?|the) +')

# date
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

# year
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005)

# month
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6)

# day
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3)

# week_day
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2)

# hour
#
# Event.objects.filter(timestamp__hour=23)
# Event.objects.filter(time__hour=5)
# Event.objects.filter(timestamp__hour__gte=12)

# minute
# Event.objects.filter(timestamp__minute=29)
# Event.objects.filter(time__minute=46)
# Event.objects.filter(timestamp__minute__gte=29)

# second
# Event.objects.filter(timestamp__second=31)
# Event.objects.filter(time__second=2)
# Event.objects.filter(timestamp__second__gte=31)

其餘操做

# extra
#
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])


# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1)

# Q
# 方式一:
# Q(nid__gt=10)
# Q(nid=8) | Q(nid__gt=10)
# Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
# 方式二:
# con = Q()
# q1 = Q()
# q1.connector = 'OR'
# q1.children.append(('id', 1))
# q1.children.append(('id', 10))
# q1.children.append(('id', 9))
# q2 = Q()
    # q2.connector = 'OR'
    # q2.children.append(('c1', 1))
    # q2.children.append(('c1', 10))
# q2.children.append(('c1', 9))
# con.add(q1, 'AND')
    # con.add(q2, 'AND')

# models.Tb1.objects.filter(con)


# 執行原生SQL
# from django.db import connection, connections
# cursor = connection.cursor()  # cursor = connections['default'].cursor()
# cursor.execute("""SELECT * from auth_user where id = %s""", [1])
# row = cursor.fetchone()

建立一個django項目,建立app,配置static,加入apps,註釋csrf,配置數據庫鏈接
1. Django_Orm_table_Demo1/urls.py

from django.conf.urls import url,include
urlpatterns = [
    url(r'^cmdb/', include("cmdb.urls")),
]

2. Django_Orm_table_Demo1/cmdb/urls.py

from django.conf.urls import url,include
from cmdb import views
urlpatterns = [
    url(r'^business',views.business),
]

3.Django_Orm_table_Demo1/cmdb/models.py

from django.db import models
class Business(models.Model):
    caption = models.CharField(max_length=32)
    code = models.CharField(max_length=32,default='PASS')

class Host(models.Model):
    nid = models.AutoField(primary_key=True)
    hostname = models.CharField(max_length=32,db_index=True)
    ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
    port = models.IntegerField()
    b = models.ForeignKey(to="Business", to_field='id',on_delete=models.CASCADE,)

4. Django_Orm_table_Demo1/cmdb/views.py

from django.shortcuts import render,HttpResponse,redirect
from cmdb import models

def business(request):
    v1 = models.Business.objects.all()
    v2 = models.Business.objects.all().values('id','caption')
    v3 = models.Business.objects.all().values_list('id','caption')
    return render(request, 'business.html', {'v1': v1,'v2': v2, 'v3': v3})

def host(request):
    v4 = models.Host.objects.filter(nid__gt=0)
    for row in v4:
        print(row.nid,row.hostname,row.ip,row.port,row.b_id,row.b.caption,row.b.code,sep="\t")
        # print(row.b.fk.name)
    # return HttpResponse('host')
    return render(request,'host.html',{'v4':v4})

# 外鍵經過`.`進行跨表
# `__`也能跨表

5.Django_Orm_table_Demo1/templates/business.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>業務線列表(對象)</h1>
    <ul>
        {% for row in v1 %}
            <li>{{ row.id }} - {{ row.caption }} - {{ row.code }}</li>
        {% endfor %}
    </ul>
    <h1>業務線列表(字典)</h1>
    <ul>
        {% for row in v2 %}
            <li>{{ row.id }} - {{ row.caption }}</li>
        {% endfor %}
    </ul>
    <h1>業務線列表(元組)</h1>
    <ul>
        {% for row in v3 %}
            <li>{{ row.0 }} - {{ row.1 }}</li>
        {% endfor %}
    </ul>
</body>
</html>

5.Django_Orm_table_Demo1/templates/host.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>業務線列表(對象)</h1>
<table border="">
    <thead>
    <tr>
        <th>主機名</th>
        <th>IP</th>
        <th>端口</th>
        <th>業務線名稱</th>
    </tr>
    </thead>
<tbody>
{% for row in v4 %}
    <tr bid="{{ row.nid }}" bid="{{ row.b_id }}">
        <td>{{ row.hostname }}</td>
        <td>{{ row.ip }}</td>
        <td>{{ row.port }}</td>
        <td>{{ row.b.caption }}</td>
    </tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
一對多數據實例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .hide{
            display: none;
        }
        .shade{
            position: fixed;
            top: 0;
            right: 0;
            left: 0;
            bottom: 0;
            background: black;
            opacity: 0.6;
            z-index: 100;
        }
        .add_model{
            position: fixed;
            height: 300px;
            width: 500px;
            top: 100px;
            left: 50%;
            z-index: 111;
            border: 1px solid red;
            background-color: blanchedalmond;
            margin-left: -270px;
        }
    </style>

</head>
<body>
<h1>業務線列表(對象)</h1>
<div>
    <input id="add_host" type="button" value="添加" />
</div>
<table border="1">
    <thead>
    <tr>
        <th>主機名</th>
        <th>IP</th>
        <th>端口</th>
        <th>業務線名稱</th>
        <th>序號</th>
    </tr>
    </thead>
<tbody>
{% for row in v4 %}
    <tr bid="{{ row.nid }}",bid="{{ row.b_id }}">
        <td>{{ forloop.counter }}</td>
        <td>{{ row.hostname }}</td>
        <td>{{ row.ip }}</td>
        <td>{{ row.port }}</td>
        <td>{{ row.b.caption }}</td>
    </tr>
{% endfor %}
</tbody>
</table>
<div class="shade hide"></div>
<div class="add_mode hide ">
    <div class="group">
        <input type="text" placeholder="主機名" name="hostname" />
    </div>

    <div class="group">
        <input type="text" placeholder="IP" name="ip" />
    </div>

    <div class="group">
        <input type="text" placeholder="端口" name="port" />
    </div>

    <div class="group">
        <select>業務線一</select>
        <select>業務線二</select>
        <select>業務線三</select>
        <select>業務線四</select>
    </div>

    <input type="submit" value="提交" />
    <input type="submit" value="取消" />
</div>

<script src="/static/jquery-1.12.4.js"></script>
<script>
    $(function () {
        $('#add_host').click(function () {
            $(".shade,.add_model").removeClass('hide');
        })
    })
</script>
</body>
</html>

Django多表操做

建立模型

表和表之間的關係

    一對1、多對1、多對多 ,用book表和publish表本身來想一想關係,想一想裏面的操做,加外鍵約束和不加外鍵約束的區別,一對一的外鍵約束是在一對多的約束上加上惟一約束。

  實例:咱們來假定下面這些概念,字段和關係

  做者模型:一個做者有姓名和年齡。

  做者詳細模型:把做者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。做者詳情模型和做者模型之間是一對一的關係(one-to-one)

  出版商模型:出版商有名稱,所在城市以及email。

  書籍模型: 書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many);一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many)。

from django.db import models


class Author(models.Model): #比較經常使用的信息放到這個表裏面
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()

    # 與AuthorDetail創建一對一的關係,一對一的這個關係字段寫在兩個表的任意一個表裏面均可以
    authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE)
    #就是foreignkey+unique,只不過不須要咱們本身來寫參數了,而且orm會自動幫你給這個字段名字拼上一個_id,數據庫中字段名稱爲authorDetail_id

class AuthorDetail(models.Model):#不經常使用的放到這個表裏面

    nid = models.AutoField(primary_key=True)
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField( max_length=64)

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField()

# 多對多的表關係,mysql的時候是怎麼創建的,是否是手動建立一個第三張表,而後寫上兩個字段,每一個字段外鍵關聯到另外兩張多對多關係的表,orm的manytomany自動幫咱們建立第三張表,兩種方式創建關係均可以,之後的學習咱們暫時用orm自動建立的第三張表,由於手動建立的第三張表咱們進行orm操做的時候,不少關於多對多關係的表之間的orm語句方法沒法使用
#若是你想刪除某張表,你只須要將這個表註銷掉,而後執行那兩個數據庫同步指令就能夠了,自動就刪除了。
class Book(models.Model):

    nid = models.AutoField(primary_key=True)
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)

    # 與Publish創建一對多的關係,外鍵字段創建在多的一方,字段publish若是是外鍵字段,那麼它自動是int類型
    publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #foreignkey裏面能夠加不少的參數,都是須要我們學習的,慢慢來,to指向表,to_field指向你關聯的字段,不寫這個,
    # 默認會自動關聯主鍵字段,on_delete級聯刪除字段名稱不須要寫成publish_id,orm在翻譯foreignkey的時候會自動給你這個字段拼上一個_id,
    # 這個字段名稱在數據庫裏面就自動變成了publish_id
    # 與Author表創建多對多的關係,ManyToManyField能夠建在兩個模型中的任意一個,自動建立第三張表,
    # 而且注意一點,你查看book表的時候,你看不到這個字段,由於這個字段就是建立第三張表的意思,不是建立字段的意思,
    # 因此只能說這個book類裏面有authors這個字段屬性
    authors=models.ManyToManyField(to='Author',)
    # 注意無論是一對多仍是多對多,寫to這個參數的時候,最後後面的值是個字符串,否則你就須要將你要關聯的那個表放到這個表的上面

class UserInfo(models.Model):
    name = models.CharField(max_length=16)
    password = models.CharField(max_length=20)

class Student(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16)
    age = models.IntegerField()
多對多表的三種建立方式

方式一: 自行建立第三張表

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="書名")


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="做者姓名")


# 本身建立第三張表,分別經過外鍵關聯書和做者
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")

    class Meta:
        unique_together = ("author", "book")

方式二: 經過ManyToManyField自動

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="書名")


# 經過ORM自帶的ManyToManyField自動建立第三張表
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="做者姓名")
    books = models.ManyToManyField(to="Book", related_name="authors")  #自動生成的第三張表咱們是沒有辦法添加其餘字段的

方式三: 設置ManyTomanyField並指定自行建立的第三張表(稱爲中介模型)

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="書名")


# 本身建立第三張表,並經過ManyToManyField指定關聯
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="做者姓名")
    books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
    # through_fields接受一個2元組('field1','field2'):
    # 其中field1是定義ManyToManyField的模型外鍵的名(author),field2是關聯目標模型(book)的外鍵名。


class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")
    #能夠擴展其餘的字段了
    class Meta:
        unique_together = ("author", "book")

注意

# 當咱們須要在第三張關係表中存儲額外的字段時,就要使用第三種方式,第三種方式仍是能夠使用多對多關聯關係操做的接口(all、add、clear等等)

# 當咱們使用第一種方式建立多對多關聯關係時,就沒法使用orm提供的set、add、remove、clear方法來管理多對多的關係了。

一對一相關字段

to
    設置要關聯的表。

to_field
    設置要關聯的字段。
    
on_delete
    同ForeignKey字段。

建立一對一關係字段時的一些參數

一對多相關字段

to
    設置要關聯的表

to_field
    設置要關聯的表的字段

related_name
    反向操做時,使用的字段名,用於代替原反向查詢時的'表名_set'。
related_query_name
    反向查詢操做時,使用的鏈接前綴,用於替換表名。

on_delete
    當刪除關聯表中的數據時,當前表與其關聯的行的行爲。

建立一對多關係字段時的一些參數

多對多相關字段

多對多的參數:
    to
        設置要關聯的表

    related_name
        同ForeignKey字段。

    related_query_name
        同ForeignKey字段。
    through
        在使用ManyToManyField字段時,Django將自動生成一張表來管理多對多的關聯關係。

        但咱們也能夠手動建立第三張表來管理多對多關係,此時就須要經過        
    through來指定第三張表的表名。

    through_fields
        設置關聯的字段。

    db_table
        默認建立第三張表時,數據庫中表的名稱。            

建立多對多字段時的一些參數

元信息

元信息
    ORM對應的類裏面包含另外一個Meta類,而Meta類封裝了一些數據庫的信息。主要字段以下:
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")
    class Meta:
        unique_together = ("author", "book")

db_table
    ORM在數據庫中的表名默認是 app_類名,能夠經過db_table能夠重寫表名。db_table = 'book_model'

index_together
    聯合索引。

unique_together
    聯合惟一索引。

ordering
    指定默認按什麼字段排序。
    ordering = ['pub_date',]
    只有設置了該屬性,咱們查詢到的結果才能夠被reverse(),不然是能對排序了的結果進行反轉(order_by()方法排序過的數據)

建立表時的一些元信息設置

獲取元信息,能夠經過model對象._meta.verbose_name等獲取本身經過verbose_name指定的表名,model對象._meta.model_name獲取小寫的表名,還有model對象.app_label能夠獲取這個對象的app應用名等等操做。例如:book_obj = models.Book.objects.get(id=1),book_obj._meta.model_name。

關於db_column和verbose_name

1.指定字段名: 在定義字段的時候,增長參數db_column=’real_field’; 

2.指定表名: 在model的class中,添加Meta類,在Meta類中指定表名db_table 
# 例如在某個models.py文件中,有一個類叫Info:


class Info(models.Model):  
    ''''' 
            信息統計 
    '''  
    app_id = models.ForeignKey(App)  
    app_name = models.CharField(verbose_name='應用名',  max_length=32, db_column='app_name2')  
  
    class Meta:  
        db_table = 'info'  
        verbose_name = '信息統計'  
        verbose_name_plural = '信息統計'

# 其中db_column指定了對應的字段名,db_table指定了對應的代表; 

# 若是不這樣指定,字段名默認爲app_name, 而代表默認爲app名+類名: [app_name]_info.

# verbose_name指定在admin管理界面中顯示中文;verbose_name表示單數形式的顯示,verbose_name_plural表示複數形式的顯示;中文的單數和複數通常不做區別。

# 建立完這個表,咱們本身能夠經過navicat工具來看看數據庫裏面的那些表,出版社這個表裏面沒有任何的關係字段,
# 這種單表的數據,咱們能夠先添加幾條數據,在進行下面的增刪改查的操做。

# 生成表以下:

注意事項

# 表的名稱myapp_modelName,是根據 模型中的元數據自動生成的,也能夠覆寫爲別的名稱  
# id 字段是自動添加的
# 對於外鍵字段,Django 會在字段名上添加"_id" 來建立數據庫中的列名
# 這個例子中的CREATE TABLE SQL 語句使用PostgreSQL 語法格式,要注意的是Django 會根據settings 中指定的數據庫類型來使用相應的SQL 語句。
# 定義好模型以後,你須要告訴Django _使用_這些模型。你要作的就是修改配置文件中的INSTALL_APPSZ中設置,在其中添加models.py所在應用的名稱。
# 外鍵字段 ForeignKey 有一個 null=True 的設置(它容許外鍵接受空值 NULL),你能夠賦給它空值 None 。
# 我們的表裏麪包含了一對1、一對多、多對多的關係,咱們基於這幾個表來練習,未來不管有多少張表,都逃脫不了這三個關係,操做起來都是同樣的。

on_delete

on_delete
當刪除關聯表中的數據時,當前表與其關聯的行的行爲。

models.CASCADE
刪除關聯數據,與之關聯也刪除


models.DO_NOTHING
刪除關聯數據,引起錯誤IntegrityError


models.PROTECT
刪除關聯數據,引起錯誤ProtectedError


models.SET_NULL
刪除關聯數據,與之關聯的值設置爲null(前提FK字段須要設置爲可空)


models.SET_DEFAULT
刪除關聯數據,與之關聯的值設置爲默認值(前提FK字段須要設置默認值)


models.SET

刪除關聯數據,
a. 與之關聯的值設置爲指定值,設置:models.SET(值)
b. 與之關聯的值設置爲可執行對象的返回值,設置:models.SET(可執行對象)


# ForeignKey的db_contraint參數

關係和約束你們要搞清楚,我不加外鍵能不能表示兩個表之間的關係啊,固然能夠

可是咱們就不能使用ORM外鍵相關的方法了,因此咱們單純的將外鍵換成一個其餘字段類型,只是單純的存着另一個關聯表的主鍵值是不能使用ORM外鍵方法的。

#db_constraint=False只加二者的關係,沒有強制約束的效果,而且ORM外鍵相關的接口(方法)還能使用,因此若是未來公司讓你創建外鍵,而且不能有強制的約束關係,那麼就能夠將這個參數改成False
    customer = models.ForeignKey(verbose_name='關聯客戶', to='Customer',db_constraint=False)
添加表記錄

操做前先簡單的錄入一些數據:仍是create和save兩個方法,和單表的區別就是看看怎麼添加關聯字段的數據

一對多

相關文章
相關標籤/搜索