1. 程式人生 > >Part3:視圖和模板

Part3:視圖和模板

part .html 印象 方法調用 out 部分 urls.py文件 hat available

一、簡述

一個視圖就是一個頁面,通常提供特定的功能,使用特定的模板。例如:在一個博客應用中,你可能會看到下列視圖:

  • 博客主頁:顯示最新發布的一些內容
  • 博客主頁:顯示最新發布的一些內容
  • 基於年的博客頁面:顯示指定年內的所有博客文章
  • 基於月的博客頁面:顯示指定月內的所有博客文章
  • 基於天的博客頁面:顯示指定日內的所有博客文章
  • 發布評論:處理針對某篇博客發布的評論

二、編寫視圖

下面,打開polls/views.py:文件,輸入下列代碼


from django.shortcuts import render, HttpResponse

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)

然後,在polls/urls.py:文件中加入下面的url模式,將其映射到我們上面新增的視圖。 from django.conf.urls import url from . import views

urlpatterns = [
    # ex: /polls/
    url(r^$, views.index, name=index),
    # ex: /polls/5/
    url(r^(?P<question_id>[0-9]+)/$, views.detail, name=detail),
    # ex: /polls/5/results/
url(r^(?P<question_id>[0-9]+)/results/$, views.results, name=results), # ex: /polls/5/vote/ url(r^(?P<question_id>[0-9]+)/vote/$, views.vote, name=vote), ]

  現在去瀏覽器中訪問/polls/34/(註意:這裏省略了域名。另外,使用了二級路由後,url中都要添加polls部分,參考前面的章節),它將調用detail()函數,然後在頁面中顯示你在url裏提供的ID。訪問/polls/34/results/和/polls/34/vote/,將分別顯示預定義的偽結果和投票頁面

  上面訪問的路由過程如下:當有人訪問/polls/34/地址時,Django將首先加載mysite.urls模塊,因為它是settings文件裏設置的根URL配置文件。在該文件裏,Django發現了urlpatterns變量,於是在其內按順序進行匹配。當它匹配上了^polls/,就裁去url中匹配的文本polls/,然後將剩下的文本“34/”,傳遞給polls.urls進行下一步的處理。在polls.urls中,又匹配到了r’^(?P<question_id>[0-9]+)/$’,最終結果就是調用該模式對應的detail()視圖

三、編寫能實際幹點活的視圖

每個視圖至少做兩件事之一:返回一個包含請求頁面的HttpResponse對象或者彈出一個類似Http404的異常。其它的則隨你便,你愛幹嘛幹嘛

下面是一個新的index()視圖,用於替代先前無用的index,它會根據發布日期顯示最近的5個投票問卷。

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)

  這裏有個非常重要的問題:在當前視圖中的HTML頁面是硬編碼的。如果你想改變頁面的顯示內容,就必須修改這裏的Python代碼。為了解決這個問題,需要使用Django提供的模板系統,解耦視圖和模板之間的硬連接。

  首先,在polls目錄下創建一個新的templates目錄,Django會在它裏面查找模板文件。在templates目錄中,再創建一個新的子目錄名叫polls,進入該子目錄,創建一個新的html文件index.html。換句話說,你的模板文件應該是polls/templates/polls/index.html。可以在DJango中直接使用polls/index.html引用該文件。

註意:在Pycharm中,templates文件夾通常已經幫你創建好了!

模板命名空間:

  你也許會想,為什麽不把模板文件直接放在polls/templates目錄下,而是費勁的再建個子目錄polls呢?設想這麽個情況,有另外一個app,它也有一個名叫index.html的文件,當Django在搜索模板時,有可能就找到它,然後退出搜索,這就命中了錯誤的目標,不是我們想要的結果。解決這個問題的最好辦法就是在templates目錄下再建立一個與app同名的子目錄,將自己所屬的模板都放到裏面,從而達到獨立命名空間的作用,不會再出現引用錯誤。

現在,將下列代碼寫入文件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 %}

同時,修改視圖文件polls/views.py,讓新的index.html文件生效:

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文件,並傳遞給它一個參數。這個參數是一個字典,包含了模板變量名和Python對象之間的映射關系。

  在瀏覽器中通過訪問/polls/,你可以看到一個列表,包含“What’s up”的問卷,以及連接到其對應詳細內容頁面的鏈接點。

  如果你顯示的是No polls are available.說明你前面沒有添加Questions對象。沒關系,我們手動添加一下就可以。

進入admin界面,選擇Questions,點擊右上角的Add question,如下操作。

技術分享圖片

添加完後,刷新/polls/頁面。

快捷方式:render()

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)

  在實際運用中,加載模板、傳遞參數,返回HttpResponse對象是一整套再常用不過的操作了,為了節省力氣,Django提供了一個快捷方式:render函數,一步到位!看如下代碼:

  render()函數的第一個位置參數是請求對象(就是view函數的第一個參數),第二個位置參數是模板。還可以有一個可選的第三參數,一個字典,包含需要傳遞給模板的數據。最後render函數返回一個經過字典數據渲染過的模板封裝而成的HttpResponse對象

四、返回404錯誤

  現在讓我們來編寫返回具體問卷文本內容的視圖polls/views.py

from django.shortcuts import render, HttpResponse
from django.shortcuts import Http404
from .models import Question


def detail(request, question_id):
    """
    測試Http404
    :param request:
    :param question_id:
    :return:
    """
    try:
        # 獲取Question表中的id數據
        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文件,暫時寫入下面的代碼:

{{ question }}

快捷方式:get_object_or_404()

  就像render函數一樣,Django同樣為你提供了一個偷懶的方式,替代上面的多行代碼,那就是get_object_or_404()方法,參考下面的代碼:

polls/views.py

from django.shortcuts import get_object_or_404


def detail(request, question_id):
    """
    測試Http404
    :param request:
    :param question_id:
    :return:
    """
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/detail.html", {"question": question})

  別說我沒提醒你,和render一樣,也需要從Django內置的快捷方式模塊中導出get_object_or_404()

  get_object_or_404()方法將一個Django模型作為第一個位置參數,後面可以跟上任意個數的關鍵字參數,如果對象不存在則彈出Http404錯誤。

  同樣,還有一個get_list_or_404()方法,和上面的get_object_or_404()類似,只不過是用來替代filter()函數,當查詢列表為空時彈出404錯誤。(filter是模型API中用來過濾查詢結果的函數,它的結果是一個列表集。而get則是查詢一個結果的方法,和filter是一個和多個的區別!)

五、 使用模板系統

  detail()視圖會將上下文變量question傳遞給對應的polls/templates/polls/detail.html模板,修改該模板的內容,如下所示:

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

  在模板系統中圓點.是萬能的魔法師,你可以用它訪問對象的屬性。在例子{{ question.question_text }}中,DJango首先會在question對象中嘗試查找一個字典,如果失敗,則嘗試查找屬性,如果再失敗,則嘗試作為列表的索引進行查詢。

  在 {% for %}循環中的方法調用——question.choice_set.all其實就是Python的代碼question.choice_set.all(),它將返回一組可叠代的Choice對象,並用在{% for %}標簽中。

  這裏我們對Django模板語言有個簡單的印象就好,更深入的介紹放在後面。

六、刪除模板中硬編碼的URLs

  在polls/index.html文件中,還有一部分硬編碼存在,也就是href裏的“/polls/”部分:

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

  它對於代碼修改非常不利。設想如果你在urls.py文件裏修改了正則表達式,那麽你所有的模板中對這個url的引用都需要修改,這是無法接受的!

  我們前面給urls定義了一個name別名,可以用它來解決這個問題。具體代碼如下:

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

  Django會在polls.urls文件中查找name=‘detail‘的url,具體的就是下面這行:

url(r^(?P<question_id>[0-9]+)/$, views.detail, name=detail),

  舉個栗子,如果你想將polls的detail視圖的URL更換為polls/specifics/12/,那麽你不需要在模板中重新修改url地址了,僅僅只需要在polls/urls.py文件中,將對應的正則表達式改成下面這樣的就行了,所有模板中對它的引用都會自動修改成新的鏈接:

# 添加新的單詞‘specifics‘
url(r^specifics/(?P<question_id>[0-9]+)/$, views.detail, name=detail),

七、URL names的命名空間

  本教程例子中,只有一個app也就是polls,但是在現實中很顯然會有5個、10個、更多的app同時存在一個項目中。Django是如何區分這些app之間的URL name呢?

  答案是使用URLconf的命名空間。在polls/urls.py文件的開頭部分,添加一個app_name的變量來指定該應用的命名空間:

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

app_name = polls  # 關鍵是這行
urlpatterns = [
    url(r^$, views.index, name=index),
    url(r^(?P<question_id>[0-9]+)/$, views.detail, name=detail),
    url(r^(?P<question_id>[0-9]+)/results/$, views.results, name=results),
    url(r^(?P<question_id>[0-9]+)/vote/$, views.vote, name=vote),
]

現在,讓我們將代碼修改得更嚴謹一點,將polls/templates/polls/index.html中的

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

修改為:

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

註意引用方法是冒號而不是圓點也不是斜杠!!!!!!!!!!!!

Part3:視圖和模板