flask实战-留言板-使用bootstrap-flask简化页面编写(代码片段)

xiaxiaoxu xiaxiaoxu     2022-12-01     310

关键词:

使用Bootstrap-Flask简化页面编写

扩展Bootstrap-Flask内置了可以快速渲染Bootstrap样式HTML组件的宏,并提供了内置的Bootstap资源,方便快速开发,使用它可以简化在web程序里使用Bootstrap的过程。

 

扩展Bootstrap-Flask基于Flask-Bootstrap实现,旨在替代缺乏维护的后者。和Flask-Bootstrap相比,Bootstrap-Flask简化了大部分功能(比如未内置基模板),添加了Bootstrap4支持,并增加了一些辅助功能。

 

安装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()宏还支持许多参数来自定义表单,常用的参数及说明如下表:

render_form()宏常用参数:

技术图片

 

包括用来渲染表单的render_field()和render_form()宏在内,Bootstrap-Flask还提供了许多其他用途的宏,这些宏都可以通过bootstrap目录下的模板导入,常用的Bootstrap-Flask宏如表:

技术图片

 

使用Flask-Moment本地化日期和时间

在Message类中,我们存储时间戳时使用的是datatime模块的datetime.now()方法生成的datetime对象,它是一个本地时间。具体来说,这个方法会返回服务器(也就是运行程序的计算机)设置的时区所对应的时间。对于测试来说这足够了,如果要把程序部署到真正的服务器上,就可能会面临时区问题。比如,我把程序部署到美国的服务器上,那么这个时间将不再是我们期待的东八区时间,而是服务器本地的美国时间。另一方面,如果我们的程序被其他时区的人访问,他们更希望看到自己所在时区的时间,而不是固定的东八区时间。

 

本地化前的准备

如何让世界各地的用户访问程序时都能看到自己的本地时间呢?一个简单的方法是使用JavaScript库在客户端(浏览器)中进行时间的转换,因为浏览器可以获取到用户浏览器/电脑上的时区设置信息。

为了能够在客户端进行时间的转换,我们需要在服务器端提供更纯正的时间(naive time),即不包含时区信息的时间戳(与之相对,包含时区的时间戳被称为细致的时间,即aware time)。datetime模块的datetime.utcnow()方法用来生成当前的UTC(Coordinated Universal Time, 协调世界时间),而UTC格式时间就是不包含时区信息的纯正时间。我们将使用它在时间戳字段上替代之前的datetime.now方法,作为时间戳timestamp字段的默认值:

from datetime import datetime

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

 

使用Flask-Moment集成Moment.js

Moment.js是一个用于处理时间和日期的开源JavaScript库,它可以对时间和日期进行各种方式的处理。它会根据用户电脑上的时区设置在客户端使用JavaScript来渲染时间和日期,另外还提供了丰富的时间渲染格式支持。

扩展Flask-Moment简化了在Flask项目中使用Moment.js的过程,集成了常用的时间和日期处理函数。首先使用pipenv安装:

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的文件基本都是固定的框架文件,不需改动太大

技术图片

 

目录结构

 技术图片

 

flask实战-留言板-使用faker生成虚拟数据(代码片段)

使用Faker生成虚拟数据创建虚拟数据是编写Web程序时的常见需求。在简单的场景下,我们可以手动创建一些虚拟数据,但更方便的选择是使用第三方库实现。流行的python虚拟数据生成工具有Mimesis和Faker,后者同时支持python2和python... 查看详情

flask开发实战:前言(代码片段)

...能的实现方法和技巧。  ·第7章:通过一个简单的留言板程序SayHello介绍Web开发的基本流程和基本的项目管理方式,对第一部分的基础知识进行简单回顾。 ·第8章:通过个人博客程序Bluelog介绍CRUD操作、用户认证、... 查看详情

推荐系统从入门到实战——flask框架的使用

Flask框架的使用​​Flask框架的使用​​​​Flask简介​​​​Flask环境配置​​​​安装virtualenv​​​​创建虚拟环境​​​​激活环境​​​​安装包​​​​测试安装​​​​主要内容​​​​路由​​​​route装饰器​​... 查看详情

flask企业级论坛实战目录导航

Flask是流行的pythonweb框架...(* ̄︶ ̄)零基础到企业级论坛实战,人生苦短,我用python,开启FLask之旅吧...Flask之旅Flask实战第1天:第一个Flask程序Flask实战第2天:URL传参Flask实战第3天:url_for使用Flask实战第4天:自定义url转换器Flask... 查看详情

flask高效开发实战-flask1

在模板渲染中,使用Markup转换变量中的特殊字符fromflaskimportMarkupMarkup函数对字符串进行转移处理再传递给render_template()函数在浏览器中显示标签代码路由地址的反响生成通过函数名获得与其绑定的Url地址需要使用url_for函数进行反... 查看详情

flask实战-个人博客-使用类组织配置(代码片段)

使用类组织配置在实际需求中,我们往往需要不同的配置组合。例如,开发用的配置,测试用的配置,生产环境用的配置。为了能方便地在这些配置中切换,你可以把配置文件升级为包,然后为这些使用场景分别创建不同的配置... 查看详情

flask实战-个人博客-使用类组织配置(代码片段)

使用类组织配置在实际需求中,我们往往需要不同的配置组合。例如,开发用的配置,测试用的配置,生产环境用的配置。为了能方便地在这些配置中切换,你可以把配置文件升级为包,然后为这些使用场景分别创建不同的配置... 查看详情

flask框架的学习与实战:开发环境搭建

Flask是一个使用Python编写的轻量级Web应用框架。其WSGI工具箱采用Werkzeug,模板引擎则使用Jinja2。很多功能的实现都参考了django框架。由于项目需要,在此记录下学习的过程及心得。工欲善其事,必先利其器。就从搭建一套flask开... 查看详情

16flask实战第16天:flask文件上传

上传文件以及访问上传的文件这里以图片来演示flask如何上传文件首先来写个简单的前端页面upload.html,在里面写个文件上传的表单后端debug断点调试获取到文件后,当然,我们需要把文件保存下来。在项目下新建一个目录media用来... 查看详情

flask框架的学习与实战:开发环境搭建(代码片段)

Flask是一个使用 Python 编写的轻量级Web应用框架。其 WSGI 工具箱采用Werkzeug,模板引擎则使用Jinja2。很多功能的实现都参考了django框架。由于项目需要,在此记录下学习的过程及心得。工欲善其事,必先利其器。就... 查看详情

哈士奇赠书活动-18期-〖flaskweb全栈开发实战〗

文章目录⭐️赠书活动-《FlaskWeb全栈开发实战》⭐️编辑推荐⭐️内容提要⭐️赠书活动→获奖名单⭐️赠书活动-《FlaskWeb全栈开发实战》内容简介:《FlaskWeb全栈开发实战》围绕Flask框架,详细地讲解了使用Flask开发网... 查看详情

flask实战-个人博客-电子邮件支持(代码片段)

电子邮件支持因为博客要支持评论,所以我们需要在文章有了新评论后发邮件通知管理员。而且,当管理员回复了读者的评论后,也需要发送邮件提醒读者。 为了方便读者使用示例程序,personalBlog中仍然使用Flask-Mail来发送... 查看详情

flask实战第41天:发送短信验证码

本项目使用的短信运营商是阿里云。使用淘宝账号登录阿里云控制台。在“产品与服务”中搜索“短信”进入短信服务获取AccessKey输入子账户用户名权限选择管理短信服务  签名管理:申请签名模板管理:设... 查看详情

flask实战第67天:flask+celery实现邮件和短信异步发送(代码片段)

之前在项目中我们发送邮件和短信都是阻塞的,现在我们来利用Celery来优化它们官方使用文档: http://flask.pocoo.org/docs/1.0/patterns/celery/redis服务器及插件,还有cerely在上节我们已经安装好,这里就不重复过程了。首先,来完成... 查看详情

flask高效开发实战-flask2

回调接入点-页面缓存逻辑fromflaskimportFlask,request,render_templatefromwerkzeug.contrib.cacheimportSimpleCacheapp=Flask(__name__)CACHE_TIMEOUT=300cache=SimpleCache()cache.timeout=CACHE_TIMEOUT@app.before_request 查看详情

10.flask博客项目实战五之用户登录功能(代码片段)

配套视频教程本文B站配套视频教程密码哈希用户模型有一个password_hash字段,到目前为止尚未使用。它是用于保存用户密码的哈希值,密码用于验证用户在登录过程中输入的密码。密码散列是一个复杂的主题,应交给安全专家,... 查看详情

如何部署apache2+mod_wsgi+flask?

最近初学python,在linux环境下写了一个基于flask的留言板程序,怎样把代码使用Apache+mod_wsgi部署呢?希望大侠们能说得清楚点.每一个步骤都说一下!谢谢,感激不尽!!!参考技术A直接用uwsgi+nginx吧用百度搜索uwsgiflask有例子 查看详情

在重构flask项目的时候对于wsgi中间件和好密钥的规划实战(代码片段)

##手动推入一个上下文如果尝试访问应用程序上下文之外的当前内容_应用程序或此对象的任何使用都将收到此错误消息:RuntimeError:Workingoutsideofapplicationcontext.​Thistypicallymeansthatyouattemptedtousefunctionalitythatneededtointerfacewiththecurrentapp... 查看详情