close

Python Django 學習紀錄(三) 視圖與模板

 

 

一、Django的Framework架構:

Django是採用MTV架構,它和MVC架構類似,都是近年來大型專案開發的主流方式。

1.認識MVC:

採用MVC架構設計軟體系統會比較好維護、擴充、除錯、且分工明確

MVC是軟體設計的一 種架構,將軟體系統分為三個部分:

模型(Model):代表商業邏輯與資料庫存取有關的程式

視圖(View):代表輸入和輸出畫面的呈現

控制(Controller):介於Model和View之間,判斷該呼叫哪個Model存取資料庫必透過哪個View介面輸入表單或顯示資料

2.Django的MTV架構:

Django採用的MTV架構分為Model、Template、View

Model:代表商業邏輯與資料庫存取有關的程式

Template(.html):當作介面輸入表單或顯示資料,取代MVC的View

View:處理Model存取資料庫和Template介面資料的輸入或顯示,取代MVC

 

※MTV以Template模板(.html檔)輸入或輸出資料,在<views.py>檔定義函式,利用該函式透過Model存取資料庫,同時將資料以Template模板顯示

※在<views.py>函式取得的資料,必須傳給Template模板,最簡單的方式就是透過render函式,然後在Template模板顯示傳遞的資料

 

 

二、視圖和模板:

實例說明視圖(View)和模板(Template)

1.建立試圖和模板:

(1)定義url

在學習紀錄(二)已經使用過模板。透過<urls.py>檔定義url,瀏覽hello4/xxx/執行hello4自訂函數:

from django.contrib import admin
from django.urls import path

from django.conf.urls import url  #導入url套件
from myapp.views import hello4  #導入hello4自訂函數

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^hello4/(\w+)/$',hello4),
]

(2)定義View視圖

在<views.py>檔定自hello4自訂函數,以render函式呼叫模板,以locals()將所有區域變數傳遞給<hello4.html>模板:

from django.shortcuts import render
def hello4(request,username):
    now = datetime.now()             #取得現在日期時間
    return render(request,"hello4.html",locals())  #render(request傳遞GET或POST,模板名稱,傳遞所有區域變數)

(3)建立Template模板

<hello4.html>檔視MTV中的Template模板,建立在Template目錄中,內容如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>顯示圖片的模板</title>
    {% load staticfiles %}
    <link href = "{% static "css/style.css" %}" rel="stylesheet" type="text/css"/>
</head>
<body>
  <div id="home">
      <img src="{% static "images/potato.jpg" %}" alt="歡迎光臨" width="32" height="32" />
    <span class = "info">歡迎光臨: {{username}}</span>
    <h2>{{now}}</h2>
    </div>
</body>
</html>

 

MTV的model需要配合資料庫,之後會再作重點整理

2.傳遞變數到Template模板檔案:

在View視圖可以使用render函式傳遞字典給顯示的模板

傳遞字典到顯示模板:

使用render函式,必須先import該套件

第一個參數是HttpRequest物件

第二個參數是模板名稱

第三個參數是字典

from django.shortcuts import render
#render(request, template_name, context)

例如傳遞字典「{"potato":1}」給顯示模板<ivan.html>

render(request, "ivan.html", {"potato":1})

傳遞多筆字典「{"name":"ivan","age":22}」給顯示模板<ivan.html>

render(request, "ivan.html", {"name":"ivan","age":22})

 

3.範例實作:

建立一個名為passdata的專案,接著建立名為passdataapp的app,templates目錄、static目錄、migration資料檔,利用migrate將模型與資料庫同步。

15.JPG

記得接下來要完成<setting.py>的設置:

"""
Django settings for passdata project.

Generated by 'django-admin startproject' using Django 2.0.1.

For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'li)-4-ce72bvaqxb_+o=%0b#8g3!^7=q73)k1o8li166mb44l1'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'passdata'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'passdata.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'passdata.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'zh-Hant'

TIME_ZONE = 'Asia/Taipei'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [
  os.path.join(BASE_DIR, 'static'),
  ]


再來完成<urls.py>的定義:

會有「/dice/」執行 dice 函式、「/dice2/」執行 dice2 函式、「/dice3/」執行 dice3 函式、「/show/」執行 show 函式、「/filter/」執行 filter函式

這邊先把一些函式註解掉,之後要用到時再陸續加回來,以免稍後測試網頁會出現錯誤

from django.contrib import admin
from django.urls import path
from django.conf.urls import url 
from passdataapp.views import dice  #,dice2,dice3,show,filter
urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^dice/$', dice),
    # url(r'^dice2/$', dice2),
    # url(r'^dice3/$', dice3),
    # url(r'^show/$', show),
    # url(r'^filter/$', filter),
]

 

範例1:傳遞變數到Template模板

首先請打開passdataapp裡的<view.py>寫一個名為dice的自訂函數

接下來將使用 randint()函式,寫出一個擲骰子點數的程序

from django.shortcuts import render
import random

def dice(request):
    no=random.randint(1,6)
    return render(request,"dice.html",{"no":no})

接著在templates目錄中創建一個<dice.html>模板,以{{no}}顯示字典 

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>擲骰子</title>
</head>
<body>
    <h1>點數: {{no}}</h1>
</body>
</html>

打開瀏覽器,在網址列輸入127.0.0.1:8000/dice  即可看到剛剛寫的擲骰子網頁

16.JPG

 

※使用locals()函式

如果要傳遞的變數有很多,則可以在render函式中的第三個參數使用locals()函式

locals()函式可以將所有區域變數轉換成字典,這樣就能一次傳遞所有變數(global全域變數不會被處理)

以下將先做舉例說明: 

def example(request):
    no=1
    dict1={"name":ivan,"age":22}
    return render(request,"dice.html",locals())

經過locals()函式轉換後,會建立一個 {"no":1,"dict1":{"name":"ivan","age":22}} 的字典

※模板顯示變數(使用locals()函式)

在模板中要顯示locals()字典的內容,要需要用讀取字典的語法來讀取它

在模板中可以顯示接收的變數,必須以「變數」為鍵 

{{變數}}

 例如顯示 no

{{no}}

如果要顯示字典變數,前面要加上字典變數的名稱

{{字典變數.鍵}}

因此可以用下列語法取得name值的內容

{{dict1.name}}

如果要顯示串列變數,以「串列變數.索引」的格式取得

{{串列變數.索引}}

顯示list串列的第一個元素內容

{{list.0}}

 

範例2:使用locals()傳遞變數到Template模板 (一)

請打開passdataapp裡的<view.py>寫一個名為dice2的自訂函數

先使用 randint()函式,寫出三個擲骰子點數的程序

最後使用locals()函式傳遞所有區域變數

def dice2(request):
    no1=random.randint(1,6)
    no2=random.randint(1,6)
    no3=random.randint(1,6)
    return render(request,"dice2.html",locals())

接著在templates目錄中創建一個<dice2.html>模板,以{{no1}}、{{no2}}、{{no3}}顯示變數

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>擲骰子2</title>
</head>
<body>
    <h1>點數一: {{no1}}</h1>
    <h1>點數二: {{no2}}</h1>
    <h1>點數三: {{no3}}</h1>
</body>
</html>

最後記得在<views.py> import dice2 和在<urls.py>新增dice2的url路徑

最後網頁執行結果如下:

17.JPG

 

 

範例4:使用locals()傳遞變數到Template模板 (二)

請打開passdataapp裡的<view.py>寫一個名為times值為0的變數

接著自訂一個名為dice3的函數:

def dice3(request):
    global times #宣告全域變數
    times = times + 1 
    local_times = times #指派給區域變數
    username="Ivan"
    dict_no={"no":random.randint(1,6)}
    return render(request,"dice3.html",locals())

接著在templates目錄中創建一個<dice3.html>模板,以{{username}}、{{dict_no.no}}、{{local_times}}顯示變數

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>擲骰子3</title>
</head>
<body>
    <h1>玩家: {{username}}</h1>
    <h1>點數: {{dict_no.no}}</h1>
    <h1>累計次數: {{local_times}}</h1>
</body>
</html>

最後記得在<views.py> import dice3 和在<urls.py>新增dice3的url路徑

最後網頁執行結果如下:

18.JPG

 

三、Template語言:

Template模板有自己的語言,以下將做一些重點整理

Template模板組成

名稱 說明 範例

變量

 

將views內容傳送到模板上 {{username}}
標籤 if條件指令和for迴圈指令

{% if found %}

{% for item in items %}

單行註解

 

語法:{#註解文字#}

{#這是註解文字#}
多行註解

語法:{% comment %}

               .....

               .....

          {% endcomment %}

{% comment %}

          內容

          內容

 {% endcomment %}

文字

 

HTML標籤或文字 <title>這是標題</title>

1.變量

變量就是要顯示的變數,可以是一般的變數,或是字典、屬性、方法、串列

以下將有一個對照表:

名稱 Template語法 Python語法

字典

 

{{dict.name}} dict[name]
屬性 {{obj1.name}}

obj1.name

方法

 

{{obj1.show}}

obj1.show()
串列

{{list1.0}}

list1[0]

 

2.標籤

Template模板的條件判斷指令和迴圈指令稱為標籤,有if條件指令和for迴圈指令

(1)條件指令

分為單向、雙向、多向判斷式

單向判斷式:

{% if 條件 %}
  程式區塊
{% endif %}

雙向判斷式:

{% if 條件 %}
  程式區塊 
{% else %}
  程式區塊
{% endif %}

多向判斷式:

{% if 條件 %}
  程式區塊
{% elseif %}
  程式區塊
{% elseif %}
 程式區塊
{% else %}
  程式區塊
{% endif %}

 

(2)for迴圈指令

for迴圈可以依序讀取串列元素,並執行程式區塊。若串列無資料會執行 empty 區塊:

{% for 變數 in 串列 %}
  程式區塊
{% empty %}
  程式區塊
{% endfor %}

範例4:模板中接收串列資料並依序顯示

請打開passdataapp裡的<view.py>寫一個名為show的自訂函數:

將三個字典person1、person2、person3放進串列persons

def show(request):
    person1={"name":"Ivan","phone":"0911111111","age":22}
    person2={"name":"Clover","phone":"0922222222","age":20}
    person3={"name":"Victor","phone":"0933333333","age":21}
    persons=[person1,person2,person3]
    return render(request,"show.html",locals())

接著在templates目錄中創建一個<show.html>模板,以for迴圈顯示剛剛的資料

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>基本資料</title>
</head>
<body>
    <h2>
    {% for person in persons %}
    <ul>
        <li>姓名: {{person.name}}</li>
        <li>手機: {{person.phone}}</li>
        <li>年齡: {{person.age}}</li>
    </ul>
    {% empty%}
    沒有任何資料
    {% endfor %}
    </h2>
</body>
</html>

最後記得在<views.py> import show 和在<urls.py>新增show的url路徑

最後網頁執行結果如下:

19.JPG

 

(3)forloop變量及其屬性

Template的 for 迴圈提供 forloop 屬性,當作計數器

forloop屬性
forloop 說明
forloop.counter 由1開始遞增迭代總數
forloop.counter0 由0開始遞增迭代總數
forloop.revcounter 從串列元素總數開始遞減到1
forloop.revcounter0 從串列元素總數開始遞減到0
forloop.first 判斷是否為第一次for迴圈,值為True或False
forloop.last 判斷是否為最後一次for迴圈,值為True或False
forloop.parentloop 上一層迴圈的forloop

 

範例4延伸:模板中接收串列資料並依序顯示,由1開始顯示資料為第幾位員工

這邊增加了forloop屬性,記得語法為{{forloop屬性}}

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>基本資料</title>
</head>
<body>
    <h2>
    {% for person in persons %}
    第 {{ forloop.counter}} 位員工
    <ul>
        <li>姓名: {{person.name}}</li>
        <li>手機: {{person.phone}}</li>
        <li>年齡: {{person.age}}</li>
    </ul>
    {% empty%}
    沒有任何資料
    {% endfor %}
    </h2>
</body>
</html>

(4)for迴圈倒轉

將串列資料倒轉輸出有兩種方法:

  1. 在<views.py>視圖函式將取得的資料倒轉
  2. 在Template模板的for指令上加上reversed(例如:{% for person in persons reversed %})

20.JPG

※forloop屬性也要改為revcounter

3.過濾器

過濾器必須搭配變數一起使用,他使用「|」管線符號做處理

例如:cut可以移除變量中所有指定的字串{{values|cut:"2"}},則會把values字串中所有的2移除

過濾器種類實在太多了,需要參考的話可以直接點此連結參考官方的說明

接下來直接實行範例囉~

範例5:使用filter過濾器

請打開passdataapp裡的<view.py>寫一個名為filter的自訂函數:

def filter(request):
    value=3
    list1=[2,3,4]
    pw="生番是你"
    html="<h1>hello</h1>"
    value2=False
    return render(request,"filter.html",locals())

 

接著在templates目錄中創建一個<filter.html>模板,開始測試各種過濾器吧!

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>過濾器測試</title>
</head>
<body>
    <h3>value={{value}},value|add:"2"= {{value|add:"2"}} <br /> <!-- 將值增加2 -->
        value|stringformat:"E"= {{value|stringformat:"E"}} <br /> <!-- 以科學記號標示 -->
        list1={{list1}},list1|length ={{list1|length}} <br /> <!-- 顯示串列和串列長度 -->

        {% if pw == "生番是你"  %} <!-- 字串比對,==後面要空格-->
        密碼正確  <br />
        {% else %}
        密碼錯誤  <br />
        {% endif %}

        value2={{value2}},value2|yesno:"是,否,取消"= {{value2|yesno:"是,否,取消"}} <br /> <!-- 因為value2=False,所以會傳回第二個項目 -->
        html={{ html }},html|safe ={{ html|safe }} <!-- 以網頁格式顯示 -->
        
    </h3>
</body>
</html>

 

最後記得在<views.py> import filter 和在<urls.py>新增filter的url路徑

最後網頁執行結果如下:

21.JPG

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 ivankao 的頭像
    ivankao

    IvanKao的部落格

    ivankao 發表在 痞客邦 留言(1) 人氣()