Django 官網(wǎng)最新 Tutorial 渣翻 - Part 3

上一節(jié): Django 官網(wǎng)最新 Tutorial 渣翻 - Part 2

手寫第一個(gè)Django應(yīng)用, 第二部分

緊接著Tutorial 2 ,我們繼續(xù)開發(fā)投票這個(gè)web應(yīng)用,并將注意力集中在創(chuàng)建對外訪問的“視圖”界面上脓杉。

概覽

視圖(view)是Django應(yīng)用中的一“類”網(wǎng)頁,它通常有一個(gè)特定的函數(shù)以及一個(gè)特定的模板。例如,在博客應(yīng)用中芦昔,可能有以下視圖:

  • 博客首頁 —— 顯示最新發(fā)表的文章鏈接。
  • 博客“詳細(xì)”頁面 —— 單篇文章的詳細(xì)頁面娃肿。
  • 基于年份的歸檔頁面 —— 顯示某給定年份里所有月份發(fā)表過的博客咕缎。
  • 基于月份的歸檔頁面 —— 顯示在給定月份中發(fā)表過所有文章珠十。
  • 基于日期的歸檔頁面 —— 顯示在給定日期中發(fā)表過的所有文章鏈接。
  • 評論 —— 評論某博客

在我們的投票應(yīng)用中凭豪,將有以下四個(gè)視圖:

  • Question首頁 —— 顯示最新發(fā)布的幾個(gè)Question宵睦。
  • Question“詳細(xì)”頁面 —— 顯示單個(gè)Question的具體內(nèi)容,有一個(gè)投票的表單墅诡,但沒有投票結(jié)果壳嚎。
  • Question“結(jié)果”頁面 —— 顯示某Question的投票結(jié)果。
  • 投票功能 —— 可對Question中某個(gè)Choice的進(jìn)行投票末早。

在Django中烟馅,網(wǎng)頁的頁面和其他內(nèi)容都是由視圖來傳遞的.每個(gè)視圖都是由一個(gè)簡單的Python函數(shù)(或者是基于類的視圖的方法)。Django通過檢查請求的URL(準(zhǔn)確地說然磷,是URL里域名之后的那部分)來選擇使用哪個(gè)視圖郑趁。

平日你上網(wǎng)時(shí),可能會(huì)遇到像 “ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”這樣優(yōu)美的URL姿搜。 你將會(huì)愉快地了解到寡润,Django允許我們使用更加優(yōu)雅的URL模式。

URL模式(URL pattern)就是一個(gè)URL的通用形式 —— 例如: /newsarchive/<year>/<month>/.

Django使用叫做‘URLconfs’的配置來為URL匹配視圖舅柜。 一個(gè)URLconf負(fù)責(zé)將URL模式匹配到視圖梭纹。

本教程有URLconfs的基本使用方法,你可以在 URL dispatcher看到更詳細(xì)的信息 致份。


編寫更多的視圖

現(xiàn)在讓我們給polls/views.py添加一些更多的視圖变抽。這些視圖和之前的略有不同,因?yàn)樗鼈兞韼Я艘粋€(gè)參數(shù):

polls/views.py

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_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)

通過下面的path() 調(diào)用將這些新的視圖和polls.urls模塊關(guān)聯(lián)起來:

polls/urls.py

from django.urls import path

from . import views

urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/5/
    path('<int:question_id>/', views.detail, name='detail'),
    # ex: /polls/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/34/”, 它將運(yùn)行detail()方法并顯示你在URL中提供的ID绍载。 再試一下“/polls/34/results/”和“/polls/34/vote/” —— 它們將顯示出對應(yīng)的結(jié)果界面和投票界面。

當(dāng)有人請求你的網(wǎng)站的一個(gè)頁面時(shí) —— 比如“/polls/34/”滔蝉,Django 將加載 python 模塊 mysite.urls, 因?yàn)樵O(shè)置文件中的ROOT_URLCONF 指定了他, 他會(huì)找變量名為urlpatterns并按順序遍歷他, 當(dāng)找到匹配的'polls/'后, 它會(huì)截?cái)嗥ヅ涞降淖址?"polls/"), 然后發(fā)送剩余的字符串 --- "34/" 給 ‘polls.urls’ 這個(gè)URLconf作進(jìn)一步處理. 這里他僅匹配到了 '<int:question_id>/' , 這樣一來他就會(huì)像這樣一樣調(diào)用 detail():

detail(request=<HttpRequest object>, question_id=34)

question_id=34部分來自<int:question_id>, 使用尖括號(hào)"捕獲"的URL, 并將他(尖括號(hào)中的內(nèi)容)作為關(guān)鍵字參數(shù)傳入視圖函數(shù)中, question_id>, 部分的內(nèi)容當(dāng)作匹配的標(biāo)識(shí), <int來決定怎么匹配.

我們不需要添加那些不像url的東西, 如 .html - 除非你想這么做, 這種情況下你可以這樣弄:

path('polls/latest.html', views.index),

不過這真是一種**(文明你我他)的行為: )


寫點(diǎn)有意義的視圖

每個(gè)視圖都負(fù)責(zé)一兩件事, 返回一個(gè)包含整個(gè)請求頁面內(nèi)容的HttpResponse對象, 或者拋出一個(gè)像 Http404的異常, 這取決于你.

你的視圖可以是從數(shù)據(jù)庫讀取記錄, 或不讀取數(shù)據(jù)庫, 你能用Django自帶的模板系統(tǒng), 或第三方的模板系統(tǒng), 甚至你也可以不用模板, 你還可以動(dòng)態(tài)的生成一個(gè)PDF文件, 輸出XML文件, 創(chuàng)建一個(gè)ZIP文件击儡,使用你想用的Python 庫生成任何你想要的。

Django只要求返回一個(gè)HttpResponse.或拋出異常.

讓我們來用用Tutorial 2學(xué)到的數(shù)據(jù)庫API蝠引,它是非常方便的阳谍。下面是一個(gè)新的index()視圖,它列出了最近的5個(gè)投票Question記錄立肘,并用逗號(hào)隔開边坤,以發(fā)布日期排序:

polls/views.py

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)

# Leave the rest of the views (detail, results, vote) unchanged

這里有一個(gè)問題:頁面的設(shè)計(jì)被硬編碼在視圖中。 如果你想更改頁面的外觀谅年,就得編輯這段Python代碼。 因此肮韧,讓我們使用Django的模板系統(tǒng)融蹂,通過創(chuàng)建一個(gè)視圖能夠調(diào)用的模板旺订,將頁面的html代碼從Python中分離出來。

首先超燃,在你的polls目錄下創(chuàng)建一個(gè)叫做 templates的目錄区拳。Django將在這里查找模板。

你項(xiàng)目配置文件中的 TEMPLATES 決定了Django如何加載和渲染模板意乓。默認(rèn)配置下樱调,Django模板引擎在 APP_DIRS中的設(shè)置為True。Django模板引擎會(huì)在INSTALLED_APPS里的各個(gè)APP目錄下查找名為templates的子目錄届良。

在你剛剛創(chuàng)建的templates目錄中笆凌,創(chuàng)建另外一個(gè)目錄polls,并在其中創(chuàng)建一個(gè)文件index.html士葫。也就是說乞而,你的模板應(yīng)該位于 polls/templates/polls/index.html。由于app_directories 模板加載器按照上面描述的方式工作慢显,在Django中你可以簡單地用polls/index.html引用這個(gè)模板爪模。

模板命名空間
其實(shí)我們可以直接將我們的模板放在polls/templates中(而不用創(chuàng)建另外一個(gè)polls子目錄),但實(shí)際上這是個(gè)壞主意荚藻。Django將選擇它找到的名字匹配的第一個(gè)模板文件屋灌,如果你在不同的應(yīng)用有相同名字的模板文件,Django將不能區(qū)分它們应狱。我們需要將Django指向正確的模板声滥,最簡單的方式是使用命名空間。具體實(shí)現(xiàn)方式是侦香,將這些模板文件放在以應(yīng)用的名字來命名的另一個(gè)目錄下落塑。

將以下代碼寫入剛創(chuàng)建的模板:

polls/templates/polls/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 %}

Now let’s update our index view in polls/views.py to use the template:
現(xiàn)在我們用剛剛的模板來更新polls/views.py的視圖函數(shù):

polls/views.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模板,并傳遞了一個(gè)context對象罐韩,context是一個(gè)字典憾赁, 將模板的變量和python對象一一對應(yīng)。

瀏覽器訪問“/polls”散吵,你看到一個(gè)列表龙考,包含了我們在Tutorial 2創(chuàng)建的 “What’s up” question,這個(gè)鏈接指向了Question的詳細(xì)頁.

一種快捷方式: render()

加載模板矾睦、填充一個(gè)context 然后返回一個(gè)含有模板渲染結(jié)果的HttpResponse對象是非常頻繁的晦款。Django為此提供一個(gè)快捷方式。下面是重寫后的index()視圖:

polls/views.py

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)

注意枚冗,一旦我們在所有的視圖上都應(yīng)用這個(gè)快捷函數(shù)缓溅,我們將不再需要導(dǎo)入loaderHttpResponse(如果你沒有改變先前的detail、results和 vote方法赁温,你將需要在導(dǎo)入中保留HttpResponse )坛怪。

render()函數(shù)將請求對象(request)作為它的第一個(gè)參數(shù)淤齐,模板的名字作為它的第二個(gè)參數(shù),一個(gè)字典作為它可選的第三個(gè)參數(shù)袜匿。 它返回一個(gè) HttpResponse 對象更啄,含有用給定的context 渲染后的模板。


拋出一個(gè)404錯(cuò)誤

現(xiàn)在讓我們來處理question detail視圖——顯示某question內(nèi)容的頁面居灯,下面是該視圖:

polls/views.py

from .models import Question
# ...
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})

新概念:這個(gè)視圖拋出了Http404 異常祭务,如果請求的question ID不存在的情況下。

稍后我們將討論polls/detail.html模板可以寫點(diǎn)什么怪嫌。但是如果你想快速讓上面的例子工作义锥,如下就可以:

polls/templates/polls/detail.html

{{ question }}

快捷方法: get_object_or_404

當(dāng)用 get()時(shí),如果對象不存在拋出 Http404異常是很常用的喇勋。Django提供了一個(gè)快捷方式缨该,重寫后的detail()視圖:

polls/views.py

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()函數(shù)將Django模型作為它的第一個(gè)參數(shù),任意數(shù)量關(guān)鍵詞參數(shù)川背,它將傳遞給作為模型管理器的 get() 函數(shù)贰拿,如果對象不存在,它就引發(fā)一個(gè) Http404 異常熄云。

哲學(xué)
為什么我們要用一個(gè)輔助函數(shù) get_object_or_404() 而不是在上層捕獲ObjectDoesNotExist異常膨更,或者讓模型的API引發(fā) Http404而不是ObjectDoesNotExist
因?yàn)槟菢訒?huì)將模型層與視圖層耦合。Django 最重要的設(shè)計(jì)目標(biāo)就是保持松耦合缴允。一些可控的耦合在django.shortcuts有介紹荚守。

還有一個(gè) get_list_or_404()函數(shù), 原理和get_object_or_404()一樣——差別在與它用filter() 而不是 get(). 當(dāng)列表為空時(shí),它拋出 Http404 異常练般。

使用模板系統(tǒng)

回到我們投票應(yīng)用的detail()視圖矗漾。 根據(jù)context 變量question,下面是polls/detail.html模板可能的樣子:

polls/templates/polls/detail.html

{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

模板系統(tǒng)使用點(diǎn)號(hào)查找語法來訪問變量的屬性薄料。在例子 {{ question.question_text }}中, Django首先對question對象做字典查詢敞贡。如果失敗,Django會(huì)接著嘗試按屬性查詢 —— 在這個(gè)例子中摄职,屬性查詢會(huì)成功誊役。如果屬性查詢也失敗,Django將嘗試列表索引查詢谷市。

方法調(diào)用發(fā)生在 {% for %}循環(huán)中: question.choice_set.all 被解釋為Python的代碼question.choice_set.all()蛔垢,它返回一個(gè)由Choice對象組成的可迭代對象,并將其用于{% for %} 標(biāo)簽迫悠。

查看 template guide 了解更多模板信息.


去除模板中的硬編碼

請記住鹏漆,當(dāng)我們在polls/index.html 模板中編寫一個(gè)指向Question的鏈接時(shí),鏈接中一部分是硬編碼的:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

這種緊耦合的硬編碼有一個(gè)問題,就是如果我們想在模板眾多的項(xiàng)目中修改URLs甫男,將會(huì)變得非常困難且改。 但是验烧,如果你在polls.urls模塊的 path() 函數(shù)中定義了name 參數(shù)板驳,你可以通過使用 {% url %}模板標(biāo)簽來移除對你的URL配置中定義的特定的URL的依賴:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

它的工作原理是在 polls.urls 模塊里查找指定的URL。你可以看到名為‘detail’的URL的準(zhǔn)確定義:

...
#  {% url %} 模板Tag調(diào)用了'name' 的值
path('<int:question_id>/', views.detail, name='detail'),
...

如果你想把polls應(yīng)用中detail視圖的URL改成其它樣子比如 polls/specifics/12/ 碍拆,就可以不必在該模板(或者多個(gè)模板)中修改它若治,只需要修改 polls/urls.py

...
# added the word 'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),
...

URL命名空間

教程中的這個(gè)項(xiàng)目只有一個(gè)應(yīng)用polls。在真實(shí)的Django項(xiàng)目中感混,可能會(huì)有五個(gè)端幼、十個(gè)、二十個(gè)或者更多的應(yīng)用弧满。 Django如何區(qū)分它們URL的名字呢婆跑? 例如,polls應(yīng)用具有一個(gè)detail 視圖庭呜,相同項(xiàng)目中的博客應(yīng)用可能也有這樣一個(gè)視圖滑进。當(dāng)使用模板標(biāo)簽{% url %} 時(shí),人們該如何做才能使得Django知道為一個(gè)URL創(chuàng)建哪個(gè)應(yīng)用的視圖募谎?

答案是在你的主URLconf下添加命名空間扶关。 在 polls/urls.py文件中,增加一個(gè)app_name的變量作為命名空間:

polls/urls.py

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/results/', views.results, name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

現(xiàn)在修改polls/index.html模板:

polls/templates/polls/index.html

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

當(dāng)你對你寫的視圖感到滿意后数冬,請閱讀 part 4 of this tutorial來了解簡單的表單處理和通用視圖节槐。

下一節(jié): Django 官網(wǎng)最新 Tutorial 渣翻 - Part 3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拐纱,隨后出現(xiàn)的幾起案子铜异,更是在濱河造成了極大的恐慌,老刑警劉巖秸架,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揍庄,死亡現(xiàn)場離奇詭異,居然都是意外死亡咕宿,警方通過查閱死者的電腦和手機(jī)币绩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來府阀,“玉大人缆镣,你說我怎么就攤上這事∈哉悖” “怎么了董瞻?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我钠糊,道長挟秤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任抄伍,我火速辦了婚禮艘刚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘截珍。我一直安慰自己攀甚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布岗喉。 她就那樣靜靜地躺著秋度,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钱床。 梳的紋絲不亂的頭發(fā)上荚斯,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機(jī)與錄音查牌,去河邊找鬼事期。 笑死,一個(gè)胖子當(dāng)著我的面吹牛僧免,可吹牛的內(nèi)容都是我干的刑赶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼懂衩,長吁一口氣:“原來是場噩夢啊……” “哼撞叨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浊洞,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤牵敷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后法希,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枷餐,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年苫亦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了毛肋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屋剑,死狀恐怖润匙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唉匾,我是刑警寧澤孕讳,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響厂财,放射性物質(zhì)發(fā)生泄漏芋簿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一璃饱、第九天 我趴在偏房一處隱蔽的房頂上張望与斤。 院中可真熱鬧,春花似錦帜平、人聲如沸幽告。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至齐唆,卻和暖如春嗤栓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箍邮。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工茉帅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锭弊。 一個(gè)月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓堪澎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親味滞。 傳聞我的和親對象是個(gè)殘疾皇子樱蛤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容