django管理界面
設計背景
為你的員工或客戶生成一個用戶添加,修改和刪除內容的后台是一項缺乏創造性和乏味的工作。因此,django全自動地根據模型創建后台界面。
django產生於一個公眾頁面和內容發布者頁面完全分離的新聞類站點的開發過程中。
站點管理人員使用管理系統來添加新聞、時間和體育時訊等,這些添加的內容被顯示在公共頁面上。django通過為站點管理人員創建統一的內容編輯界面解決了這個問題。
管理界面不是為了網站的訪問者,而是為了管理者准備的。
創建一個管理員賬號
首先,我們得創建一個能登錄管理頁面的用戶。請運行下面的命令:
py -3 manage.py createsuperuser
D:\django\mysite>py -3 manage.py createsuperuser
#輸入用戶名
Username (leave blank to use 'lenovo'): admin
#輸入郵箱
Email address: admin@example.com
#輸入密碼
Password:
Password (again):
The password is too similar to the username.
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
啟動開發服務器
django的管理頁面默認就是啟用的。讓我們啟用開發服務器,看看它到底是什么樣的。
如果開發服務器未啟動,用以下命令啟動它:
python manage.py runserver
瀏覽器訪問http://127.0.0.1:8000/admin/,轉到本地域名的”/admin/”目錄,你會看到管理員登錄界面:

django默認開啟翻譯功能,所以登錄界面可能會使用瀏覽器的語言,取決於瀏覽器的設置和django是否擁有你語言的翻譯。
進入管理員站點頁面
現在,試着使用你在上一步中創建的超級用戶來登錄。然后你將會看到django管理頁面的索引頁:

你將會看到幾種可編輯的內容:組和用戶。它們是由django.contrib.auth提供的,這是django開發的認證框架。
向管理頁面中加入投票應用
但是我們的投票應用在哪兒呢?它沒在索引頁面里顯示
只需要做一件事:我們得告訴管理頁面,問題Question對象需要被管理。開發polls/admin.py文件,把它編輯成下面這樣:
polls/admin.py:
from django.contrib import admin # Register your models here. from .models import Question admin.site.register(Question)
體驗便捷的管理功能
現在我們向管理頁面注冊了問題Question類。django知道它應該被顯示在索引頁里:

點擊“Question”。現在看到是問題“Question”對象的列表“change list”。這個界面會顯示所有數據庫里的問題Queston對象,你可以選擇一個來修改。這里現在有我們在上一部分中創建的”What’s new?”問題。

點擊”What’s new?”來編輯這個問題(Question)對象:

注意事項:
1)這個表單時從問題Question模型中自動生成的
2)不同的字段類型(日期時間字段DateTimeField、字符字段CharField)會生成對應的HTML輸入控件。每個類型的字段都知道它們該如何在管理頁面顯示自己。
3)每個日期時間字段DateTimeField都有JavaScript寫的快捷按鈕。日期有轉到今天(Today)的快捷按鈕和一個彈出式日歷界面。時間有設為現在(Now)的快捷按鈕和一個列出常用時間的方便的彈出式列表。
頁面底部提供了幾個選項
1)保存(Save)-保存修改,然后返回對象列表
2)保存並繼續編輯(Save and continue editing)-保存改變,然后重新載入當前對象的修改界面。
3)保存並新增(Save and add another)-保存改變,然后添加一個新的空對象並載入修改界面
4)刪除(Delete)-顯示一個確認刪除頁面
如果顯示的“發布日期(Date Published)”和你在創建它們的時間不一致,這意味着你可能沒有正確的設置時區(TIME_ZONE)。改變設置,然后重新載入頁面看看是否顯示了正確的值。
通過點擊“今天(Today)”和 “現在(Now)”按鈕改變“發布日期(Date Published)”。然后點擊“保存並繼續編輯(Save and add another)”按鈕。然后點擊右上角的“歷史(History)”按鈕。你會看到一個列出了所有通過django管理頁面對當前對象進行的改變的頁面,其中列出了時間戳和進行修改操作的用戶名:

視圖
django中的視圖的概念是【一類具有相同功能和模板的網頁的集合】。
比如,在一個博客應用中,你可能會創建如下幾個視圖:
1)博客首頁—展示最近的幾項內容。
2)內容“詳情”頁—詳細展示某項內容
3)以年為單位的歸檔頁—展示選中的月份里各天創建的內容。
4)以天為單位的歸檔頁—展示選中天里創建的所有內容
5)評論處理器—用於響應為一項內容添加評論的操作
在我們的投票應用中,我們需要下列幾個視圖:
1)問題索引頁—展示最近的幾個投票問題
2)問題詳情頁—展示某個投票的問題和不帶結果的選項類表
3)問題結果頁—展示某個投票的結果
4)投票處理器—用於響應用戶為某個問題的特定選項投票的操作
在django中,網頁和其他內容都是從視圖派生而來。
每一個視圖表現為一個簡單的python函數(或者說方法,如果是在基於類的視圖里的話)。
django將會根據用戶請求的URL來選擇使用哪個視圖(更准確的說,是根據URL中域名之后的部分)。
在你上網的過程中,很可能看見過像這樣的URL:
“ME2/Sites/dirmod.sap?sid=&type=gen&mod=Core+Pages&gid=A6CD123KJUHJ213”。別擔心,django里的URL規則要比這優雅的多!
一個URL模式定義了某種URL的基本格式
—舉個例子:/newsarchive/<year>/<month>/
為了將URL和視圖關聯起來,django使用了’URLconfs’來配置。URLconf將URL模式映射到視圖。
如:mysite/urls.py:
urlpatterns = [ path('polls/', include('polls.urls')), path('admin/', admin.site.urls), ]
待練習URL調度器:https://docs.djangoproject.com/zh-hans/2.1/topics/http/urls/
編寫更多視圖
現在我們向polls/views.py里添加更多視圖,這些視圖有一些不同,因為他們接收參數:
polls/views.py:
def detail(request, question_id): return HttpResponse("You're lokking at question %s." % requestion_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)
我們把這些新視圖添加進polls.urls模塊里,只要添加幾個url()(re_path函數的別名)函數調用就可以:
添加視圖函數到polls.urls模塊
polls/urls.py:
urlpatterns = [ # ex: /polls/ path('', views.index, name='index'), # ex: /polls/5/ path('<int:question_id>/', views.detail, name='detail'), # ex:/pools/5/results/ path('<int:question_id>/results/', views.results, name='results'), # ex: /polls/5/vote/ path('<int:question_id>/vote/', views.vote, name='vote'), ]
瀏覽器訪問”/polls/14/”,django會將運行detail()方法並且展示你在URL里提供的問題ID。

訪問”/polls/34/vote/”和”/polls/14/vote/”,你將會看到暫時用於占位的結果和投票頁

當某人請求你網站的某一頁時,如”/polls/14/”, django將會載入mysite.urls模塊
因為這在配置項ROOT_URLCONF中設置了:
settings.py:
ROOT_URLCONF = 'mysite.urls' mysite/urls.py: urlpatterns = [ path('polls/', include('polls.urls')), path('admin/', admin.site.urls), ]
polls/urls.py:
urlpatterns = [ # ex: /polls/ path('', views.index, name='index'), # ex: /polls/5/ path('<int:question_id>/', views.detail, name='detail'), # ex:/pools/5/results/ path('<int:question_id>/results/', views.results, name='results'), # ex: /polls/5/vote/ path('<int:question_id>/vote/', views.vote, name='vote'), ]
然后django尋找名為urlpatterns變量並且按順序匹配正則表達式。在找到匹配項’polls/’,它切掉了匹配的文本(”polls/”),將剩余文本--”14/”,發送到”polls.urls” URLconf做進一步處理。
URLconf就是指polls.urls
url匹配視圖的過程
匹配的順序是先根據settings.py中ROOT_URLCONF變量('mysite.urls')指向的urls文件去找urlpatterns變量,在urlpatterns變量中依次匹配path()函數中的route參數('polls/'),匹配到之后,把匹配到的文本('polls/'--路徑的一部分)截掉,剩余的文本(‘14/’),去path()函數中指定的URLconf(polls.urls)中去匹配視圖函數,這里剩余的文本”14/”匹配到了’<int:question_id>/’,使得django以如下形式調用detail():
detail(request=<HttpRequest object>, question_id=14)
polls/views.py: detail函數的定義:
def detail(request, question_id): return HttpResponse("You're lokking at question %s." % question_id)
question_id=14由<int:question_id>匹配生成。使用尖括號“捕獲”這部分URL,且以關鍵字參數的形式發送給視圖函數。上述字符串的:question_id>部分定義了將被用於區分匹配模式的變量名,這里的模式就是urlpattern里的一行匹配代碼,而int: 則是一個轉換器,決定了應該以什么變量類型匹配這部分的URL路徑。
為每個URL加上不必要的東西,例如.html,是沒有必要的。如果非要加的話,也可以:
path(‘polls/latest.html’, views.index),
但不推薦這樣做,太low了
寫一個真正有用的視圖
每個視圖必須要做的只有兩件事:
返回一個包含被請求頁面內容的HttpResponse對象,或拋出一個異常,比如Http404。
至於你還想干些什么,隨便你。
你的視圖可以從數據庫里讀取記錄,可以使用一個模板引擎(比如django自帶的,或者其他第三方的),可以生成一個PDF文件,可以輸出一個XML,創建一個ZIP文件,你可以做任何你想做的事,使用任何你想用的python庫。
django只要求返回的是一個HttpResponse,或者拋出一個異常
因為django自帶的數據庫API很方便,我們在前面學過,所以我們試試在視圖里使用它。
在index()函數里插入一些新的內容,讓它能展示數據庫里以發布日期排序的最近5個投票問題,以空格分割:
from django.shortcuts import render # Create your views here. from django.http import HttpResponse from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] output = ', '.join([q.question_text for q in latest_question_list]) return HttpResponse(output)
其他視圖函數不變(detail,results,vote)
為了演示,我們添加6個問題:

查看數據庫中的數據

瀏覽器訪問http://127.0.0.1:8000/polls/,觸發index視圖函數:

這里有個問題:頁面的設計寫死在視圖函數的代碼里的。如果你想改變頁面的樣子,你需要編輯python代嗎。所以讓我們使用django的模板系統,只要創建一個視圖,就可以將頁面的設計從代碼中分離出來。
使用django的模板系統
首先,在你的polls項目里創建一個templates目錄。django將會在這個目錄里查找模板文件。
你的項目的TEMPLATES配置項描述了django如何載入和渲染模板。默認的設置文件設置了DjangoTemplates后端,並將APP_DIRS設置成了True。這一選項將會讓DjangoTemplates在每個INSTALLED_APPS文件夾中尋找”templates”子目錄。
settings.py:
INSTALLED_APPS = [ 'polls.apps.PollsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
在你剛剛創建的templates目錄里,再創建一個目錄polls,然后在其中新建一個文件index.html。換句話說,你的模板文件的路徑應該是polls/templates/polls/index.html。因為django會尋找到對應的app_directories,所以你只需要使用polls/index.html就可以引用到這一模板了。
模板命名空間
雖然我們現在可以將模板文件直接在polls/templates文件中(而不是再建立一個polls子文件夾),但是這樣做不太好。django將會選擇第一個匹配的模板文件,如果你有一個模板文件正好和另一個應用中的某個模板文件重名,django沒有辦法區分它們。我們需要幫助django選擇正確的模板,最簡單的方法就是把它們放入個字的命名空間中,也就是把這些模板放入一個和自身應用重名的子文件夾里。
所以我們在templates文件夾下,新建一個跟應用重名的文件夾”polls”,然后在該子文件夾下新建一個index.html文件:polls/templates/polls/index.hmtl
在index.html模板中輸入如下代碼
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
然后,讓我們更新一下polls/views.py里的index視圖來使用模板:
視圖函數中使用模板:
polls/view.py:
from django.http import HttpResponse from django.template import loader from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] template = loader.get_template('polls/index.html') context = { 'latest_question_list': latest_question_list, } return HttpResponse(template.render(context, request))
上下文:
上述代碼的作用是,載入polls/index.html模板文件,並且向它傳遞一個上下文(context)。這個上下文是一個字典,它將模板內的變量映射為python對象。
此處context是一個字典,渲染模板時通過template.render()方法把context和請求對象傳入到模板文件中,在模板文件中,對context中的對象進行渲染
再次觸發視圖函數,渲染模板
瀏覽器訪問”polls/”,將會看到一個無序列表,列出了我們在之前添加的投票問題,鏈接指向了這個投票的詳情頁。

點擊鏈接:

快捷函數:render()
“載入模板,填充上下文,再返回由它生成的HttpResponse對象”,是一個非常常用的操作流程。
於是django提供了一個快捷函數,我們用它來重寫index()視圖:
polls/views.py
from django.shortcuts import render from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context)
注意,我們不再需要導入loader和HttpResponse,不過如果你還有其他函數(比如details,results和vote)需要用到它的話,就需要保持HttpResponse的導入
再次啟動服務器,訪問127.0.0.1:8000/polls/,可以正常訪問

render()函數的第一個參數是請求對象request,模板文件名稱作為第二個參數,第三個參數是一個可選的,字典形式的對象。render()函數返回的是模板文件根據context參數渲染后得到的HttpResponse對象。
拋出404錯誤
現在,我們來處理投票詳情視圖—它會顯示指定投票的問題標題。下面是視圖的代碼:
polls/views.py:
from django.shortcuts import render from .models import Question from django.http import Http404 def detail(request, question_id): try: question = Question.objects.get(pk=question_id) except Question.DoesNotExist: raise Http404("Question does not exist") return render(request, 'polls/detail.html', {'question': question})
這里有個新原則,如果指定問題ID所對應的問題不存在,這個視圖就會拋出一個Http404異常。
我們稍后討論你需要在polls/detail.html里輸入什么,但是如果你想試試上面這段代碼是否正常工作的話,可以暫時把下面這段輸進去,這樣你就能測試了。
polls/templates/polls/detail.html:
{{ question }}
訪問一個不存在的request_id: http://127.0.0.1:8000/polls/10/

快捷函數:get_object_or_404()
嘗試用get()函數獲取一個對象,如果不存在就拋出Http404錯誤也是一個普遍的流程。
django也提供了一個快捷函數,下面是修改后的詳情detail()視圖代碼:
polls/views.py:
from django.shortcuts import get_object_or_404, render from .models import Question def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question})
get_object_or_404()方法的第一個參數是模型類,第二個參數可以是任意的數字,該數字會傳給模型類對應的manager的get()方法,如果用該數字查不到對象,則會報Http404錯誤
設計哲學:
為什么我們使用輔助函數get_object_or_404()而不是自己捕獲ObjectDoesNotExist異常呢?還有,為什么模型API不直接拋出ObjectDoesNotExist而是拋出Http404呢?
因為這樣做會增加模型層和視圖層的耦合性。指導django設計的最重要的思想之一就是要保證松散耦合。一些受控的耦合將會包含在django.shortcuts模塊中。
也有get_list_or_404()函數,工作原理和get_object_or_404()一樣,除了get()函數被換成了filter()函數。如果類表為空的話會拋出Http404異常。
