Flask實戰-留言板-使用Bootstrap-Flask簡化頁面編寫 --

使用Bootstrap-Flask簡化頁面編寫

擴展Bootstrap-Flask內置了能夠快速渲染Bootstrap樣式HTML組件的宏,並提供了內置的Bootstap資源,方便快速開發,使用它能夠簡化在web程序裏使用Bootstrap的過程。javascript

 

擴展Bootstrap-Flask基於Flask-Bootstrap實現,旨在替代缺少維護的後者。和Flask-Bootstrap相比,Bootstrap-Flask簡化了大部分功能(好比未內置基模板),添加了Bootstrap4支持,並增長了一些輔助功能。css

 

安裝Bootstrap-Flask經過pipenv install bootstrap-flask

 

須要注意,Bootstrap-Flask提供的包名稱爲flask_bootstrap,咱們從這個包導入並實例化Bootstrap類,傳入程序實例app,以完成擴展的初始化:
from flask import Flask from flask_bootstrap import Bootstrap app = Flask(__name__) bootstrap = Bootstrap(app)
加載資源文件
Bootstrap-Flask在模板中提供了一個bootstrap對象,這個對象提供了兩個方法能夠用來生成資源引用代碼:用來加載CSS文件的bootstrap.load_css()方法和用來加載JavaScript文件(包括Bootstrap、jQuery、Popper.js)的bootstrap_load_js()方法。 Flask-Bootstrap默認從CDN(content Delivery Network,內容分發網絡)加載Bootstrap資源,同時也提供了內置的本地資源。若是你想用Bootstrap-Flask提供的本地資源,能夠將配置變量BOOTSTRAP_SERVER_LOCAL設爲True。另外,當FLASK_ENV環境變量設爲development時,Bootstrap-Flask將自動使用本地資源。
儘管使用這些方法很是方便,但咱們最好在開發時自動手動管理本地靜態資源。messageBoard的static目錄下包含了全部須要的資源文件,基模板中的資源文件都從static文件夾中引入。
若是想使用Bootstrap-Flask提供的方法加載資源,那麼只須要在相應的位置分別調用資源加載方法,替換掉這些對應的資源加載語句便可:
<head> {{ bootstrap.load_css() }} </head>
<body> {{ bootstrap.load_js() }} </body>
另外,在bootstrap_load_js()方法中,使用with_jquery和with_popper能夠設置是否加載jQuery和Popper.js的JavaScript資源,默認爲True,設爲False能夠關閉。
快捷渲染表單
Bootstrap-Flask內置了兩個用於渲染WTForms表單類的宏,一個是與咱們以前建立的form_field宏相似的render_field()宏,另外一個是用來快速渲染整個表單的render_form()宏。這兩個宏都會自動渲染錯誤消息,渲染表單的驗證狀態樣式。
Bootstrap-Flask提供的表單渲染宏經過其內置的bootstrap/form.html模板導入,render_field()宏的使用方式和咱們本身編寫的form_field()宏徹底相同。值得特別介紹的是render_form()宏,它使用起來更加簡單,使用一行代碼能夠渲染整個表單,並且會自動幫咱們渲染CSRF令牌字段form.csrf_token。下面使用這個宏在index.html模板中渲染問候表單:
{% extends 'base.html' %} {% from 'bootstrap/form.html' import render_form %} {% block contetn %} <div class="hello-form"> {{ render_form(form), action=request.full_path }} </div> {% endblock %}

 

它將會自動爲你建立一個<form>標籤,而後在標籤內一次渲染包括CSRF令牌在內的全部字段。除了渲染表單字段,它還會根據表單的驗證狀態來渲染表單狀態和錯誤消息。通常狀況下,你只須要傳入表單類實例做爲參數。除此以外,quick_form()宏還支持許多參數來自定義表單,經常使用的參數及說明以下表:html

render_form()宏經常使用參數:java

 

包括用來渲染表單的render_field()和render_form()宏在內,Bootstrap-Flask還提供了許多其餘用途的宏,這些宏均可以經過bootstrap目錄下的模板導入,經常使用的Bootstrap-Flask宏如表:jquery

 

使用Flask-Moment本地化日期和時間

在Message類中,咱們存儲時間戳時使用的是datatime模塊的datetime.now()方法生成的datetime對象,它是一個本地時間。具體來講,這個方法會返回服務器(也就是運行程序的計算機)設置的時區所對應的時間。對於測試來講這足夠了,若是要把程序部署到真正的服務器上,就可能會面臨時區問題。好比,我把程序部署到美國的服務器上,那麼這個時間將再也不是咱們期待的東八區時間,而是服務器本地的美國時間。另外一方面,若是咱們的程序被其餘時區的人訪問,他們更但願看到本身所在時區的時間,而不是固定的東八區時間。git

 

本地化前的準備

如何讓世界各地的用戶訪問程序時都能看到本身的本地時間呢?一個簡單的方法是使用JavaScript庫在客戶端(瀏覽器)中進行時間的轉換,由於瀏覽器能夠獲取到用戶瀏覽器/電腦上的時區設置信息。github

爲了可以在客戶端進行時間的轉換,咱們須要在服務器端提供更純正的時間(naive time),即不包含時區信息的時間戳(與之相對,包含時區的時間戳被稱爲細緻的時間,即aware time)。datetime模塊的datetime.utcnow()方法用來生成當前的UTC(Coordinated Universal Time, 協調世界時間),而UTC格式時間就是不包含時區信息的純正時間。咱們將使用它在時間戳字段上替代以前的datetime.now方法,做爲時間戳timestamp字段的默認值:web

from datetime import datetime class Message(db.Model): timestamp = db.Column(db.DateTime, default=datetime.utcnow)

 

使用Flask-Moment集成Moment.js

Moment.js是一個用於處理時間和日期的開源JavaScript庫,它能夠對時間和日期進行各類方式的處理。它會根據用戶電腦上的時區設置在客戶端使用JavaScript來渲染時間和日期,另外還提供了豐富的時間渲染格式支持。sql

擴展Flask-Moment簡化了在Flask項目中使用Moment.js的過程,集成了經常使用的時間和日期處理函數。首先使用pipenv安裝:flask

pipenv install flask-moment

 

 

而後咱們實例化擴展提供的Moment類,並傳入程序實例app,以完成擴展的初始化:

from flask_moment import Moment app = Flask(__name__) moment = Moment(app)
 

爲了使用Moment.js,咱們須要在基模板中加入Moment.js資源。Flask-Moment在模板中提供了moment對象,這個對象提供了兩個方法來加載資源:moment.include_moment()方法用來加載Moment.js的JavaScript資源;moment.include_jquery()用來加載jQuery。這兩個方法默認從CDN加載資源,傳入local_js參數能夠指定本地資源URL。

 

咱們在使用Bootstrap時已經加載了jQuery,這裏只須要加載Moment.js的JavaScript文件。

爲了更好的管理資源,咱們將在程序中手動加載資源。首先訪問moment.js官網下載相應的資源文件到static文件夾中:moment-with-locales.min.js,而後在基模板中引入。由於moment.include_moment()會用來生成執行時間渲染的JavaScript函數,因此咱們必須調用它,能夠經過local_js參數傳入本地資源的URL,若是不傳入這個參數則會從CDN加載資源:

 

{{ moment.include_moment(local_js=url_for('static', filename='js/moment-with-locales.min.js')) }}

 

Moment.js官網提供的文件moment.min.js僅包含英文語言的 時間日期字符,若是要使用其餘語言,須要下載moment-with-locales.min.js。

 

Flask-Moment默認以英文顯示時間,咱們能夠傳入區域字符串到locale()方法來更改顯示語言,下面在base.html中將語言設爲簡體中文:

… {{ moment.locale('zh-cn') }} </body>

 

在Moment.js中,簡體中文的地區字符串爲「zh-cn」,中國香港繁體中文和中國臺灣繁體中文,則分別使用「zh-hk」和「zh-tw」。

除了使用locale參數固定地區,更合理的方式是根據用戶瀏覽器或計算機的語言來設置語言,咱們能夠在locale()方法中將auto_detect參數設爲True,這會自動探測客戶端語言設置並選擇合適的區域設置:base.html

… {{ moment.locale(auto_detect=True) }} </body>

 

渲染時間日期

Moment.js提供了很是豐富、靈活的時間日期格式化方式。在模板中,咱們能夠經過moment類調用format()方法來格式化時間和日期,moment的構造方法接收使用utcnow()方法建立的datetime對象做爲參數,即Message對象的timestamp屬性。format()方法接收特定的格式字符串來渲染時間格式,好比:

{{ moment(timestamp).format(‘格式字符串’) }}

 

時間日期會在頁面加載完成後執行JavaScript函數使用Moment.js渲染,因此時間日期的顯示會有微小的延遲。

 

Moment.js提供了一些內置的格式字符串,字符串及對應的中文輸出實例以下:

 

咱們也能夠經過Moment.js支持的時間單位參數自定義時間輸出,好比使用格式字符串「YYYYMMMMDo, ah: mm: ss」將會獲得輸出:2019四月14日,早上9:00:00。

 

除了輸出普通的時間日期,Moment.js還支持輸出相對時間。好比相對於當前時間的「三分鐘前」,「一個月前」等。這經過formNow()方法實現,在原做者新版本的示例中,時間戳就使用這個函數渲染:

<small>{{ moment(message.timestamp).fromNow(refresh=True) }}</small>

 

將refresh參數設爲True(默認爲False)可讓時間戳在不重載頁面的狀況下,隨着時間的變化自動刷新。若是在頁面上等待一下子,就會看到時間戳從「幾秒前」變成了「1分鐘前」。

 

Flask-Moment實現了Moment.js的formt()、fromNow()、fromTime()、calendar()、valueof()和unix()方法,

 

有些時候,使用Flask-Moment提供的方法還不夠靈活,這時能夠手動使用Moment.js渲染時間日期。好比,當鼠標懸停在問候消息的時間日期上時,咱們但願可以顯示一個包含具體的絕對時間的彈出窗口(tooltip)。

 

爲了可以在JavaScript中使用Moment.js渲染時間日期,咱們須要在顯示相對時間的HTML元素中建立一個data-timestamp屬性存儲原始的時間戳,以便在JavaScript中獲取:

index.html:

<small data-toggle="tooltip" data-placemoment="top" data-timestamp="{{ message.timestamp.strftime('%Y-%m-%dT%H:%M:%SZ') }}" data-delay="500"> {{ moment(message.timestamp).fromNow(refresh=True) }} </small>

 

爲了讓時間戳可以正常被Moment.js解析,咱們須要使用strftime()方法對原始的時間字符串按照ISO8061標準進行格式化處理。

咱們在script.js腳本存儲JavaScript代碼,下面的JavaScript代碼將時間日期對應元素的tooltip內容設置爲渲染後的時間日期:

$(function){ function render_time(){ return moment($(this).data('timestamp')).format('lll') } $('[data-toggle="tooltip"]').tooltip( {title: render_time} ); };

 

data-toggle指以什麼事件觸發,經常使用的如modal,popover,tooltips等

在Bootstrap中,Tooltip組件須要調用tooltip()方法進行初始化。咱們使用data-toggle屬性做爲選擇器選擇全部設置了tooltip的元素,對其調用tooltip()方法。在調用這個方式時,能夠傳入一些選項,如title選項用來設置彈出的內容,能夠是字符串也能夠是函數對象。

 

在渲染時間日期的render_time()函數中,渲染時間日期使用的moment()函數是由Moment.js提供的,而不是Flask-Moment傳入模板的類。$(this).data(‘timestamp’)獲取了對應元素的data-timestamp屬性值,特殊變量this表示當前觸發事件的元素對象。如今,當鼠標懸停在時間戳上時,會彈出包含具體時間的小窗口,以下所示:

 

在Bootstrap中,Popover和Tooltip組件依賴於JavaScript包Popper.js,要使用這兩個組件,需確保在及模板中加載了對應的JavaScript文件。做爲替代,你也能夠加載Bootstrap提供的合集包文件bootstrap.bundle.min.css。

 

最終的代碼:

messageBoard/messageBoard/__init__.py:

 

#encoding=utf-8

from flask import Flask from flask_bootstrap import Bootstrap from flask_moment import Moment from flask_sqlalchemy import SQLAlchemy app = Flask('messageBoard') app.config.from_pyfile('settings.py') app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True db = SQLAlchemy(app) bootstrap = Bootstrap(app) moment = Moment(app) from messageBoard import views, errors, commands
 
 
messageBoard/base.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>{% block title %}How you are doing ?{% endblock %}</title>
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}" type="text/css">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css">
</head>
<body>
<main class="container">
    <header>
        <h1 class="text-center display-4">
            <a href="{{ url_for('index') }}" class="text-success"><strong>Leave Message</strong></a>
            <small class="text-muted sub-title">to the world</small>
        </h1>
    </header> {% for message in get_flashed_messages() %} <div class="alert alert-info">
        <button type="button" class="close" data-dismiss="alert">&times;</button> {{ message }} </div> {% endfor %} {% block content %}{% endblock %} <footer class="text-center"> {% block footer %} <small> &copy; 2019 <a href="https://www.cnblogs.com/xiaxiaoxu/" title="xiaxiaoxu's blog">夏曉旭的博客</a> / <a href="https://github.com/xiaxiaoxu/hybridDrivenTestFramework" title="Contact me on GitHub">GitHub</a> / <a href="http://helloflask.com" title="A HelloFlask project">Learning from GreyLi's HelloFlask</a>
            </small>
            <p><a id="bottom" href="#" title="Go Top">&uarr;</a></p> {% endblock %} </footer>
</main>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.2.1.slim.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/popper.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/script.js') }}"></script> {{ moment.include_moment(local_js=url_for('static', filename="js/moment-with-locales.min.js")) }} </body>
</html>

 

messageBoard/index.html
{% extends 'base.html' %} {% from 'bootstrap/form.html' import render_form %} {% block content %} <div class="hello-form"> {{ render_form(form, action=request.full_path) }} </div>
    <h5>{{ messages|length }} messages <small class="float-right">
            <a href="#bottom" title="Go Bottom">&darr;</a>
        </small>
    </h5>
    <div class="list-group"> {% for message in messages %} <a class="list-group-item list-group-item-action flex-column">
                <div class="d-flex w-100 justify-content-between">
                    <h5 class="mb-1 text-success">{{ message.name }} <small class="text-muted"> #{{ loop.revindex }}</small>
                    </h5>
                    <small data-toggle="tooltip" data-placement="top" data-timestamp="{{ message.timestamp.strftime('%Y-%m-%dT%H:%M:%SZ') }}" data-delay="500"> {{ moment(message.timestamp).fromNow(refresh=True) }} </small>
                </div>
                <p class="mb-1">{{ message.body }}</p>
            </a> {% endfor %} </div> {% endblock %}
 
 
遺留問題:moment修飾的時間,不是本地的時間,沒弄清楚爲何?

{{ moment(message.timestamp).fromNow(refresh=True) }}

 

CSS和JS文件的說明

這裏用到的bootstrap和js的文件基本都是固定的框架文件,不需改動太大

 

目錄結構

 
相關文章
相關標籤/搜索