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()函數,大家可以直接練習下。


前文回顧