今日內容概要

1 Django視圖2 Django的路由系統,URL3 Django模板4 Django的ORM操作:Django的ORM語句與前面學的SQL語句有較大的區別,在Django中是通過點屬性獲取數據。

如常規SQL查詢:seletc * from tb where id > 1

Django中的查詢:models.tb.object.filter(id__gt=1) # 注意代碼中的雙下劃線 models.tb.object.filter(id=1) models.tb.object.filter(id_lt=1)

Django基礎簡要回顧

1 Django請求生命周期 請求先到達URL對應關係(匹配) -> 視圖函數 -> 返回字元串給用戶 請求先到達URL對應關係(匹配) -> 視圖函數 -> 打開一個HTML文件,讀取內容2 創建django project命令

django-admin startproject mysite # mysite是工程名稱
cd mysite
python manage.py startapp appname # 創建app
..

mysite
mysite
- 配置文件
- urls.py
- settings.py
cd mysite
python manage.py startapp cmdb

mysite
mysite
- 配置文件
- urls.py
- settings.py
cmdb
- views.py
- admin.py
- models.py # 創建資料庫

3 配置

模板路徑

靜態文件路徑 注釋CSRF行4 編寫程序

1urls.pyURL與函數的對應關係
如:/index/ -> func
2 views.py,處理用戶各種請求的函數
def func(request):
# 包含所有的請求數據
...
return HttpResponse(字元串)
return render(request, index.html, {模板渲染})
return redirect(URL)
3)模板語言,針對render()方法的第三個參數而做
假設render方法的的第三個參數的值是列表,可通過for循環獲取,也可通過索引獲取。
return render(request, index.html, {li: [11,22,33]})
{% for item in li %}
<h1>{{ item }}</h1>
{% endfor %}
通過索引取數據:索引用點取
<h2>{{ item.0 }}</h2>

一 Django基礎內容回顧

1 創建項目文件,配置相關文件的路徑首先用pycharm創建Django項目文件夾,項目文件夾名稱:michael_01File -> New Project -> 左邊選擇Django,右邊設置名稱、解釋器 -> 點擊Create接下來創建app,在pycharm的Terminal下創建app,在michael_01目錄執行命令:python manage.py startapp app01 # 創建app01

緊接著在michael_01項目文件夾下創建靜態文件夾:static

第一步:打開文件:michael_01michael_01settings.py注釋掉CSRF行。第二步:在michael_01michael_01settings.py文件中添加templates文件夾的路徑,默認已添加DIRS: [os.path.join(BASE_DIR, templates)],第三步:在michael_01michael_01settings.py文件末尾添加靜態文件路徑

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

第四步:在app01views.py文件寫函數index,代碼如下:

from django.shortcuts import HttpResponse
# 參數request可以是其它名字,只是一個形參
def index(request):
return HttpResponse("INDEX")

第五步:在在michael_01michael_01urls.py文件添加URL與函數index的對應關係,代碼修改如下:

from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path(admin/, admin.site.urls),
path(index/, views.index),
]

此時運行michael_01項目文件,在瀏覽器地址欄輸入:127.0.0.1:8000/index/,頁面上顯示:INDEX。

熟悉了上面的操作步驟後,接下來寫一個登錄頁面。

2 寫一個登錄頁面

回顧數據提交方式,常用有GET和POST方式。GET:是獲取數據,比如在地址欄輸入URL,是獲取數據。POST:是提交數據,比如頁面有表單時,需要提交到後台。此外的提交方式還有:PUT,DELETE, HEAD, OPTION等,大概有11種。第一步:新建login.html文件,路徑是:michael_01 emplateslogin.html,代碼如下:

<body>
<form action="/login/" method="post">
<p>
<input type="text" name="user" placeholder="用戶名">
</p>
<p>
<input type="password" name="pwd" placeholder="密碼">
</p>
<input type="submit" value="提交">
</form>
</body>

第二步:在app目錄新建login函數處理登錄請求。

文件是:michael_01app01views.py,在該文件中寫下面函數的代碼:

# 處理登錄請求
def login(request):
return render(request,"login.html")

第三步:在michael_01michael_01urls.py文件中添加URL與函數的映射關係:

urlpatterns = [
path(admin/, admin.site.urls),
path(index/, views.index),
path(login/, views.login), # 添加這行代碼
]

完成上面三步,現在運行michael_01,在瀏覽器地址欄輸入 127.0.0.1:8000/login/ 就可以看到簡單的登錄頁面。

上面實現的登錄頁面,但是在login函數中沒有對用戶提交的數據進行處理,以及沒有獲取用戶提交的方式,改寫login函數的代碼,獲取較詳細的信息:

文件:michael_01app01views.py

def login(request):
if request.method == "GET":
return render(request,"login.html")
elif request.method == "POST":
# 獲取用戶名和密碼
u = request.POST.get("user")
p = request.POST.get("pwd")
# 驗證用戶和密碼
if u == "michael" and p == "123":
return redirect(/index/)
else:
return render(request, login.html)
else:
# 處理不是GET和POST方式提交
return redirect("/index/")

3 獲取用戶提交的表單內容及上傳的文件內容

在上面的HTML代碼中添加單選、多選、複選、上傳文件等操作,試下在後台是怎樣獲取的用戶提交內容。當在地址欄直接輸入網址時是GET提交方式,當點擊提交按鈕時按POST提交方式。在login函數中分別獲取用戶點擊提交後的表單內容,通過print方法輸出的方式驗證是否成功獲取到表單的內容。當要處理上傳的文件時,在HTML中的form標籤中要添加enctype="multipart/form-data"屬性,該屬性在於表示上傳的文件只認為上傳的字元串。後台通過obj=request.FILES.get(fname)方法獲取到文件,其中fname是form標籤中的<input type="file" name="fname">中的name屬性值,要真正讀取到文件的內容,還需要調用obj的chunks()方法,該方法返回的是一個生成器,需要使用for循環讀取。此時可以對上傳的文件做統一管理,可以在項目文件夾michael_01下新建uploadfile來放置上傳的文件。詳細代碼如下所示:michael_01 emplateslogin.html文件的代碼如下:

<body>
<!--在form標籤中加 enctype="multipart/form-data" 屬性時,上傳的文件只認為是上傳的字元串,
帶著這屬性時,前端的input標籤放到POST中發到後台,文件就放FILES中發到後台。這樣FILES就只能獲取
到文件,POST就只能獲取POST的基本數據。-->
<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>
<!--添加單選框,後台獲取方式:request.POST.get("gender")-->
男:<input type="radio" name="gender" value="男">
女:<input type="radio" name="gender" value="女">
</p>
<p>
<!--添加多選框,後台獲取方式:request.POST.getlist("favor")-->
Python:<input type="checkbox" name="favor" value="Python">
Linux:<input type="checkbox" name="favor" value="Linux">
HTML:<input type="checkbox" name="favor" value="HTML">
</p>
<p>
<!--添加select選擇框,要實現多選,添加multiple屬性-->
<!--單選時,後台獲取方式:request.POST.get("city")-->
<!--多選時,後台獲取方式:request.POST.getlist("city")-->
<select name="city" multiple>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="cd">成都</option>
</select>
</p>
<p>
<!--測試上傳文件,提交後後台獲取到的內容-->
<!--後台獲取方式:request.POST.get("fname"),這樣獲取到的是文件名稱-->
<input type="file" name="fname">
</p>
<input type="submit" value="提交">
</form>
</body>

michael_01app01views.py文件中的login函數代碼如下:

from django.shortcuts import render,redirect
from django.shortcuts import HttpResponse
def login(request):
if request.method == "GET":
return render(request,"login.html")
elif request.method == "POST":
# raidio,測試HTML代碼中的單選、多選、複選後提交到後台時的獲取方法
gender = request.POST.get("gender")
favor = request.POST.getlist(favor)
city = request.POST.getlist(city)
fname = request.POST.get("fname") # 這樣獲取到的只是文件名,不是文件內容
# 當在HTML代碼中的form標籤添加屬性 enctype="multipart/form-data" 時,把文件當成字元
# 串來處理,此時fname的輸出值是:None
# 經驗證,後台獲取方式是正確的
print("gender:",gender)
print("favor:",favor)
print("city:",city)
print("filename:", fname)
# 當form標籤不添加屬性 enctype="multipart/form-data" 時,下面代碼的obj變數值是:None
# 當添加了 enctype="multipart/form-data" 時,下面代碼的obj變數是文件名,但實際不僅是文件
# 名,它是 uploadedfile.TemporaryUploadedFile 類型,是在類中定義了__str__方法或者
# _repr__方法才會輸出這樣的結果,obj.name才是真正的文件名。
obj = request.FILES.get(fname)
print(obj, type(obj), obj.name)

# 要真正獲取用戶上傳文件,需要調用chunks()方法,這個方法返回的是生成器。在調用這個方法前需要
# 先打開指定要保存的文件
import os
fils_path = os.path.join(uploadfile, obj.name) # 拼接文件路徑
f = open(fils_path, mode="wb")
for i in obj.chunks():
f.write(i)
f.close()
from django.core.files.uploadedfile import InMemoryUploadedFile
return render(request, login.html)
else:
# 處理不是GET和POST方式提交
return redirect("/index/")

二 Django視圖

1 獲取用戶請求數據通過上面的代碼中可以了解到,request目前已經有3個方法,分別是:

request.GET:當以GET方式提交時獲取數據的方法

request.POST:當以POST方式提交時獲取數據的方法request.FILES:上傳文件時用到的方法。2 獲取checkbox等多選的內容獲取request.POST.getlist()3 上傳文件內容獲取,form標籤要做特殊設置

obj = request.FILES.get(fname)
obj.name
f = open(obj.name, mode=wb)
for item in obj.chunks():
f.write(item)
f.close()

4 FBV 和 CBV

FBV(function base view)CBV(class base view)urls.py:URL與函數名的對應關係。例如:index -> 函數名

views.py:具體要實現的函數。

def 函數(request): ...CBV是URL對應類,下面的home.html、views.py模塊中的Home類代碼說明了該用法:michael_01 emplateshome.html代碼如下:

<body>
<form action="/home/" method="post">
<input type="text" name="user" />
<input type="submit" />
</form>
</body>

michael_01app01views.py模塊中添加Home類,代碼如下:

from django.views import View
class Home(View):
"""當請求是GET時就調用get方法,請求是POST時調用post方法。
此外,還有其它方法是:put, patch, delete, head, options, trace
"""
def get(self, request):
print(request.method)
return render(request, home.html)
def post(self, request):
print(request.method)
return render(request, home.html)

michael_01michael_01urls.py模塊中的urlpatterns列表中增加下面這一行代碼:

path(home/, views.Home.as_view()),此時運行michael_01,在地址欄輸入URL:127.0.0.1:8000/home/時,後台獲取到的是GET請求,當點擊頁面上的提交按鈕時,後台獲取到的是POST請求。根據後台的輸出可以判斷。此外,也可以在頁面點擊右鍵->檢查 -> Network時,重新在地址欄輸入URL,可以看到Headers是的Request Method是GET;當點擊提交後,Headers的Request Method是POST。

在Home中能夠知道用戶發來的是什麼請求,是因為該類繼承了View類,在View類中有一個dispatch方法實現的這個功能,還可以對這個方法進行改寫,可以添加需要的功能在裡面,有時就做到了裝飾器功能。例如修改後的代碼如下:

michael_01app01views.py模塊中Home類修改代碼:

from django.views import View
class Home(View):
"""當請求是GET時就調用get方法,請求是POST時調用post方法。
此外,還有其它方法是:put, patch, delete, head, options, trace
"""
"""在這個class中是怎麼知道以哪種方式提交的呢?是通過View類中的dispatch方法實現,根據用戶端發送
過來的數據提取Request Method的值可判斷出是以哪種方式提交的。當對這個方法進行重寫後可以實現
需要的功能。"""
def dispatch(self, request, *args, **kwargs):
#return HttpResponse(OK) # 直接return時,其它的get和post方法不會執行
# 調用父類中的dispatch方法,當要實現裝飾器功能時,可以把print方法改為需要的裝飾器
print("before")
result = super(Home, self).dispatch(request, *args, **kwargs)
print("after")
return result

def get(self, request):
print(request.method)
return render(request, home.html)

def post(self, request):
print(request.method)
return render(request, home.html)

三 Django的模板語言:處理字典

在上一篇的Django基礎中,Django的HTML模板語言可以處理列表、字元串等特殊內容。在模板語言中還可以處理字典。當render方法的第三個參數傳遞的是字典時,在模板語言中可以對字典進行處理。為了說明字典的處理情況,先在michael_01michael_01urls.py文件中的urlpatterns變數中添加下面這行代碼:path(index/, views.index),接下來在:michael_01app01views.py中增加下面的代碼,下面代碼中有一個字典參數USER_DICT:

USER_DICT = {
k1: root1,
k2: root2,
k3: root3,
k4: root4,
}
def index(request):
return render(request, index.html, {"user_dict": USER_DICT})

michael_01 emplatesindex.html的代碼如下所示:

<body>
<!--測試在views模塊中傳遞字典參數給render方法,有哪些獲取方式-->
{{ user_dict.k1 }} <!--可以通過字典的鍵獲取-->
<ul>
<!--字典有keys方法,但是後面沒有括弧,這樣獲取的是字典的鍵-->
{% for k in user_dict.keys %}
<li>{{ k }}</li>
{% endfor %}
</ul>
<ul>
<!--字典有values方法,但是後面沒有括弧,這樣獲取的是字典的值-->
{% for k in user_dict.values %}
<li>{{ k }}</li>
{% endfor %}
</ul>
<ul>
<!--字典有items方法,但是後面沒有括弧,可以同時獲取字典的鍵和值-->
{% for k,v in user_dict.items %}
<li>{{ k }}-{{ v }}</li>
{% endfor %}
</ul>
</body>

在上面的views.py文件中,render方法的第三個參數傳遞的是字典USER_DICT,此時在HTML中可以通過字典的keys、values、items方法分別獲取字典的鍵、值、鍵和值,注意這些方法後面沒有逗號。當然也可以直接通過字典的點屬性獲取,比如 user_dict.k1。

四 Django的路由系統,URL

改寫前面的index.html代碼,在頁面上顯示用戶名,當點擊用戶名的時候,就是在查看這個用戶名的詳細信息。michael_01 emplatesindex.html的修改後代碼如下所示:

<body>
<ul>
<!--字典有items方法,但是後面沒有括弧,可以同時獲取字典的鍵和值-->
{% for k,v in user_dict.items %}
<li><a target="_blank" href="/detail/?nid={{ k }}">{{ v.name }}</a></li>
{% endfor %}
</ul>
</body>

在這段代碼中循環字典user_dict,同時獲取字典的鍵和值,由於值還是字典,且有name屬性,所以通過v.name獲取其值,並且放在a標籤內,給a標籤的href屬性構造URL,使其在點擊某個用戶的時候,就跳轉到某個用戶的詳細頁面,例如點擊第5個用戶時的URL是:127.0.0.1:8000/detail/?。詳細頁面由detail.html文件顯示。此時的detail.html文件內容如下。

michael_01 emplatesdetail.html代碼如下:

<body>
<h3>詳細信息</h3>
<h5>用戶名:{{ detail_info.name }}</h5>
<h5>郵箱:{{ detail_info.email }}</h5>
</body>

michael_01app01views.py模塊新增的內容如下所示:

USER_DICT = {
1: {name: root1, email: [email protected]},
2: {name: root2, email: [email protected]},
3: {name: root3, email: [email protected]},
4: {name: root4, email: [email protected]},
5: {name: root5, email: [email protected]},
}
def index(request):
return render(request, index.html, {"user_dict": USER_DICT})
def detail(request):
nid = request.GET.get(nid)
detail_info = USER_DICT[nid]
return render(request, detail.html, {detail_info: detail_info})

由於用戶發送過來的請求是通過輸入URL請求的,所以是GET請求。在detail函數中獲取用戶的請求nid號,根據nid號獲取字典的值,並將其傳遞給render的第三個參數,同時替換掉detail.html文件的指定變數,從而達到顯示用戶詳細信息的目的。此時的urls.py文件中的urlpatterns變數內容如下:

michael_01michael_01urls.py文件中的urlpatterns變數內容:

urlpatterns = [
path(admin/, admin.site.urls),
path(index/, views.index),
path(login/, views.login),
path(home/, views.Home.as_view()),
path(detail/, views.detail),
]

在上面這個查看詳細信息的實例中,當點擊某個用戶的時候URL是通過 ?nid=1 的方式構造的,這裡構造URL還可用正則的方式進行構造,比如點擊第一個用戶的時候讓URL顯示為:127.0.0.1:8000/detail-1,這樣的情況在打開網站時經常會看到。要做到這樣的效果,需要改寫幾個文件的代碼。

第一步:改寫michael_01michael_01urls.py文件的urlpatterns代碼:

from django.conf.urls import url
urlpatterns = [
path(admin/, admin.site.urls),
path(index/, views.index),
path(login/, views.login),
path(home/, views.Home.as_view()),
#path(detail/, views.detail),
url(^detail-(d+).html, views.detail),
]

這裡要注意,在URL中要寫正則表達式,需要使用 django.conf.urls 模塊中的url才能正確匹配。

第二步:改寫michael_01 emplatesindex.html中的代碼:

<body>
<ul>
{% for k,v in user_dict.items %}
<li><a target="_blank" href="/detail-{{ k }}.html">{{ v.name }}</a></li>
{% endfor %}
</ul>
</body>

第三步:修改michael_01app01views.py模塊中的detail函數代碼如下:

def detail(request, nid):
# return HttpResponse(nid) # 測試是否獲取到uid號
detail_info = USER_DICT[nid]
return render(request, detail.html, {detail_info: detail_info})

經過上面3個步驟,不需要修改detail.html中的代碼,此時運行michael_01,在頁面上點擊一個用戶名,URL就變成了 127.0.0.1:8000/detail-5 這樣的樣式,並且能正常顯示用戶名對應的詳細信息。

1 URL對應函數或類

經過對上面的代碼實例的了解,可以知道一個URL可以對應一個函數和一個類,例如:path(login/, views.login),path(home/, views.Home.as_view()),URL對應類的時候,要在類名後面使用 as_view() 方法2 多個URL對應一個函數,或者一類URL對應一個函數還可以在URL中使用正則方法,使多個URL對應一個函數,例如:url(^detail-(d+).html, views.detail),3 更多的了解一類URL對應一個函數的情況當在URL中使用多個正則表達式的情況,此時傳遞的參數個數就會不一樣,例如URL改為這樣的方式:url(^detail-(d+)-(d+).html, views.detail),上面的URL中有兩個正則的分組,這時detail函數也需要接收這2個參數才行:

def detail(request, nid, uid):
print(nid, uid)
return HttpResponse("ok")

這樣在運行michael_01後,地址欄輸入:127.0.0.1:8000/detail-3,後台會得到輸出 3 9。這裡的detail函數的nid和uid參數交換的話,得到的輸出也是一樣,但要根據該參數做相應的其它處理就顯得不那麼方便。此時可以把URL中的正則表達式做下改動,代碼如下所示:

url(^detail-(?P<nid>d+)-(?P<uid>d+).html, views.detail),這樣修改後,detail函數中的uid和nid參數位置不管怎樣變換,都會得到各自相應的值。但是在detail函數中需要有相應的參數來接收才行,在detail函數中的參數還可以這樣寫:*args, **kwargs,這樣不管有多少個參數都可以接收到。此時的detail函數可以修改為下面這樣的方式:

def detail(request, *args, **kwargs):
detail_info = USER_DICT[kwargs[nid]]
return render(request, detail.html, {detail_info: detail_info})

在實際應用中,URL傳遞參數常用下面幾種方式:

第一種方式:url(^detail-(d+)-(d+).html, views.detail),對應的detail函數是:

def detail(request, nid, uid):
pass
def detail(request, *args):
args = (2, 9)
def detail(request, *args, **kwargs):
args = (2, 9)

第二種方式,在正則表達式中分組,以關鍵字參數傳遞:

url(^detail-(?P<nid>d+)-(?P<uid>d+).html, views.detail),

def detail(request, nid, uid):
pass
def detail(request, **kwargs):
kwargs = {nid: 2, uid: 5}
def detail(request, *args, **kwargs):
args = (2, 9)

第三種方式:在url中使用第三個參數name

當在URL中使用第三個參數時,HTML中的跳轉連接可以做些靈活變動。HTML中的跳轉可以根據URL中的name參數的值獲取相應的URL,例如下面這個URL:path(indexpython/, views.index, name="michael"),在這個URL中第三個參數name的值是michael,在HTML中需要跳轉URL的地方做相應的改變,就可以跳轉到相應的URL中,現在假設index.html的代碼如下所示:

<body>
<form action="{% url michael %}" method="post">
<p><input type="text" name="user" placeholder="用戶名"></p>
<p><input type="text" name="email" placeholder="郵箱"></p>
<input type="submit" value="提交" />
</form>
<ul>
{% for k,v in user_dict.items %}
<li><a target="_blank" href="/detail-{{ k }}.html">{{ v.name }}</a></li>
{% endfor %}
</ul>
</body>

在這段index.html的代碼中,form標籤的action屬性的值是 {% url michael %} ,其中的michael就是URL中的第三個參數name的值,此時對應的URL是:indexpython/,完整的URL就是:127.0.0.1:8000/indexpyt

在上面這種根據名稱找URL的方式,當URL中有使用正則表達式的情況,代碼會有些不一樣。首先修改URL中的代碼:由:path(indexpython/(d+)/, views.index, name="michael"),修改為:url(^indexpython/(d+)/, views.index, name="michael"),其次修改index.html的代碼中的form標籤:由:<form action="{% url michael %}" method="post">修改為:<form action="{{ request.path_info }}" method="post">還要修改views.py模塊中的index函數,讓該函數接收URL中傳遞的參數,該函數中的request.path_info就是用於獲取完整URL的,由於在render中傳遞了request參數,所在index.html中可以使用 {{ request.path_info }} 這種方式獲取完整的URL。

def index(request, nid):
print(request.path_info) # 查看 request.path_info的返回信息
return render(request, index.html, {"user_dict": USER_DICT})

4 URL中的name屬性

name屬性是對URL路由關係進行命名,以後可以根據此名稱生成自己想要的URL。例如有下面幾種URL方式:path(indexpython/, views.index, name="michael"),url(^indexpython/(d+)/, views.index, name="michael"),在模板語言中獲取URL的方式:對於第一種URL獲取方式:{% url "michael" %}對於第二種URL獲取方式:{% url "michael" 3 %},後面的數字3可根據自己想跳轉到哪裡就寫什麼。對於第二種URL獲取方式還可以這樣用:{{ request.path_info }},獲取的是當前的URL。除了上面這幾種獲取URL方式外,在views.py模塊中的index函數中也可以構造URL。index函數的代碼如下所示:

def index(request, nid):
# print(request.path_info) # 獲取request.path_info的信息
# /indexpython/1/的方式設置URL,還可以通過reverse方法生成URL
from django.urls import reverse
v = reverse(michael, args=(123,)) #
print(v)
return render(request, index.html, {"user_dict": USER_DICT})

注意代碼中的reverse方法,第一個參數是url中的name參數值,第二個參數是是一個元組,元組的第一個參數是URL中需要匹配的個數。如果URL中有多個匹配時,這裡要傳相應個數的參數才行,index函數也要接收相應個數的參數。

此時的 url(^indexpython/(d+)/, views.index, name="michael"),是這種方式時,在地址欄輸入127.0.0.1:8000/indexpyt,後台的print(v)也是/indexpython/123/,這樣構造URL比字元串拼接方便些。當URL變為下面這種方式時:url(^indexpython/(?P<nid>d+)/(?P<uid>d+)/, views.index, name="michael"),index函數就要以字典方式設置,例如下面這樣:

def index(request, nid, uid):
# print(request.path_info) # 獲取request.path_info的信息
# /indexpython/1/的方式設置URL,還可以通過reverse方法生成URL
from django.urls import reverse
v = reverse(michael, kwargs={nid: 2, uid: "30"}) #
print(v)
return render(request, index.html, {"user_dict": USER_DICT})

運行代碼後,在地址欄輸入:127.0.0.1:8000/indexpyt,後台的print(v)同樣可以輸出:/indexpython/2/30/,說明構造URL正常。此時index.hmtl中的form標籤也可以做相應的修改:

<form action="{% url michael nid=1 uid=20 %}" method="post">在地址欄輸入127.0.0.1:8000/indexpyt可以正常訪問,此時右鍵點擊「審查」時,看到的action源代碼是:/indexpython/1/20/。

5 URL小結

假設有下面三種URL:url(^python/, views.index, name="i1"),url(^linux/(d+)/(d+)/, views.index, name="i2"),url(^javascript/(?P<nid>d+)/(?P<uid>d+)/, views.index, name="i3"),對應使用函數獲取URL方式如下:

def index(request, *args, **kwargs):
from django.urls import reverse
url1 = reverse(i1) # 生成的url是:python/
url2 = reverse(i2, args=(1,2,)) # 生成的URL是:linux/1/2/
url3 = reverse(i3, kwargs={nid:1, uid: 9}) # 生成的URL是javascript/1/9/

對應的模板語言中中xxx.hmtl中生成URL的方式:

{% url "i1" %} # 獲取的URL是:python/
{% url "i2" 1 2 %} # 獲取的URL是:linux/1/2/
{% url "i3" nid=1 uid=5 %} # 獲取的URL是:javascript/1/5/

6 第5種URL形式

當一個請求到達的時候,首先會去找到項目文件下對應的url路徑,在這裡該文件是:michael_01michael_01urls.py,Django可以將這個文件中的url設置為指向app目錄下的URL,這樣有多個app存在的時候,項目文件夾下的urls.py文件就要更便於管理。需要在項目文件夾下的urls.py文件中導入include方法才行。首先修改項目文件夾下的URL代碼如下:michael_01michael_01urls.py

from django.urls import path,include
urlpatterns = [
path(app01/, include("app01.urls")),
]

app01目錄下的urls.py文件代碼如下:michael_01app01urls.py

from django.conf.urls import url
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path(admin/, admin.site.urls),
url(^indexpython/(?P<nid>d+)/(?P<uid>d+)/, views.index, name="michael"),
path(login/, views.login),
path(home/, views.Home.as_view()),
url(^detail-(?P<nid>d+)-(?P<uid>d+).html, views.detail),
]

URL經過這樣的修改後,訪問的時候,需要在URL前指定app01才能正常訪問,如:127.0.0.1:8000/app01/lo,訪問登錄頁面時,需要在login/前面加app01/才可以正常訪問。這樣在項目文件夾下的urls.py文件中的:

path(app01/, include("app01.urls")),第一個參數app01/可以取其它想要名稱,第二個參數的include表示要指向哪個文件的url,這裡指向的是app01目錄下的urls.py文件,urls就是指的urls.py文件。7 URL還有默認值8 URL的命名空間參考:cnblogs.com/wupeiqi/art

五 Django的ORM操作

ORM:關係對象映射Django的ORM操作,首先要創建類。1 根據類自動創建資料庫表在app目錄下的models.py,該文件名不能變。在該文件寫入下面的代碼:第一步先寫類:文件:michael_01app01models.py

from django.db import models
# 自定義的類必須要繼承models.Model,默認創建的表名是:app01_userinfo
class UserInfo(models.Model):
# 執行下面的命名,Django會自動創建一張表,自動創建id列,並且是自增,還是主鍵
# 用戶名列,字元串類型,指定長度
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)

第二步註冊app:修改settings.py文件下的INSTALLED_APPS變數,在後面添加app01目錄,修改如下:

INSTALLED_APPS = [
django.contrib.admin,
django.contrib.auth,
django.contrib.contenttypes,
django.contrib.sessions,
django.contrib.messages,
django.contrib.staticfiles,
app01, # 添加這一行,注意後面的逗號不能少
]

第三步執行命令:進入項目文件夾的終端模式(Terminal),執行下面的命令:

python manage.py makemigrations成功執行命令後,會創建:app01migrations 001_initial.py文件,該文件記錄資料庫的每次操作。接著再在終端下執行下面這條命令:python manage.py migrate成功執行命令就會在項目文件夾michael_01目錄下的db.sqlite3文件中創建對應表和欄位信息。默認創建的表名是:app01_userinfoDjango默認使用的資料庫引擎是SQLite3,這個默認數據是可以修改的。默認使用的資料庫代碼在michael_01目錄下的settings.py文件的DATABASES變數指定的。如下所示:

DATABASES = {
default: {
ENGINE: django.db.backends.sqlite3,
NAME: os.path.join(BASE_DIR, db.sqlite3),
}
}

在該段代碼的上面還有資料庫相關設置的文檔連接,連接如下:

docs.djangoproject.com/如果要修改為使用mysql資料庫,這個DATABASES修改為下面這樣:

DATABASES = {
default: {
ENGINE: django.db.backends.mysql, # 指定資料庫引擎
NAME: dbname, # 指定資料庫,Django不能自動創建資料庫
USER: root, # 用戶名
PASSWORD: xxx, # 密碼
HOST: xx, # 主機
PORT: xx, # 埠
}
}

注意:使用Django連接mysql時,Django默認使用的是mysqldb模塊,由於該模塊在python3中不能使用,在python3中使用的是PyMySQL。這裡需要做一些修改,在michael_01michael_01\__init__.py文件中添加下面的代碼:

import pymysqlpymysql.install_as_MySQLdb()

2 根據類對資料庫表中的數據進行各種操作

DjangoORM的基本增刪改查:可視化軟體Navicat可以連接SQLite資料庫(指定資料庫路徑和名字即可連接),連接上後就可以看到db.sqlite3文件下有很多的表格。(1) DjangoORM在資料庫中增加記錄現在自己創建的數據中還沒有數據,為了方便統一管理,創建一個orm函數來專做這件事。首先在app01目錄下的urls.py文件添加一個URL,如下所示:

urlpatterns = [
path(admin/, admin.site.urls),
url(^indexpython/(?P<nid>d+)/(?P<uid>d+)/, views.index, name="michael"),
path(login/, views.login),
path(home/, views.Home.as_view()),
url(^detail-(?P<nid>d+)-(?P<uid>d+).html, views.detail),
path(orm/, views.orm), # 添加這一行
]

接著在app01目錄下的views.py模塊中寫入orm函數,

文件:michael_01app01views.py文件中的orm函數代碼:

# 要操作資料庫,是通過models.py文件的類來操作,所以在使用前要先導入
from app01 import models
def orm(request):
# 使用models中UserInfo類操作資料庫
# UserInfo類有兩個變數username和password,所以create方法傳兩個參數
models.UserInfo.objects.createusername=root,password=123)
return HttpResponse(Python)

寫完這段orm函數的代碼後,在瀏覽器地址欄輸入:127.0.0.1:8000/app01/or,就會在資料庫創建一條username是root,password是123的記錄。也可以不用create()方法創建記錄,例如下面代碼所示:

第二種創建記錄的方法:

from app01 import models
def orm(request):
# 第二種創建方法,不使用create方法,直接使用類
obj = models.UserInfo(username=michael,password=123)
obj.save() # 需要調用save()方法才能創建
return HttpResponse(Python)

在瀏覽器地址欄輸入:127.0.0.1:8000/app01/or,就成功的創建了第二條記錄。還可以傳遞字典參數給create()方法創建記錄,也是第三種創建記錄方法:

第三種創建記錄的方法:

from app01 import models
def orm(request):
# 第三種創建記錄方法,傳遞字典參數給create()方法
dic = {username: tom, password:123456}
models.UserInfo.objects.create(**dic) # 要注意在參數前加兩個*號
return HttpResponse(Python)

在瀏覽器地址欄輸入:127.0.0.1:8000/app01/or,就成功的創建了第三條記錄。用Navicat可以看到增加的記錄。

(2) DjangoORM在資料庫中查找記錄

第一種查找數據的方法:使用objects.all()方法,該方法返回的結果是一個列表,列表是QuerySet類型,這個類型是Django提供。列表元素是object類型。例如下面代碼查詢結果所示:

from app01 import models
def orm(request):
# 查找數據
result = models.UserInfo.objects.all()
print(result) # result是Django提供的QuerySet類型,可理解為是一個列表。
return HttpResponse(Python)

在瀏覽器地址欄輸入:127.0.0.1:8000/app01/or,後台輸出結果如下:

<QuerySet [<UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>, <UserInfo: UserInfo object (3)>]>

這個輸出結果的每一個元素都是類UserInfo類的對象,在創建該UserInfo類時,有username和password欄位,再加上默認的id欄位,那麼這結果中的每個對象此時都有3個欄位,分別是(id, username, password)。可以用循環方法獲取每個元素的各個欄位值。如下面代碼所示:

from app01 import models
def orm(request):
# 查找數據
result = models.UserInfo.objects.all()
# 遍歷查詢結果的每個元素
for row in result:
print(row.id, row.username, row.password, sep=,)
return HttpResponse(Python)

在瀏覽器地址欄輸入:127.0.0.1:8000/app01/or,後台輸出結果如下:

1,root,1232,michael,1233,tom,123456要在頁面上顯示這些查找的內容,只需要模板裡面循環就可以做到。在查詢的時候還可以指定查詢條件,需要調用filter()方法,相當於SQL語言中where條件。例如要查詢username為michael的用戶,查詢代碼可這樣寫:result = models.UserInfo.objects.filter(username=michael)查詢結果仍然是QuerySet類型的列表結果。完整查詢代碼如下所示:

from app01 import models
def orm(request):
result = models.UserInfo.objects.filter(username=michael)
print(result)
# 遍歷查詢結果的每個元素
for row in result:
print(row.id, row.username, row.password)
return HttpResponse(Python)

在瀏覽器地址欄輸入:127.0.0.1:8000/app01/or,後台輸出結果如下:

<QuerySet [<UserInfo: UserInfo object (2)>]>2 michael 123filter()方法可以同時指定多個條件,條件之間是關係,例如同時查找username為michael,密碼是123的記錄:result = models.UserInfo.objects.filter(username=michael, password=123)(3) DjangoORM在資料庫中刪除記錄可根據查詢條件,刪除指定的記錄,也可以使用all()方法查找後刪除所有的記錄,例如要刪除所有的記錄,可使用下面的方法:models.UserInfo.objects.all().delete()要刪除username是tom的記錄,可使用下面方法:models.UserInfo.objects.filter(username=tom).delete()(4) DjangoORM在資料庫中更新記錄根據查找條件查找,對找到的結果進行更新使用update()方法,例如要更新所有用戶的password值為1234567:models.UserInfo.objects.all().update(password="1234567")還可以用filter()方法過濾條件進行更新,例如要更新id為2的password值,可使用下面方法:models.UserInfo.objects.filter(id=2).update(password="66666")3 基於ORM驗證用戶登錄根據用戶提交的用戶名和密碼驗證登錄。後台獲取用戶輸入的用戶名和密碼去查詢數據。直接使用filter()方法過濾查詢對象,返回的是一個QuerySet類型,可以在filter()方法後面接first()方法取查詢結果的第一個,或者接count()方法計算查詢結果。在實際使用中,如果使用count()方法得到的是查詢的個數,想要獲取該用戶的其它信息的話,還是要用first()方法。查詢到信息後,還應可以對信息的修改、刪除等操作,此外還要實現點擊增加按鈕能夠添加記錄的功能。4 小結Django的ORM操作可以修改表結構,也就是修改models.py文件中的代碼後,執行下面兩條例命令就可以修改表結構:python manage.py makemigrationspython manage.py migrate例如把models.py文件中的password欄位的值改小,由password = models.CharField(max_length=64)修改為password = models.CharField(max_length=60),此時執行上面兩條命令後,表中原來的記錄對應該欄位的值要是超過了60,則超過的部分就會丟失。 如果要在表中增加欄位的話,例如增加email欄位:email = models.CharField(max_length=60),此時執行python manage.py makemigrations命令時,由於在增加email欄位時沒有指定默認值,所以在輸出中就有提示是否要指定默認值,提示信息如下所示:

1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py

此時選擇第1個,輸入"michael",那麼表中原來的記錄中新增的email欄位的值都是michael。接著執行命令:python manage.py migrate也就會成功。如果要增加的欄位指定null=True參數,則表示可以為空,例如:

gender = models.CharField(max_length=60, null=True),這時執行makemigrations和migrate也就會成功。如果要刪除某一欄位,可以其在models.py中對應行的代碼注釋掉後重新執行makemigrations和migrate兩個命令就可完成操作。另外,在資料庫中使用最多的類型有:字元串、數字、時間、二進位等,前面用的是CharField類型是字元串,此外字元串類型還有:URLField、EmailField、GenericIPAddressField(IPv4、IPv6)等,這些類型都是字元串類型,看上去沒有什麼區別,但是在後面使用Django的管理員用戶的時候,有了這些類型,在添加記錄的時候Django會自動去判斷相應的類型。在Django中的所有類型有:AutoField, BLANK_CHOICE_DASH, BigAutoField, BigIntegerField, BinaryField, BooleanField, CharField, CommaSeparatedIntegerField, DateField, DateTimeField, DecimalField, DurationField, EmailField, Empty, Field, FieldDoesNotExist, FilePathField, FloatField, GenericIPAddressField, IPAddressField, IntegerField, NOT_PROVIDED, NullBooleanField, PositiveIntegerField, PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField, TimeField, URLField, UUIDField。在這些類型中,AutoField是自增的類型,如果定義了這個欄位,Django默認不會自動自增,例如下面這樣:

class UserGroup(models.Model):
uid = models.AutoField(primary_key=True) # 利用AutoField設置自增列
caption = models.CharField(max_length=32)

在UserGroup類(或表)中由於聲明了AutoField類型的自增列,所以Django不會默認自增。在使用AutoField類型時,後面必須加參數primary_key=True才生效,這點要特別注意。

Django欄位的大類有:字元串類型、數字、時間、二進位,其中有一個自增AutoField欄位(primary_key=True5 欄位的參數

DjangoORM的欄位參數大致有下面這些:

null 資料庫中欄位是否可以為空
db_column 資料庫中欄位的列名,如db_column="cp"指定列名為cp
db_tablespace
default 資料庫中欄位的默認值
primary_key 資料庫中欄位是否為主鍵
db_index 資料庫中欄位是否可以建立索引,如db_index=True可建立索引
unique 資料庫中欄位是否可以建立唯一索引,unique=True
unique_for_date 資料庫中欄位【日期】部分是否可以建立唯一索引,截斷做索引
unique_for_month 資料庫中欄位【月】部分是否可以建立唯一索引
unique_for_year 資料庫中欄位【年】部分是否可以建立唯一索引
auto_now 創建時,自動生成時間(DateTimeField類的參數)
auto_now_add 更新時,自動更新為當前時間(DateTimeField類的參數)
關於auto_now與auto_now_add的示例代碼見後面。

choices Admin中顯示選擇框的內容,用不變動的數據放在內存中從而避免跨表操作
如:gf = models.IntegerField(choices=[(0, 超級用戶),(1, 普通用戶),],default=1)
關於choices的用法請見後面代碼示例。
verbose_name Admin中顯示欄位中文,如verbose_name="用戶名"
blank Admin中是否可以為空
editable Admin中是否可以被編輯
help_text Admin中該欄位的提示信息,如help_text="密碼"

error_messages 自定義錯誤信息(字典類型),從而定製想要顯示的錯誤信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{null: "不能為空.", invalid: 格式錯誤}

validators 自定義錯誤驗證(列表類型),從而定製想要的驗證規則
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
c1: 優先錯信息1,
c2: 優先錯信息2,
c3: 優先錯信息3,
},
validators=[
RegexValidator(regex=root_d+, message=錯誤了, code=c1),
RegexValidator(regex=root_112233d+, message=又錯誤了, code=c2),
EmailValidator(message=又錯誤了, code=c3), ]
)

關於auto_now與auto_now_add的區別示例代碼如下所示:

class UserGroup(models.Model):
uid = models.AutoField(primary_key=True) # 利用AutoField設置自增列
caption = models.CharField(max_length=32)
# 在某個頁面函數使用了這個類,訪問該頁面下面的代碼就會自動獲取當前時間
ctime = models.DateTimeField(auto_now_add=True, null=True)
# 在某個頁面函數使用了這個類,執行指定方式的代碼才會更新當前時間
uptime = models.DateTimeField(auto_now=True, null=True)

# 執行下面的代碼,UserGroup類中的uptime不會自動更新
obj = UserGroup.objects.filter(id=1).update(caption=CTO)
# 執行下面三條代碼,UserGroup類中的uptime才會自動更新
obj = UserGroup.objects.filter(id=1).first()
obj.caption = CEO
obj.save()

關於choices參數的使用介紹,如下面代碼所示:

class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=60)
email = models.CharField(max_length=60)

user_type_choices = (
(1, 超級用戶),
(2, 普通用戶),
(3, 一般用戶),
)
user_type_id = models.IntegerField(choices=user_type_choices, default=1)

在上面代碼中,user_type_choices是雙重元組,在數據表中增加了user_type_id欄位,在數據表默認顯示的是數字,但是在Admin用戶的管理頁面中顯示的是下拉框形式,下拉框中有:超級用戶、普通用戶、一般用戶。這些數據表中的數字與管理頁面的下拉框顯示是由user_type_choices變數做對應的,這個對應關係是在內存中,這樣做可以避免連表查詢。

6 DjangoORM外鍵操作外鍵(ForeignKey)是兩個表之間建立關係的鍵,必須要以數字列作為外鍵關聯的列,並且數字列不能重複。例如在app01models.py文件中的UserInfo中添加下面這行代碼:user_group = models.ForeignKey("UserGroup", to_field="uid", default=1)這行代碼指定了外鍵關聯的是表UserGroup中的uid欄位,default參數表示默認關聯的是uid為1的值。當執行python manage.py makemigrations和python manage.py migrate時,在UserInfo表中就會增加一個欄位user_group,但在表中該欄位的實際名稱是user_group_id,要特別注意這一點。當查詢這個外鍵時,要注意下面這個情況:

user_list = UserInfo.objects.all()
for row in user_list:
print(row.user_group_id) # 這樣獲取到的是真實的user_group_id值
print(row.user_group) # 這裡的user_group指的是UserGroup對象,可通過這個user_group調用UserGroup對象中的欄位
print(row.user_gruou.uid) # 獲取UserGroup對象的uid值,與第一個print的輸出一樣

這裡一定要注意user_group是對象,這個對象封裝了UserGroup類的欄位,可以通過user_group對象去取UserGruoup類中的欄位值,在HTML代碼中也是通過這樣的方式獲取值。切記!

一對多操作要獲取資料庫的數據,可用下面這條命令一次性獲取:user_list = models.UserInfo.objects.all()但是要插入數據的話,就需要把每個參數都寫清楚,例如下面的插入代碼所示:

models.UserInfo.objects.create(
username=root,
password=12345,
email="[email protected]",
test="python",
user_group = models.UserGroup.objects.filter(id=1).first()
)

在這裡user_group是對象,所以賦值的是一個查詢對象,這點要注意。當然也可以直接使用user_group_id=1來做插入操作,例如下面代碼所示:

models.UserInfo.objects.create(
username=root1,
password=123,
email="asdfasdf",
test="asdfasdf",
user_group_id = 1
)

例如要在頁面上實現添加用戶功能,在頁面上有下拉框讓用戶選擇組,這個組的下拉框要與資料庫保持同步,就需要每次運行頁面時,GET請求都要從資料庫獲取最新的數據,此時在函數中可以將查詢結果傳給render()方法,讓其在HTML代碼中獲取數據並渲染到頁面上。代碼如下所示:

函數中的代碼片斷:group_list = models.UserGroup.objects.all()return render(request, user_info.html, {group_list: group_list})user_info.html中的代碼片斷如下所示:

<select>
{% for item in group_list %}
<option value="{{ item.uid }}">{{ item.caption }}</option>
{% endfor %}
</select>

實驗:用戶管理,要求如下:

1、用戶組的增刪改查

2、用戶的增刪改查 - 添加要用模態對話框 - 刪除也要用模態對話框 - 修改:必須顯示默認值,可在HTML中使用if判斷3、使用比較好看的頁面
推薦閱讀:
相关文章