-
Notifications
You must be signed in to change notification settings - Fork 1
Flask Web Development 03
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 如下:
完整的功能可以參考 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 %}
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 %}
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') }}