天天看點

民意調查Django實作(三)

版權聲明:您好,轉載請留下本人部落格的位址,謝謝 https://blog.csdn.net/hongbochen1223/article/details/50297547

我們接着第二小節的開始繼續我們的旅程。我們會繼續Web-poll應用,并且将會專注于建立公共接口 - “Views”。

哲學思想

一個視圖是你的Django應用中的一個Web頁面,它大體上服務一個特别的函數和一個特别的模闆。例如,在一個部落格應用中,你可能有下面的視圖:

  • 部落格首頁 - 展示了最近的條目
  • 條目詳細頁面 - 為一個單一條目的固定連結頁面
  • 基于年的檔案頁面 - 展示給定年份的所有月份的條目
  • 基于月的存檔頁面 - 展示給定月份的所有條目
  • 基于日的存檔頁面 - 展示給定日的所有的條目
  • 評論動作 - 處理對給定條目的釋出評論動作

在我們的poll應用中,我們會有下面4個視圖:

  • 問題索引頁面 - 展示最近的幾個問題
  • 問題詳細頁面 - 展示一個問題文本,沒有結果僅僅是一個投票的表格。
  • 問題結果頁面 - 展示特定問題的結果
  • 投票動作 - 處理特定問題的投票動作

在Django中,web頁面和其他内容是通過view被傳送的。每一個視圖是被一個簡單的Python函數(或者是方法)代表的。Django将會通過檢查要求的URL來選擇視圖(詳細來說,就是域名後面的位址部分)。

你可能會碰到例如 “ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B”這樣的位址。你将會愉快的了解到Django允許我們更多的URL模式。

一個URL模式就是一個URL的簡單的模型 - 例如:

/newsarchive/<year>/<month>/

為了能夠從一個網址到一個視圖,Django使用我們了解到的’URLconfs’。一個URLconfs映射URL模型(被描述成正規表達式)到視圖。

該小節提供了URLconfs使用的基本介紹。

編寫你的第一個視圖

讓我們編寫第一個視圖。打開檔案polls/views.py并且将下面的Python代碼添加進去:

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello,world.You are at the polls index.")
           

這個可能是在Django中的最簡單的視圖。為了調用這個視圖,我們需要去将他映射到一個URL中 - 并且我們需要一個URLconf。

為了在polls目錄中建立一個URLconf,建立一個檔案乘坐urls.py。你的app目錄看上去應該是這樣的:

polls目錄下:

├── admin.py
├── admin.pyc
├── __init__.py
├── __init__.pyc
├── migrations
│   ├── 0001_initial.py
│   ├── 0001_initial.pyc
│   ├── __init__.py
│   └── __init__.pyc
├── models.py
├── models.pyc
├── tests.py
├── urls.py
└── views.py
           

在polls/urls.py中包含下面的代碼:

from django.conf.urls import patterns,url

from polls import views

urlpatterns = patterns('',
    url(r'^$', views.index,name='index'),
)
           

下一步就是在polls.urls子產品中指出根URLconf。在mysite/urls.py中插入一個include(),就像是下面這樣的:

from django.conf.urls import patterns, include, url
from django.contrib import admin

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'mysite.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),
    url(r'^polls/',include('polls.urls')),
    url(r'^admin/', include(admin.site.urls)),
)
           

現在我們已經把index視圖連接配接到URLconf中。在浏覽器中打開連結

http://localhost:8000/polls

。并且你将會看到文本”Hello,world.You are at the polls index.”,這個也就是在你的index視圖中定義的。

url()函數被傳遞四個參數,兩個必須的:正則和視圖,還有兩個可選的:kwargs和name。在這個時候,回顧一下這些參數是幹啥的是值得的。

url()參數:regex

術語”regex”是一個常用的縮寫代表着”正規表達式”,這個是用于字元串比對的文法,或者是在這個例子中的url模式。Django在第一個正規表達式開始,比較所有的URL位址看是否比對,直到找到比對的位址。

注意這些正規表達式不會搜尋GET和POST參數,或者是域名。例如,在請求

http://www.example.com/myapp

中,URLconf就會去檢視myapp/。在請求

http://www.example.com/myapp/?page=3

中,URLconf也會查找myapp/。

在這裡我們不需要很專業的正規表達式的學習,隻是了解基本的模型就行。

最後,一個性能提醒:在URLconf子產品首次被加載的時候,這些正規表達式被編譯。他們運作是很快的。

url()參數:view

當Django找到一個正則比對的時候,Django就會調用特定的視圖函數,該函數帶有HttpRequest對象最為第一參數和其他從正規表達式中捕獲的值作為其他參數。如果regex使用簡單的捕獲,值就會作為位置參數傳遞。如果它私用名稱捕獲,值将會作為關鍵值參數傳遞。後面我們将會給出例子程式。

url()參數:kwargs

自由的關鍵字參數可以從一個目錄中傳遞到目标視圖中。在這裡我們不會使用這個特征。

url()參數:name

在Django的特定的模闆中給你的URL命名可以使你在任何地方來找到他。這個是非常強大的特征,它允許你僅僅建立一個單一的檔案來對你的項目的url模型做一個全局的改變。

編寫更多視圖

現在讓我們在polls/views.py中添加一個更多視圖。這些視圖稍微有點不同,因為他們采用一個參數:

def detail(request,question_id):
    return HttpResponse("You are looking at question %s." % question_id)

def results(request,question_id):
    response = "You are looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request,question_id):
    return HttpResponse("You are voting on question %s." % question_id)
           

對于這些新視圖,我們需要在polls/urls.py中添加索引:

from django.conf.urls import patterns,url

from polls import views

urlpatterns = patterns('',
    # ex: /polls/
    url(r'^$', views.index,name='index'),
    # ex: /polls/5/
    url(r'^(?P<question_id>\d+)/$',views.detail,name='detail'),
    # ex: /polls/5/results
    url(r'^(?P<question_id>\d+)/results/$',views.results,name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>\d+)/vote/$',views.vote,name='vote'),
)
           

我們打開我們的浏覽器看一下,輸入網址:

https://localhost:8000/polls/34/

。她就會運作detail()函數并且展示相應的内容,同樣,我們也可以測試一下别的函數:

我們來看一下:

檢視指定問題

檢視指定問題的結果

向指定問題投票

下面我們來看一下url的指定流程:

當有人從你的web站點中請求一個頁面的時候,也就是說這樣請求的時候,”polls/34/”,Django将會加載mysite.urls子產品,因為他指向ROOT_URLCONF設定。他找到變量名稱urlpatterns,并且按照順序轉換正規表達式。我們使用的include()函數指向其他URLconfs。注意,對于include()函數的正規表達式沒有$(end-of-string match charactor,字元串末尾比對字元),而是一個斜杠。當Django遇到include()的時候,他将相應的字元串傳到包含的URLconfs中以求進一步處理。

在include()背後的思想就是使插入和運作URL更加簡單。既然polls在他們自己的URLconfs(polls/urls.py)中,他們可以置于”/polls/”或者是置于”/fun_polls”或者是”/content/polls”,或者是任何其他根路徑上,app依然可以工作。

下面就是使用者進入/polls/34之後發生的事情:

  • Django将會找到比對’^polls/’
  • 然後,Django将會截取比對字元串前一部分(“polls/”),并且将剩下的文本 - “34/” - 發送到’polls.urls’中進行進一步處理,處理完成後,導緻調用detail()視圖:

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

編寫能夠真正做事情的視圖

每一個視圖都負責做兩件事情中的一件事情:傳回一個包含請求頁内容的HttpResponse對象,或者是發出一個異常例如Http404。剩下的事情取決于你。

你的視圖可以從資料庫中讀取記錄。他可以使用一個模闆系統例如Django的 - 第三方模闆系統 -。他可以生成一個PDF檔案,輸出XML,建立一個ZIP檔案或者是任何你想要的事情,這些都是使用那些Python庫來完成的。

所有的Django需求都是HttpResponse或者是一個異常。

由于他是友善的,是以讓我們來使用Django自身的資料庫API,我們在第一小節中講到過了。下面是index()視圖的一小部分,該部分用于展示系統中的最近的5條投票問題,根據公共日期由逗号分隔。

位置:polls/views.py

from django.http import HttpResponse

from polls.models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([p.question_text for p in latest_question_list])
    return HttpResponse(output)

# 其他函數沒有發生變化
           

這裡有一個問題:在視圖中的也頁面設計是寫死的。如果你想要去改變頁面的外觀的話,你必須去編輯Python代碼。是以,讓我們來使用Django的模闆系統,通過建立一個你可以使用的視圖模闆來将設計從Python代碼中分離開來。

首先,在你的polls目錄下面建立一個目錄稱為templates,Django将會在這裡搜尋模闆。

Django的TEMPLATE_LOADERS設定包含一系列的知道如何從各種源碼中引入模闆的調用。預設的一個就是django.template.loaders.app_directories.Loader,它會在每一個INSTALL_APPS中搜尋”templates”子目錄 - 這就是Django如何找到polls模闆的,即使我們沒有修改TEMPLATE_DIRS設定。

在我們剛剛建立的templates目錄中,建立另外一個目錄稱作polls,并且在polls中建立一個檔案稱作index.html。也就是說你的模闆應該是polls/templates/polls/index.html。由于剛剛我們已經了解到app_directories模闆如何加載的,是以我們就可以在Django中通過polls/index.html指向該模闆。

我們将下面的代碼加到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視圖:

from django.http import HttpResponse
from django.template import RequestContext, loader

from polls.models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = RequestContext(request, {
        'latest_question_list': latest_question_list,
    })
    return HttpResponse(template.render(context))

           

該代碼加載稱為polls/index.html的模闆,并且将他傳遞給context。context是一個字典,該字典講模闆的變量名稱映射到Python對象中。

在浏覽器中輸入:

http://localhost:8000/polls/

來加載頁面,之前我又手動添加了一個問題,是以我的顯示是這樣的:

縮寫:render()

加載模闆,填充一個context并且傳回一個帶有結果的HttpResponse對象是常用的方式。Django提供了一個快捷方式,下面是index()視圖的快捷方式實作辦法:

from django.http import HttpResponse
from django.shortcuts import render

from polls.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,RequestContext了。

render()函數采用request對象作為他的第一個參數,模闆的名稱作為第二個參數,并且字典作為可選參數。他傳回一個帶有給定context的給定模闆渲染的HttpResponse對象。

指出404錯誤

現在,我們來解決一下有問題的視圖 - 對給定poll顯示question文本的頁面。下面是視圖:

from django.http import Http404
from django.shortcuts import render

from polls.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})
           

新的概念是這樣的:如果一個帶有請求ID的問題不存在的話,視圖就會提出Http404異常。

後面我們會詳細的介紹在polls/detail.html中應該放什麼,這裡我們使用一個較快的方式來使他工作。

polls/templates/polls/detail.html

{{ question }}
           

快捷方式:get_object_or_404()

Django為我們提供了一個快捷方式,我們來重寫一下:

from django.shortcuts import get_object_or_404, render

from polls.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})
           

下面我們來看一下運作結果:

使用模闆系統

現在我們回到detail()視圖。給定上下文變量question,下面是我們的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代碼,他傳回Choice對象的疊代,并且在{% for %}中使用是合适的。

在模闆中移除URL寫死

記住,當我們在polls/index.html模闆中編寫question的連結的時候,連結也是寫死的:

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

當在一個項目中有很多模闆的時候,我們如果改變URL的話将會面臨一個挑戰。然而,既然你在polls.urls的url()函數中定義了名稱參數,你可以通過使用{% url %}模闆标簽來移除在你的url配置中特定URL路徑的依賴。

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

這種工作的方式是在polls.urls中搜尋URL的定義。你可以非常精确的看到’detail’的URL名稱是在哪裡定義的。

...
# the 'name' value as called by the {% url %} template tag
url(r'^(?P<question_id>\d+)/$', views.detail, name='detail'),
...
           

如果你想要去改變poll的detail視圖的URL,或許是polls/specifics/12/而不是原來的模闆,你可能會在polls/urls.py中修改他。

...
# added the word 'specifics'
url(r'^specifics/(?P<question_id>\d+)/$', views.detail, name='detail'),
...
           

URL名稱命名空間

在我們這個項目中,僅僅有一個app,polls。在實際Django項目中,可能有5個,10個,20個或更多app。那麼Django如何區分URL名稱呢?例如,polls app有一個detail視圖,并且在同一個項目上的另外一個app也有一個detail視圖。那麼Django如何知道哪一個app視圖呢?

答案就是為你的根URLconf添加一個命名空間。在mysite/urls.py檔案中,修改他去包含一個命名空間。

from django.conf.urls import patterns, include, url
from django.contrib import admin

urlpatterns = patterns('',
    url(r'^polls/', include('polls.urls', namespace="polls")),
    url(r'^admin/', include(admin.site.urls)),
)
           

現在修改你的polls/index.html模闆:

原來是這樣的:

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

現在加上命名空間:

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