Django — 視圖是啥?模板是何物?
- 2020 年 1 月 20 日
- 筆記
前言
在Django的官方文檔中是這麼定義視圖的: "一類具有相同功能和模板的網頁的集合",概念比較抽象,我們直接 拿比較簡單常見論壇網站來舉例,可能要求創建以下視圖:
- 論壇首頁 — 列表頁面,由時間先後倒敘展示新建的帖子
- 帖子詳情 — 詳細展示帖子內容
- 評論處理 — 為帖子增加評論的操作 在Django 中,系統所展示的頁面和其他內容都是由視圖派生而來,每一個視圖可以表現為一個簡單的Python函數,如果是基於類的視圖的,則是對應的類中的一個方法。(以上內容也許現在難以理解,耐心看下去就會豁然開朗,^_^) Django 將會根據用戶請求的URL來選擇使用哪個視圖。
在Django中,千萬不要把視圖理解為前端展示給我們的頁面,請大家回顧下Django的MTV 模式。
編寫視圖
我們在第二篇公眾號中已經演示了最簡單視圖,在 demo_app/views.py
中
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. This is your first Django")
根據前面的學習我們得知,當我們在瀏覽器中輸入 http://localhost:8000/demo_app/
時,Django 的路由規則會將這個請求分配到這個 index
這個視圖上進行處理,那如果我們輸入的url帶有參數(如帶參數的Get請求)那我們的視圖該如何處理呢?看看下面的實例
from django.http import HttpResponse * 不帶參數的視圖 def index(request): return HttpResponse("Hello, world. This is your first Django") * 帶參數的視圖 def hello_country(request, country_name): return HttpResponse("Hello," % country_name)
在 demo_app/views.py
中新增 hello_country
的配置新的路由規則:
from django.urls import path from . import views # . 表示在同級父目錄下 urlpatterns = [ path('', views.index, name='index'), path('hello/<str:country_name>/', views.hello_country, name='hello'), ] str,匹配除了路徑分隔符(/)之外的非空字元串,這是默認的形式 int,匹配正整數,包含 0。 slug,匹配字母、數字以及橫杠、下劃線組成的字元串。 uuid,匹配格式化的 uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。 path,匹配任何非空字元串,包含了路徑分隔符
當瀏覽器輸入 http://localhost:8003/demo_app/hello/china
時,我們新配置的路由規則發揮作用,決定過了 demo_app/views.py
中的 hello_country
視圖來處理該請求,那我們就會看到頁面上返回

模板
在視圖中你可以操作資料庫,可以使用模板引擎(Django自帶的,或者第三方的)等,Django 對視圖的要求是返回一個 HttpResponse
,或者拋出異常。我們現在就來演示下Django 如何使用模板系統。
我們通過登錄到後台管理系統新建幾個Cat對象(也可通過Django自帶的資料庫API),我們將在視圖中通過模板使用他們。
首先我們在 demo_app
目錄下新建templates目錄,Django將會在這個目錄里查找模板文件。在Django的配置文件中默認設置了 APP_DIRS
為True,這一選項會讓Django在每個 INSTALLED_APPS
文件夾中尋找 templates
子目錄。
在剛新建的 templates
目錄里再新建一個目錄 demo_app
,然後在其中新建 index.html
文件。我們在這推薦使用這樣的目錄結構,雖然我們也可以個把 index.html
文件建在 demo_apptemplates
目錄中。 index.html
新建後目錄結構如下:
demo_proj/ manage.py demo_proj/ __init__.py settings.py urls.py wsgi.py demo_app migrations templates demo_app index.html __init__.py admin.py apps.py models.py test.py views.py
在 demo_app/tempaltes/demp_app/index.html
文件中輸入如下程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Cat List</title> </head> <body> <table border="1"> <tr> <td>Name</td> <td>age</td> </tr> {% if cats_list %} {% for cat in cats_list %} <tr> <td>{{ cat.name }}</td> <td>{{ cat.age }}</td> </tr> {% endfor %} {% else %} <p>No cat</p> {% endif %} </table> </body> </html>
我們更新 demo_app/views.py
文件來調用我們剛才建立的模板
def index(request): cats_list = Cat.objects.all() from django.template import loader template = loader.get_template('demo_app/index.html') context = { 'cats_list': cats_list, } return HttpResponse(template.render(context, request))
上面程式碼的作用,就是載入 demo_app/index.html
模板文件,並向該模板傳遞一個context,這個上下文是個字典,可以理解為 將Python對象 cats_list
映射成 模板內的變數 cats_list
,所以,我們在模板中能夠引用變數(類似於 {%ifcats_list%}
這樣的格式{{ xxx }}).
用瀏覽器 訪問 http://127.0.0.1:8003/demo_app/
,將會看到一個表格,列出了我們新建的幾個Cat對象.

回顧下剛才views.py中使用模板的實現過程,我們先載入模板,再填充上下文,最後返回由它生成的 HttpResponse
對象,略顯繁瑣,Django提供了render() 函數來簡化這個流程,我們重新更新 demo_app/views.py
文件
def index(request): cats_list = Cat.objects.all() context = { 'cats_list': cats_list, } return render(request,'demp_app/index.html',context)
重新用瀏覽器 訪問 http://127.0.0.1:8003/demo_app/
,會返回同樣的結果。
模板系統
回顧下我們的index()視圖,它向模板傳遞了一個 cats_list
變數,我們再來看看 index.html
模板中是怎麼使用變數的。
{% if cats_list %} {% for cat in cats_list %} <tr> <td>{{ cat.name }}</td> <td>{{ cat.age }}</td> </tr> {% endfor %} {% else %} <p>No cat</p> {% endif %}
在模板系統中,我們引用變數的屬性都用 {{xx}}
, 如 {{cat.name}}
, {{cat.age}}
。 一些 判斷,循環也有固定的格式:
- For 循環
{% for xx in xxx %} ... ... ... {% end for %}
- If 判斷 (else 看實際情況)
{% if xxxxx %} {% else %} {% endif %}
- URL 路徑 一般當列表頁展示時,我們通常可以通過點擊其中的一條記錄進入到該記錄的詳情頁(detail)。 這邊我們假設程式中 detail 頁面的視圖,模板,路由配置都以配置好,當地址輸入
http://xxxxx/demo_app/x
時進入id=x 的詳情頁面。其路由配置為
from django.urls import path from . import views # . 表示在同級父目錄下 urlpatterns = [ path('', views.index, name='index'), path('hello/<str:country_name>/', views.hello_country, name='hello'), path('<int:id>/', views.detail, name='detail'), ]
我們將 index.html
改造下,讓其顯示id,並且id 為超鏈接
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Cat List</title> </head> <body> <table border="1"> <tr> <td>Id</td> <td>Name</td> <td>age</td> </tr> {% if cats_list %} {% for cat in cats_list %} <tr> <td><a href = "{% url 'demo_app:dtail' cat.id %}">{{ cat.id }}</a></td> <td>{{ cat.name }}</td> <td>{{ cat.age }}</td> </tr> {% endfor %} {% else %} <p>No cat</p> {% endif %} </table> </body> </html>
以前 href 中的網址 被 {%url'demo_app:detail'cat.id%}
所代替。注意其語法格式:
demo_app:detail
是指 在demo_app
應用的 urls.py 文件中name=detail
的路由規則。cat.id
則是傳入的參數 以空格隔開。
模板系統當然還有其他的特定用法,我們會在後面的課程中陸續接觸。
拋出異常
我們最後來處理下拋出異常的情況,那我們的detail視圖來舉例 在 demo_app/views.py
添加如下程式碼
from django.http import Http404 def detail(request, id): try: cat = Cat.objects.get(id = id) except Cat.DoesNotExist: raise Http404("Cat does not exist") return render(request, 'demo_app/detail.html',{'cat':cat})
根據id 獲取具體的cat 對象,如果對象不存在,則拋出404異常 。否則,把獲取的cat對象作為上下文傳遞給 detail.hmtl
。程式碼邏輯很清楚,但Django 還是提供了一個更為簡單的函數 get_object_or_404()
from django.shortcuts import render,get_object_or_404() def detail(request, id): cat = get_object_or_404(Cat, id=id) return render(request, 'demo_app/detail.html',{'cat':cat})
get_object_or_404()
返回具體對象或者拋出404異常,類似的也有 get_list_or_404()
函數,大家可以直接練習下。