設計背景html
爲你的員工或客戶生成一個用戶添加,修改和刪除內容的後臺是一項缺少創造性和乏味的工做。所以,django全自動地根據模型建立後臺界面。python
django產生於一個公衆頁面和內容發佈者頁面徹底分離的新聞類站點的開發過程當中。正則表達式
站點管理人員使用管理系統來添加新聞、時間和體育時訊等,這些添加的內容被顯示在公共頁面上。django經過爲站點管理人員建立統一的內容編輯界面解決了這個問題。數據庫
管理界面不是爲了網站的訪問者,而是爲了管理者準備的。django
首先,咱們得建立一個能登陸管理頁面的用戶。請運行下面的命令:後端
D:\django\mysite>py -3 manage.py createsuperuser瀏覽器
#輸入用戶名服務器
Username (leave blank to use 'lenovo'): adminsession
#輸入郵箱app
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的管理頁面默認就是啓用的。讓咱們啓用開發服務器,看看它究竟是什麼樣的。
若是開發服務器未啓動,用如下命令啓動它:
瀏覽器訪問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管理頁面對當前對象進行的改變的頁面,其中列出了時間戳和進行修改操做的用戶名:
好比,在一個博客應用中,你可能會建立以下幾個視圖:
1)博客首頁—展現最近的幾項內容。
2)內容「詳情」頁—詳細展現某項內容
3)以年爲單位的歸檔頁—展現選中的月份裏各天建立的內容。
4)以天爲單位的歸檔頁—展現選中天裏建立的全部內容
5)評論處理器—用於響應爲一項內容添加評論的操做
在咱們的投票應用中,咱們須要下列幾個視圖:
1)問題索引頁—展現最近的幾個投票問題
2)問題詳情頁—展現某個投票的問題和不帶結果的選項類表
3)問題結果頁—展現某個投票的結果
4)投票處理器—用於響應用戶爲某個問題的特定選項投票的操做
每個視圖表現爲一個簡單的python函數(或者說方法,若是是在基於類的視圖裏的話)。
django將會根據用戶請求的URL來選擇使用哪一個視圖(更準確的說,是根據URL中域名以後的部分)。
在你上網的過程當中,極可能看見過像這樣的URL:
「ME2/Sites/dirmod.sap?sid=&type=gen&mod=Core+Pages&gid=A6CD123KJUHJ213」。別擔憂,django裏的URL規則要比這優雅的多!
—舉個例子:/newsarchive/<year>/<month>/
爲了將URL和視圖關聯起來,django使用了’URLconfs’來配置。URLconf將URL模式映射到視圖。
如:mysite/urls.py:
urlpatterns = [ path('polls/', include('polls.urls')), path('admin/', admin.site.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.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'), ]
由於這在配置項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作進一步處理。
匹配的順序是先根據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/」,將會看到一個無序列表,列出了咱們在以前添加的投票問題,連接指向了這個投票的詳情頁。
點擊連接:
「載入模板,填充上下文,再返回由它生成的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對象。
如今,咱們來處理投票詳情視圖—它會顯示指定投票的問題標題。下面是視圖的代碼:
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()函數獲取一個對象,若是不存在就拋出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異常。