Flask Web Development —— 模板(中)
admin
2023-07-31 01:49:59
0

2、集成Twitter Bootstrap的Flask-Bootstrap

Bootstrap是Twitter的一个开源框架,提供用户交互组件来创建一个清新且有吸引力的web页面,并兼容所有现代web浏览器。

Bootstrap是一个客户端框架,服务端不直接参与。服务端需要做的就是提供HTML响应,引用层叠样式表(CSS)和JavaScript文件并通过HTML、CSS、和JavaScript代码来实例化需要的组件。模板是做这些的理想地方。

集成Bootstrap到应用程序最好的方式是在模板中做一些必要的改变。一个简单点的途径就是使用Flask-Bootstrap扩展去简化集成工作。可以通过pip来安装Flask-Bootstrap:

(venv) $ pip install flask-bootstrap

Flask扩展通常在应用程序实例被创建的时候初始化。示例3-4展示Flask-Bootstrap的初始化。

示例3-4. hello.py:Flask-Bootstrap初始化

from flask.ext.bootstrap import Bootstrap
# ...
bootstrap = Bootstrap(app)

和第二章的Flask-Script一样,Flask-Bootstrap从flask.ext命名空间导入并通过传递应用程序实例到构造函数来初始化。

一旦Flask-Bootstrap被初始化,一个包含所有Bootstrap文件的基础模板就可供应用程序使用了。这个模板利用Jinja2的模板继承,应用程序则可以扩展一个拥有通用页面结构,且包含Bootstrap导入的元素的基础模板。示例3-5展示作为派生模板的新版user.html

_示例3-5. templates/user.html: 使用Flask-Bootstrap的模板

{% extends \"bootstrap/base.html\" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}


Flasky
{% endblock %} {% block content %}

Hello, {{ name }}!

{% endblock %}

Jinja2的extends指令通过从Flask-Bootstrap引用bootstrap/base.html来实现模板的继承。Flask-Bootstrap的基础模板提供一个包含Bootstrap CSS和JavaScript文件的web页面骨架。

基础模板定义了一些可以被派生模板重写的blockblockendblock指令定义了被添加到基础模板中block的内容。

上面的user.html模板定义了三个block,分别命名为titlenavbarcontent。基础模板里的这些block输出派生模板定义的内容。title块比较简单;它的内容将出现在</code>标签内,然后被渲染在HTML文档的头部 。<code>navbar</code>和<code>content</code>块则是为页面保留的导航栏和主内容。</p> <p>在这个模板中,<code>navbar</code>块使用Bootstrap组件定义了一个简单的导航栏。<code>content</code>块有个名为<code>container</code>的<code><div></code>,里面嵌套了名为<code>page-header</code>的<code><div></code>。图3-1展示这些操作后应用程序长成啥样了。</p> <blockquote> <p>建议:如果你有克隆在GitHub上的应用程序,你现在可以运行<code>git checkout 3b</code>来切换到这个版本的应用程序。Bootstrap官方文档 是一个非常强大的学习资料,完全可以复制粘贴使用那些示例。</p> </blockquote> <p><img data-src="https://segmentfault.com/image?src=http://young-py.github.io/imgs/flask3-02.png&objectId=1190000000755204&token=47cb9992cd06de9f6d8314bcdb75e88e" src="http://file.zhishichong.com/images/article/20161025/flask3-02.png&objectId=1190000000755204&token=47cb9992cd06de9f6d8314bcdb75e88e.png" /></p> <p><em>图片3-1. Twitter Bootstrap模板</em></p> <p>Flask-Bootstrap的<code>base.html</code>模板定义了一些其他可供派生模板使用的<code>block</code>。表格3-2展示了完整的可用<code>block</code>列表。</p> <p><em>表格3-2. Flask-Bootstrap基础模板中的block</em></p> <p><img data-src="https://segmentfault.com/image?src=http://young-py.github.io/imgs/flask3-03.png&objectId=1190000000755204&token=3c5a6fe32d02a406cf3552e5263fe6fa" src="http://file.zhishichong.com/images/article/20161025/flask3-03.png&objectId=1190000000755204&token=3c5a6fe32d02a406cf3552e5263fe6fa.png" /></p> <p>表格3-2中的许多块用于Flask-Bootstrap自身,所以直接重写它们会引发问题。例如,<code>styles</code>和<code>scripts</code>块是Bootstrap定义文件的地方。如果应用程序需要新增自己的内容到已经有一些内容的块中,则必须使用Jinja2的<code>super()</code>。例如,如何在派生模板中写<code>scripts</code>块,来给文档增加新的JavaScript文件:</p> <pre><code>{% block scripts %} {{ super() }} <script type=\"text/javascript\" src=\"my-script.js\"></script> {% endblock %} </code></pre> <h3 id="3%e3%80%81%e8%87%aa%e5%ae%9a%e4%b9%89%e9%94%99%e8%af%af%e9%a1%b5%e9%9d%a2">3、自定义错误页面</h3> <p>当你输入错误路径在你的浏览器地址栏,你会得到404错误代码页面。目前的错误页面很普通也没有吸引力,且没有一致的使用Bootstrap页面。</p> <p>Flask允许应用程序自定义基于模板的错误页面,就像常规的路由。两个最常见的错误代码,404是在客户端请求的页面或路径不存在的时候触发;500是当存在未处理的异常时触发。示例3-6展示如何为这两个错误提供自定义处理。</p> <p><em>示例3-6. hello.py:自定义错误页面</em></p> <pre><code>@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 </code></pre> <p>错误处理返回响应,和视图函数一样。同时返回相应错误的数字状态码。</p> <p>在错误处理中引用的模板需要自己去写。这些模板需要和常规的页面一样的布局,所以在这个示例中需要导航栏和页面头部显示错误信息。</p> <p>编写这些模板的简单方式是复制<code>templates/user.html</code>到<code>templates/404.html</code>和<code>templates/500.html</code>,然后改变这两个新文件的页面头部元素来给出相应的错误信息,但这会产生很多副本。</p> <p>Jinja2的模板继承可以帮助我们解决这个问题。Flask-Bootstrap提供了一个带有基本布局页面的基础模板,应用程序可以定义自己的、带有完整页面布局的基础模板,包括导航栏和定义在派生模板中的页面内容。示例3-7展示了<code>templates/base.html</code>,它是一个继承自<code>bootstrap/base.html</code>的新模板且定义了导航栏,但对于其他模板则是一个基础模板,例如<code>templates/user.html</code>、<code>templates/404.html</code>和<code>templates/500.html</code>。</p> <p>_示例3-7. templates/base.html:带有导航栏的基础应用程序模板</p> <pre><code>{% 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 %} </code></pre> <p>这个模板中的<code>content</code>块中只是一个名为<code>container</code>的<code><div></code>元素,它包含了在派生模板中定义的名为<code>page_content</code>的空block。</p> <p>应用程序的模板将从该模板继承而不是直接从Flask-Bootstrap继承。示例3-8展示了从<code>templates/base.html</code>继承来构造一个自定义404错误页面是如此的简单。</p> <p><em>示例3-8. <code>templates/404.html</code>:使用模板继承自定义404错误页面</em></p> <pre><code>{% extends \"base.html\" %} {% block title %}Flasky - Page Not Found{% endblock %} {% block page_content %} <div class=\"page-header\"> <h1>Not Found</h1> </div> {% endblock %} </code></pre> <p>图片3-2展示在浏览器中错误页面是怎样的。</p> <p><img data-src="https://segmentfault.com/image?src=http://young-py.github.io/imgs/flask3-04.png&objectId=1190000000755204&token=aad0778ca8c03505262a0fb12560f667" src="http://file.zhishichong.com/images/article/20161025/flask3-04.png&objectId=1190000000755204&token=aad0778ca8c03505262a0fb12560f667.png" /></p> <p><em>图片3-2. 自定义404错误页面</em></p> <p>现在<code>templates/user.html</code>模板可以通过继承基础模板来简化它,就像示例3-9展示的这样。</p> <p><em>示例3-9. templates/user.html:使用模板继承简化页面模板</em></p> <pre><code>{% extends \"base.html\" %} {% block title %}Flasky{% endblock %} {% block page_content %} <div class=\"page-header\"> <h1>Hello, {{ name }}!</h1> </div> {% endblock %} </code></pre> <blockquote> <p>建议:如果你有克隆在GitHub上的应用程序,你现在可以运行<code>git checkout 3c</code>来切换到这个版本的应用程序。</p> </blockquote> <!--end::Text--> </div> <!--end::Description--> <div class="mt-5"> <!--关键词搜索--> <a href="/index.php?s=article&c=search&keyword=python" class="badge badge-light-primary fw-bold my-2" target="_blank">python</a> <a href="/index.php?s=article&c=search&keyword=flask" class="badge badge-light-primary fw-bold my-2" target="_blank">flask</a> <a href="/index.php?s=article&c=search&keyword=jinja2" class="badge badge-light-primary fw-bold my-2" target="_blank">jinja2</a> </div> <div class="mt-5"> <p class="fc-show-prev-next"> <strong>上一篇:</strong><a href="/program/40380.html">使用python写vim插件</a><br> </p> <p class="fc-show-prev-next"> <strong>下一篇:</strong><a href="/program/40382.html">flask 跨域访问装饰器实现</a> </p> </div> <!--begin::Block--> <div class="d-flex flex-stack mb-2 mt-10"> <!--begin::Title--> <h3 class="text-dark fs-5 fw-bold text-gray-800">相关内容</h3> <!--end::Title--> </div> <div class="separator separator-dashed mb-9"></div> <!--end::Block--> <div class="row g-10"> </div> </div> <!--end::Table widget 14--> </div> <!--end::Col--> <!--begin::Col--> <div class="col-xl-4 mt-0"> <!--begin::Chart Widget 35--> <div class="card card-flush h-md-100"> <!--begin::Header--> <div class="card-header pt-5 "> <!--begin::Title--> <h3 class="card-title align-items-start flex-column"> <!--begin::Statistics--> <div class="d-flex align-items-center mb-2"> <!--begin::Currency--> <span class="fs-5 fw-bold text-gray-800 ">热门资讯</span> <!--end::Currency--> </div> <!--end::Statistics--> </h3> <!--end::Title--> </div> <!--end::Header--> <!--begin::Body--> <div class="card-body pt-3"> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/4386.html" class="text-dark fw-bold text-hover-primary fs-6">Mobi、epub格式电子书如...</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/831667.html" class="text-dark fw-bold text-hover-primary fs-6">定时清理删除C:\Progra...</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/uploadfile/202403/9fc6c8bf38a85fb.png#没有设置高宽参数,将以原图输出')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/831666.html" class="text-dark fw-bold text-hover-primary fs-6">scoped_dir32_70...</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/39278.html" class="text-dark fw-bold text-hover-primary fs-6">500 行 Python 代码...</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/783.html" class="text-dark fw-bold text-hover-primary fs-6">小程序支付时提示:appid和...</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">[Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/831649.html" class="text-dark fw-bold text-hover-primary fs-6"> pycparser 是一个用...</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">`pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/4837.html" class="text-dark fw-bold text-hover-primary fs-6">微信小程序使用slider实现...</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/3333.html" class="text-dark fw-bold text-hover-primary fs-6">65536是2的几次方 计算2...</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/831541.html" class="text-dark fw-bold text-hover-primary fs-6">Apache Doris 2....</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...</span> </div> <!--end::Title--> </div> <!--begin::Item--> <div class="d-flex flex-stack mb-7"> <!--begin::Symbol--> <div class="symbol symbol-60px symbol-2by3 me-4"> <div class="symbol-label" style="background-image: url('/static/assets/images/nopic.gif')"></div> </div> <!--end::Symbol--> <!--begin::Title--> <div class="m-0"> <a href="/program/4969.html" class="text-dark fw-bold text-hover-primary fs-6">项目管理和工程管理的区别</a> <span class="text-gray-600 fw-semibold d-block pt-1 fs-7">项目管理 项目管理,顾名思义就是专注于开发和完成项目的管理,以实现目标并满足成功标准和项目要求。 工...</span> </div> <!--end::Title--> </div> </div> <!--end::Body--> </div> <!--end::Chart Widget 35--> </div> <!--end::Col--> </div> </div> <!--end::Content container--> </div> <!--end::Content--> </div> <!--end::Content wrapper--> <!--begin::Footer--> <div id="kt_app_footer" class="app-footer"> <!--begin::Footer container--> <div class="app-container container-xxl d-flex flex-column flex-md-row flex-center flex-md-stack py-3"> <!--begin::Copyright--> <div class="text-dark order-2 order-md-1"> <span class="text-muted fw-semibold me-1">2025 ©</span> 晓说杂谈<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?f7b4581e1f9f88ac28d46df58a8d3ff5"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script> <a target="_blank" href="https://beian.miit.gov.cn/">豫ICP备13019747号-13</a> </div> <!--end::Copyright--> <!--begin::Menu--> <ul class="menu menu-gray-600 menu-hover-primary fw-semibold order-1"> <li class="menu-item"> <a href="/tech" target="_blank" class="menu-link px-2">科技分享</a> </li> <li class="menu-item"> <a href="/web" target="_blank" class="menu-link px-2">网络技术</a> </li> <li class="menu-item"> <a href="/hardware" target="_blank" class="menu-link px-2">硬件设备</a> </li> <li class="menu-item"> <a href="/program" target="_blank" class="menu-link px-2">程序人生</a> </li> <li class="menu-item"> <a href="/jinrong" target="_blank" class="menu-link px-2">探索发现</a> </li> <li class="menu-item"> <a href="/jixie" target="_blank" class="menu-link px-2">机械加工</a> </li> <li class="menu-item"> <a href="/dianshang" target="_blank" class="menu-link px-2">电商</a> </li> <li class="menu-item"> <a href="/other" target="_blank" class="menu-link px-2">其他</a> </li> <li class="menu-item"> <a href="/zhishi" target="_blank" class="menu-link px-2">日常知识</a> </li> <li class="menu-item"> <a href="/yulu" target="_blank" class="menu-link px-2">每日语录</a> </li> </ul> <!--end::Menu--> </div> <!--end::Footer container--> </div> <!--end::Footer--> </div> <!--end:::Main--> </div> <!--end::Wrapper--> </div> <!--end::Page--> </div> <!--end::App--> <div id="kt_scrolltop" class="scrolltop" data-kt-scrolltop="true"> <!--begin::Svg Icon | path: icons/duotune/arrows/arr066.svg--> <span class="svg-icon"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect opacity="0.5" x="13" y="6" width="13" height="2" rx="1" transform="rotate(90 13 6)" fill="currentColor"></rect> <path d="M12.5657 8.56569L16.75 12.75C17.1642 13.1642 17.8358 13.1642 18.25 12.75C18.6642 12.3358 18.6642 11.6642 18.25 11.25L12.7071 5.70711C12.3166 5.31658 11.6834 5.31658 11.2929 5.70711L5.75 11.25C5.33579 11.6642 5.33579 12.3358 5.75 12.75C6.16421 13.1642 6.83579 13.1642 7.25 12.75L11.4343 8.56569C11.7467 8.25327 12.2533 8.25327 12.5657 8.56569Z" fill="currentColor"></path> </svg> </span> <!--end::Svg Icon--> </div> <!--begin::Javascript--> <script>var hostUrl = "/static/default/pc/";</script> <!--begin::Global Javascript Bundle(mandatory for all pages)--> <script src="/static/default/pc/plugins/global/plugins.bundle.js"></script> <script src="/static/default/pc/js/scripts.bundle.js"></script> <!--end::Global Javascript Bundle--> <!--end::Javascript--> </body> <!--end::Body--> </html>