Skip to content

Flask Web Development 03

changwu edited this page Mar 17, 2016 · 2 revisions

第三章

模版

p.41

view functions 一般有兩個執行目的, 但若邏輯交雜一起, 代碼會變得不好維護與理解

  • business logic
  • presentation logic

template 是一個檔案, 包含替換變數 placeholder variable, 動態的改變內容, 回傳後的結果稱作 rendering, flask 採取 Jinja2 的 template engine.

$ git checkout 3a

預設模版會放在 application 的 templates 資料夾下, 現在將 response 置於模版中

  • index() --> index.html
  • user(name) --> user.html

templates/index.html

<h1>Hello World!</h1>

templates/user.html

<h1>Hello, {{ name }}!</h1>

除此之外, 還要修改 hello.py 中的 view function, 讓結果能 render 到 template

from flask import Flask, render_template
from flask.ext.script import Manager

app = Flask(__name__)

manager = Manager(app)


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/user/<name>')
def user(name):
    return render_template('user.html', name=name)


if __name__ == '__main__':
    manager.run()

參數傳遞中 name=name 容易造成混淆

  • 左邊的 name 是 template 中的變數
  • 右邊的 name 是 目前函數的區域變數

Jinja2 可辨識不同的變數型態

<p>A value from a dictionary: {{ mydict['key'] }}.</p>
<p>A value from a list: {{ mylist[3] }}.</p>
<p>A value from a list, with a variable index: {{ mylist[myintvar] }}.</p>
<p>A value from an object's method: {{ myobj.somemethod() }}.</p>

另外, 變數能被 filter 修改, 舉例如下:

Hello, {{ name|capitalize }}

name 變數的首字母會自動大寫

Jinja2 支援的 filter 如下:

filter

完整的功能可以參考 Jinja2 的官方文件

Control Structures

Conditional statements

{% if user %}
    Hello, {{ user }}!
{% else %}
    Hello, Stranger!
{% endif %}

for loop

<ul>
    {% for comment in comments %}
        <li>{{ comment }}</li>
    {% endfor %}
</ul>

也支援 macros

{% macro render_comment(comment) %}
    <li>{{ comment }}</li>
{% endmacro %}

<ul>
    {% for comment in comments %}
        {{ render_comment(comment) }}
    {% endfor %}
</ul>

為了讓 macros 可複用, 可存在單一檔案中, 需要時再 import 進來

{% import 'macros.html' as macros %}
<ul>
    {% for comment in comments %}
        {{ macros.render_comment(comment) }}
    {% endfor %}
</ul>

通常會把較常用的置於一個檔案

{% include 'common.html' %}

Template inheritance

base.html

<html>
    <head>
        {% block head %}
        <title>{% block title %}{% endblock %} - My Application</title>
        {% endblock %}
    </head>
    <body>
        {% block body %}
        {% endblock %}
    </body>
</html>

extends 宣告表示下列的 template 是繼承自 base.html

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style>
    </style>
{% endblock %}
{% block body %}
<h1>Hello, World!</h1>
{% endblock %}

Bootstrap 是由 Twitter 提供的框架, 提供介面元件, 若要整合 Bootstrap 到 flask, 可安裝下列插件

$ pip install flask-bootstrap
$ git checkout 3b

hello.py, 初始化 bootstrap

from flask.ext.bootstrap import Bootstrap

# ...
bootstrap = Bootstrap(app)

新的 user.html

{% extends "bootstrap/base.html" %}

{% block title %}Flasky{% endblock %}

{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">Flasky</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="/">Home</a></li>
            </ul>
        </div>
    </div>
</div>
{% endblock %}

{% block content %}
<div class="container">
    <div class="page-header">
        <h1>Hello, {{ name }}!</h1>
    </div>
</div>
{% endblock %}

bootstrap

Flask-Bootstrap’s base template blocks

Flask-Bootstrap’s base template blocks

如果想加入自己的內容到 block 中

{% block scripts %}
    {{ super() }}
    <script type="text/javascript" src="my-script.js"></script>
{% endblock %}

Custom Error Pages

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
    return render_template('500.html'), 500

除了 bootstrap/base.html, 可自定義 base.html, 作為其他頁面的基底

{% extends "bootstrap/base.html" %}

{% block title %}Flasky{% endblock %}

{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">Flasky</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="/">Home</a></li>
            </ul>
        </div>
    </div>
</div>
{% endblock %}

{% block content %}
<div class="container">
    {% block page_content %}{% endblock %}
</div>
{% endblock %}

404.html

{% extends "base.html" %}

{% block title %}Flasky - Page Not Found{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Not Found</h1>
</div>
{% endblock %}

505.html

{% extends "base.html" %}

{% block title %}Flasky - Internal Server Error{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Internal Server Error</h1>
</div>
{% endblock %}

404

Links

在 template 中如何包含 dynamic route, 如果採絕對位址, 當檔案架構改變時, url 也會變, flask 提供 url_for() 的 helper function

  • call url_for('index') 會回傳 /.
  • url_for('index', _external=True) 會回傳絕對位址 http://localhost:5000/
  • url_for('index', page=2) 會回傳 /?page=2

Static Files

靜態檔案在 flask 中預設位置為 /static/<filename>

例如: url_for('static', filename='css/styles.css', _external=True) 會回傳 http://localhost:5000/static/css/styles.css

預設 flask 會去 application 的根目錄尋找 static 目錄, 下例為加入 favicon.ico

{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
{% endblock %}

super() 會保留原來 base.html 中 head 裡的項目

Localization of Dates and Times with Flask-Moment

安裝 flask-moment 套件

$ pip install flask-moment
$ git checkout 3e

初始化 flask-Moment

from flask.ext.moment import Moment
moment = Moment(app)

flask-moment 依賴 jquery.js 以及 moment.js, 所以必須引入 js 套件, 由於 bootstrap 已經引入 jquery.js, 故只要引入 moment.js 即可

base.html

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}

hello.py

from datetime import datetime

@app.route('/')
def index():
    return render_template('index.html',
                           current_time=datetime.utcnow())

index.html

<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}.</p>

format('LLL') 表示依據現在 client 端的時區(time zone)及語系(locale)

有關 moment.js 的文件

timestamp 可被轉成各種語言, 亦可透過 lang 來設定

{{ moment.lang('es') }}
Clone this wiki locally