
Flask Bootstrap
Bootstrap
框架
Bootstrap
中文主站:https://www.bootcss.com/
Bootstrap4
:https://getbootstrap.com/
Bootstrap
是基于HTMLCSSJAVASCRIPT
的前端框架.
移动设备优先 主流浏览器支持 响应式设计
Flask Bootstrap
对于
Flask
来说,前期有一个相关扩展Flask-Bootstrap
,不过作者很长时间没有更新过.于是有人基于它做了另外的一个扩展Bootstrap-Flask
.
Bootstrap-Flask
支持Bootstrap4
,并且基于Jinja2
模板引擎的宏做了相关扩展.参考代码:
https://github.com/ningwenyan/demo_code/tree/master/flask_demo_code/T27
安装
$ pip install bootstrap-flask
如果之前安装过
flask-bootstrap
,需要先卸载,再安装bootstrap-flask
$ pip uninstall flask-bootstrap bootstrap-flask
$ pip install bootstrap-flask官方文档:
https://bootstrap-flask.readthedocs.io/en/stable/
初始化
初始化很简单
from flask_bootstrap import Bootstrap
from flask import Flask
app = Flask(__name__)
bootstrap = Bootstrap(app)或者使用
init_app
的方式初始化from flask_bootstrap import Bootstrap
from flask import Flask
app = Flask(__name__)
bootstrap = Bootstrap()
bootstrap.init_app(app)可以浏览一下源码
class Bootstrap(object):
def __init__(self, app=None):
if app is not None:
self.init_app(app)
def init_app(self, app):
if not hasattr(app, 'extensions'):
app.extensions = {}
app.extensions['bootstrap'] = self
blueprint = Blueprint('bootstrap', __name__, template_folder='templates',
static_folder='static', static_url_path='/bootstrap' + app.static_url_path)
app.register_blueprint(blueprint)
app.jinja_env.globals['bootstrap'] = self
app.jinja_env.globals['bootstrap_is_hidden_field'] = is_hidden_field_filter
app.jinja_env.globals['get_table_titles'] = get_table_titles
app.jinja_env.add_extension('jinja2.ext.do')
# default settings
app.config.setdefault('BOOTSTRAP_SERVE_LOCAL', False)
app.config.setdefault('BOOTSTRAP_BTN_STYLE', 'primary')
app.config.setdefault('BOOTSTRAP_BTN_SIZE', 'md')
app.config.setdefault('BOOTSTRAP_BOOTSWATCH_THEME', None)
app.config.setdefault('BOOTSTRAP_ICON_SIZE', '1em')
app.config.setdefault('BOOTSTRAP_ICON_COLOR', None)
app.config.setdefault('BOOTSTRAP_MSG_CATEGORY', 'primary')
@staticmethod
def load_css(version=VERSION_BOOTSTRAP):
"""Load Bootstrap's css resources with given version.
.. versionadded:: 0.1.0
:param version: The version of Bootstrap.
"""
css_filename = 'bootstrap.min.css'
serve_local = current_app.config['BOOTSTRAP_SERVE_LOCAL']
bootswatch_theme = current_app.config['BOOTSTRAP_BOOTSWATCH_THEME']
if not bootswatch_theme:
base_path = 'css/'
else:
base_path = 'css/swatch/%s/' % bootswatch_theme.lower()
if serve_local:
css = '<link rel="stylesheet" href="%s" type="text/css">' %
url_for('bootstrap.static', filename=base_path + css_filename)
else:
if not bootswatch_theme:
css = '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@%s/dist/css/%s"'
' type="text/css">' % (version, css_filename)
else:
css = '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@%s/dist/%s/%s"'
' type="text/css"' % (version, bootswatch_theme.lower(), css_filename)
return Markup(css)
@staticmethod
def load_js(version=VERSION_BOOTSTRAP, jquery_version=VERSION_JQUERY,
popper_version=VERSION_POPPER, with_jquery=True, with_popper=True):
"""Load Bootstrap and related library's js resources with given version.
.. versionadded:: 0.1.0
:param version: The version of Bootstrap.
:param jquery_version: The version of jQuery.
:param popper_version: The version of Popper.js.
:param with_jquery: Include jQuery or not.
:param with_popper: Include Popper.js or not.
"""
js_filename = 'bootstrap.min.js'
jquery_filename = 'jquery.min.js'
popper_filename = 'popper.min.js'
serve_local = current_app.config['BOOTSTRAP_SERVE_LOCAL']
if serve_local:
js = '<script src="%s"></script>' % url_for('bootstrap.static', filename='js/' + js_filename)
else:
js = '<script src="https://cdn.jsdelivr.net/npm/bootstrap@%s/dist/js/%s">'
'</script>' % (version, js_filename)
if with_jquery:
if serve_local:
jquery = '<script src="%s"></script>' % url_for('bootstrap.static', filename=jquery_filename)
else:
jquery = '<script src="https://cdn.jsdelivr.net/npm/jquery@%s/dist/%s">'
'</script>' % (jquery_version, jquery_filename)
else:
jquery = ''
if with_popper:
if serve_local:
popper = '<script src="%s"></script>' % url_for('bootstrap.static', filename=popper_filename)
else:
popper = '<script src="https://cdn.jsdelivr.net/npm/popper.js@%s/dist/umd/%s">'
'</script>' % (popper_version, popper_filename)
else:
popper = ''
return Markup('''%s
%s
%s''' % (jquery, popper, js))很简单的初始化后,会把
bootstrap
添加到Jinja2
的虚拟环境中,它默认使用的是bootstrap4
,并且提供了2个基本的静态方法
bootstrap.load_css()
:默认是bootstrap4
,使用的是CDN
分发CSS
样式bootstrap.load_js()
:默认是bootstrap4
,使用的是CDN
分发js
样式当然,如果你不想使用
bootstrap4
,也可以自定义版本,基本配置config
配置 默认 说明 BOOTSTRAP_SERVE_LOCAL
False
False
:自訂版本,True
:系統默认版本BOOTSTRAP_BTN_STYLE
primary
button
的样式BOOTSTRAP_BTN_SIZE
md
button
大小BOOTSTRAP_ICON_SIZE
lem
图标 icon
大小BOOTSTRAP_ICON_COLOR
None
图标颜色 BOOTSTRAP_BOOTSWATCH_THEME
None
bootswatch
主题,参考BOOTSTRAP_MSG_CATEGORY
primary
flask flash
样式
bootstrap-flask
不提供基础模板base.html
,需要自己创建.load_css, load_js
都可以放置在base.html
中,类似如下<head>
....
{{ bootstrap.load_css() }}
</head>
<body>
...
{{ bootstrap.load_js() }}
</body>
启用宏
Macro Templates Path Description render_field() bootstrap/form.html 渲染一个 WTForm
表单字段render_form() bootstrap/form.html 渲染一个 WTForm
表单render_form_row() bootstrap/form.html Render a row of a grid form render_hidden_errors() bootstrap/form.html Render error messages for hidden form field render_pager() bootstrap/pagination.html Render a basic Flask-SQLAlchemy pagniantion 分页标签 render_pagination() bootstrap/pagination.html Render a standard Flask-SQLAlchemy pagination 分页标签 render_nav_item() bootstrap/nav.html Render a navigation item render_breadcrumb_item() bootstrap/nav.html Render a breadcrumb item render_static() bootstrap/utils.html Render a resource reference code (i.e. <link>
,<script>
)render_messages() bootstrap/utils.html Render flashed messages send by flash() function render_icon() bootstrap/utils.html Render a Bootstrap icon render_table() bootstrap/table.html Render a table with given data 有关它的详细解释,参考官方文档:
https://bootstrap-flask.readthedocs.io/en/stable/macros.html#render-pagination
以下选取几个常用的进行渲染.
渲染导航栏中的
URL
链接.render_nav_item(endpoint, text, badge='', use_li=False, **kwargs)
查看它的定义能更容易理解
{% macro render_nav_item(endpoint, text, badge='', use_li=False) %}
{% if use_li %}<li class="nav-item">{% endif %}
<a class="{% if not use_li %}nav-item{% endif %} nav-link {% if request.endpoint and request.endpoint == endpoint %}active{% endif %}"
href="{{%20url_for(endpoint,%20**kwargs)%20}}">
{{ text }} {% if badge %}<span class="badge badge-light">{{ badge }}</span>{% endif %}
</a>
{% if use_li %}</li>{% endif %}
{% endmacro %}可以到
bootstrap4
上找一个到导航示例https://getbootstrap.com/docs/4.5/components/navbar/
,进行改造
base.html
{# 引入 分页导航 #}
{% from 'bootstrap/nav.html' import render_nav_item %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
{% endblock %}
</title>
{# 加载bootstrap 资源 #}
{{ bootstrap.load_css() }}
{{ bootstrap.load_js() }}
</head>
<body>
{#导航栏#}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
{# 应用宏#}
{{ render_nav_item('index', '首页', use_li=True) }}
{{ render_nav_item('index', '首页', use_li=True) }}
{{ render_nav_item('index', '首页', use_li=True) }}
</ul>
</div>
</nav>
<main class="container">
</main>
</body>
</html>然后在
index.html
中继承模板,{% extends 'base.html' %}
{% block title %}
首页
{% endblock %}
{% block content %}
<h1>这是首页</h1>
{% endblock %}运行项目,可以查看结果.
render_messages
其实就是用来接受后端
flash
消息的,但是会内置一些样式在其中.参数可以参考文档
使用如下
base.html
{# 引入 分页导航 #}
{% from 'bootstrap/nav.html' import render_nav_item %}
{# 引入messages解析 #}
{% from 'bootstrap/utils.html' import render_messages %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
{% endblock %}
</title>
{# 加载bootstrap 资源 #}
{{ bootstrap.load_css() }}
{{ bootstrap.load_js() }}
</head>
<body>
{#导航栏#}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
{# 应用宏#}
{{ render_nav_item('index', '首页', use_li=True) }}
{{ render_nav_item('get_flash_messages', 'Flash messages', use_li=True) }}
{{ render_nav_item('index', '首页', use_li=True) }}
</ul>
</div>
</nav>
<main class="container">
{{ render_messages(container=False, dismissible=True, dismiss_animate=True) }}
{% block content %}
{% endblock %}
</main>
</body>
</html>设定一个视图函数
@app.route('/get_flash_messages/', methods=['GET', 'POST'])
def get_flash_messages():
flash('A simple default alert—check it out!')
flash('A simple primary alert—check it out!', 'primary')
flash('A simple secondary alert—check it out!', 'secondary')
flash('A simple success alert—check it out!', 'success')
flash('A simple danger alert—check it out!', 'danger')
flash('A simple warning alert—check it out!', 'warning')
flash('A simple info alert—check it out!', 'info')
flash('A simple light alert—check it out!', 'light')
flash('A simple dark alert—check it out!', 'dark')
flash(Markup(
'A simple success alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.'),
'success')
return render_template('get_flash_messages.html')运行页面,可以得到不同样式的提示框.(
primary, seondary, success
等等.)
render_form
它会渲染一个完整的
flask_wtf/wtforms
的表单,并且能够提示验证错误信息.同样的,它拥有很多的参数,不过一个基本的实例如下
添加 CSRF
保护csrf.init_app(app=app)
表单验证 class GetForm(FlaskForm):
username = StringField('用户名', validators=[Length(4, 9, '请输入正确长度的用户名')], render_kw={'placeholder':'username'})
password = PasswordField('密码', validators=[Length(4, 9, '请输入正确长度的密码')], render_kw={'placeholder':'password'})
submit = SubmitField('登录')
视图设计 @app.route('/get_form/', methods=['GET', 'POST'])
def get_form():
form = GetForm()
if form.validate_on_submit():
return redirect(url_for('index'))
return render_template('get_form.html', form=form)
前端页面 {% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_form %}
{% block title %}
Form
{% endblock %}
{% block content %}
{{ render_form(form) }}
{% endblock %}在前端只需要渲染
form
即可.render_form
会根据flask_wtf/wtforms
的表单定义自己生成表单内容.(有关这一部分可以查看源码宏定义)并且能处理错误信息.
render_field
渲染一个
flask-wtf/wtforms
的Field
,并且能够处理错误信息.
视图 @app.route('/get_form_filed', methods=['GET', 'POST'])
def get_form_field():
form = GetFormField()
if form.validate_on_submit():
return redirect(url_for('index'))
return render_template('get_form_field.html', form=form)
约束 class GetFormField(FlaskForm):
username = StringField('用户名', validators=[Length(4, 9, '请输入正确长度的用户名')], render_kw={'placeholder': 'username'})
password = PasswordField('密码', validators=[Length(4, 9, '请输入正确长度的密码')], render_kw={'placeholder': 'password'})
submit = SubmitField('登录')
后端 {% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_field %}
{% block title %}
Form Field
{% endblock %}
{% block content %}
<form action="" method="post">
{{ form.csrf_token }}
{{ render_field(form.username) }}
{{ render_field(form.password) }}
{{ render_field(form.submit) }}
</form>
{% endblock %}注意添加
csrf
保护,它能自动弹出错误.
render_pager/render_pagination
分页查询,这涉及到
flask-sqlalchemy
的分页查询函数db.session.query(User).filter_by().paginate(page=None, per_page=None,
error_out=True, max_per_page=None)
page
查询的页数
per_page
每页的条数
max_per_page
每页最大条数,有值时,per_page
受它影响
error_out
当值为 True 时,下列情况会报错
当 page 为 1 时,找不到任何数据 page 小于 1,或者 per_page 为负数 page 或 per_page 不是整数 该方法返回一个分页对象
Pagination
:调用paginate()
方法,会返回一个Pagination
对象,它封装了当前页的各种数据和方法
has_next
如果下一页存在,返回 Truehas_prev
如果上一页存在,返回 Trueitems
当前页的数据列表next_num
下一页的页码page
当前页码pages
总页数per_page
每页的条数prev_num
上一页的页码query
用于创建此分页对象的无限查询对象。total
总条数iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2)
迭代分页中的页码,四个参数,分别控制了省略号左右两侧各显示多少页码next(error_out=False)
返回下一页的分页对象prev(error_out=False)
返回上一页的分页对象前端渲染 {% macro render_pagination(pagination, endpoint) %}
<div class=pagination>
{%- for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
<a href="{{%20url_for(endpoint,%20page=page)%20}}">{{ page }}</a>
{% else %}
<strong>{{ page }}</strong>
{% endif %}
{% else %}
<span class=ellipsis>…</span>
{% endif %}
{%- endfor %}
</div>
{% endmacro %}
理解以上之后,再观看源码
{% macro render_pager(pagination,
fragment='',
prev=('<span aria-hidden="true">←</span> Previous')|safe,
next=('Next <span aria-hidden="true">→</span>')|safe,
align='') -%}
<nav aria-label="Page navigation">
<ul class="pagination {% if align == 'center' %}justify-content-center{% elif align == 'right' %}justify-content-end{% endif %}">
<li class="page-item {% if not pagination.has_prev %}disabled{% endif %}">
<a class="page-link"
href="{{%20url_for(request.endpoint,%20page=pagination.prev_num,%20**kwargs)%20+%20fragment%20if%20pagination.has_prev%20else%20'#'%20}}">
{{ prev }}
</a>
</li>
<li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
<a class="page-link"
href="{{%20url_for(request.endpoint,%20page=pagination.next_num,%20**kwargs)%20+%20fragment%20if%20pagination.has_next%20else%20'#'%20}}">
{{ next }}
</a>
</li>
</ul>
</nav>
{%- endmacro %}以上就很好理解了,它会生成一个类似
http://127.0.0.1/?page=1
的结构体,所以可以指定如下视图@app.route('/get_pager/', methods=['GET', 'POST'])
def get_pager():
db.drop_all()
db.create_all()
for i in range(100):
user = User(name='{}'.format(str(i)), password='h{}'.format(str(i)))
db.session.add(user)
db.session.commit()
page = request.args.get('page', 1, type=int)
pagination = User.query.paginate(page, 10)
messages = pagination.items
return render_template('get_pager.html', pagination=pagination, messages=messages)
requst.args.get('key', 'dafault', 'type')
page
是必须的,对应后端模板后端
{% extends 'base.html' %}
{% from 'bootstrap/pagination.html' import render_pager %}
{% from 'bootstrap/pagination.html' import render_pagination %}
{% block title %}
pagination
{% endblock %}
{% block content %}
{% for message in messages %}
Message:{{ message.id }}<br>
{% endfor %}
{{ render_pager(pagination) }}
{{ render_pagination(pagination) }}
{% endblock %}运行可以得到如下:
10322
– END –
原文始发于微信公众号(Flask学习笔记):Flask Bootstrap
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36395.html