模板

personalBlog采用典型的博客布局,左侧三分之二为主体,显示文章列表、正文;右侧三分之一为边栏,显示分为类列表、社交链接等。现在的工作是将HTML文件加工为模板,并创建对应的表单类,在模板中渲染。

并非所有的页面都需要添加边栏,所以我们不能把它放到基模板中。为了避免重复和易于维护,我们把边栏部分的代码放到了局部模板_sidebar.html中。除了基模板base.html和存储宏的macros.html模板,personalBlog程序的博客前台使用的模板如下所示:

index.html 主页;

about.html 关于页面;

_sidebar.html 边栏;

category.html 分类页面;

post.html 文章页面;

login.html 登录页面;

400.html;

404.html;

500.html;

personalBlog中将会用到400错误响应,表示无效请求,所以我们添加了对应的错误页面模板,在前面介绍工厂函数时我们已经编写了对应的错误处理函数。

博客后台使用的模板如下所示:

manager_category.html 分类管理页面;

new_category.html 新建分类页面;

edit_category.html 编辑分类页面;

manage_post.html 文章管理页面;

new_post.html 新建文章页面;

edit_post.html 编辑文章页面;

settings.html 博客设置页面;

manage_comment.html 评论管理页面。

这些模板根据类别分别放到了templates目录下的auth、admin、blog和errors子文件夹中,只有基模板在templates跟目录内。基模板中定义了程序的基本样式,包括导航栏和页脚,如下所示。

personalblog/templates/base.html: 基模板

<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<meta charset="utf-8">
<meta name="viepoint" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{% block title %}{% endblock title %} - PersonalBlog</title>
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/%s.min.css' % request.cookies.get('theme', 'perfect_blue')) }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css">
{% endblock head %}
</head>
<body>
{% block nav %}
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/">PersonalBlog</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01"
aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button> <div class="collapse navbar-collapse" id="navbarColor01">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/">Home</a>a>
</li>
</ul>
</div>
</div>
</nav>
{% endblock nav %}
<main class="container">
{% block content %}{% endblock content %}
{% block footer %}
<footer>
</footer>
{% endblock footer %}
</main> {% block scripts %}
<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='jsj/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')) }}
{% endblock %}
</body>
</html>

除了基本的HTML结构,我们还在基模板中加载了Favicon、自定义CSS、JavaScript文件,以及Bootstrap、Moment.js所需的资源文件,并创建了一些块用于在字模板中继承。

Bootstrap默认的样式足够没关,但也许你已经感到厌倦了。Bootswatch(https://bootswatch.com/)以及StartBootstrap(https://startbootstrap.com/)等网站上提供了许多免费的Bootstrap主题文件,你可以为自己的程序选择一个。你需要下载对应的CSS文件,保存到static/css目录下,替换Bootstrap的CSS文件(bootstrap.min.css),清楚缓存并重新加载页面即可看到新的样式。

基模板中的一些代码我们会在下面详细介绍,其他模板的实现我们则会在实现具体的功能时介绍。

1、模板上下文

在基模板的导航栏以及博客主页中需要使用博客的标题、副标题等存储在管理员对象上的数据,为了避免在每个视图函数中渲染模板时传入这些数据,我们在模板上下文函数中像模板上下文添加了管理员对象变量(admin)。另外,在多个页面中都包含的边栏中包含分类列表,我们也把分类数据传入到模板上下文中,如下所示。

personalBlog/__init__.py: 处理模板上下文

 
def create_app(config_name = None):
if config_name is None:
config_name = os.getenv('FLASK_CONFIG', 'development') app = Flask('personalBlog')
app.config.from_object(config[config_name]) register_logging(app) # 注册日志处理器
register_extensions(app) # 注册扩展(扩展初始化)
register_blueprints(app) # 注册蓝本
register_commands(app) # 注册自定义shell命令
register_errors(app) # 注册错误处理函数
register_shell_context(app) # 注册错误处理函数
register_template_context(app) # 注册模板上下文处理函数 return app def register_template_context(app):
@app.context_processor
def make_template_context():
admin = Admin.query.first()
categories = Category.query.order_by(Category.name).all()
return dict(admin=admin, categories=categories)

获取分类记录时,我们使用order_by()对记录进行排序,传入的规则是分类模型的name字段,这会对分类按字母顺序排列。在边栏模板(_sidebar.html)中,我们迭代categories变量,渲染分类列表,如下所示:

personalBlog/templates/blog/_sidebar.html: 边栏局部模板

{% if categories %}
<div class="card mb-3">
<div class="card-header">Categories</div>
<ul class="list-group list-group-flush">
{% for category in categories %}
<li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
<a href="{{ url_for('blog.show_category', category_id=category.id) }}">
{{ category.name }}
</a>
<span class="badge badge-primary badge-pill">{{ category.posts|length }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
除了分类的名称,我们还在每一个分类的右侧显示了与分类对应的文章总数,总数通过对分类对象的posts关系属性添加length过滤器获取。分类连接指向的blog.show_category视图我们将在后面介绍。
 
在基模板(base.html)和主页模板(index.html)中,我们可以直接使用传入的admin对象获取博客的标题和副标题。以主页模板为例:
personalBlog/templates/blog/index.html:
 
{% extends 'base.html' %}
{% from 'bootstrap/ pagination.html' import render_pager %} {% block title %}Home{% endblock %} {% block content %}
<div class="page-header">
<h1 class="display-3">{{ admin.blog_title|default('Blog Title') }}</h1>
<h4 class="text-muted">&nbsp;{{ admin.blog_sub_title|default('Blog Subtitle' }}</h4>
</div>
<div class="row">
<div class="col-sm-8">
{% include 'blog/_posts.html' %}
{% if posts %}
<div class="page-footer">{{ render_page(pagination }}</div>
{% endif %}
</div>
<div class="col-sm-4 sidebar">
{% include 'blog/_sidebar.html' %}
</div>
</div>
{% endblock %}
2、渲染导航链接

导航栏上的按钮应该再对应的页面显示激活状态。举例来说,当用户单击导航栏上的“关于”按钮打开关于页面时,“关于”按钮应该高亮显示。Bootstrap为导航链接提供了一个active类来显示激活状态,我们需要为当前页面对应的按钮添加active类。

这个功能可以通过判断请求的端点来实现,对request对象调用endpoint属性即可获得当前的请求端点。如果当前的端点与导航链接指向的端点相同,就为它添加active类,显示激活样式,如下所示:

<li {% if request.endpoint == 'blog.index' %}class="active"{% endif %}><a href="{{ url_for('blog.index') }}">Home</a>
</li>

有些教程中会使用endswith()方法来比较端点结尾。但是蓝本拥有独立的端点命名空间,即“<蓝本命>.<端点名>”,不同的端点可能会拥有相同的结尾,比如blog.index和auth.index,这时使用endswith()会导致判断错误,所以最妥善的做法是比较完整的端点值。

每个导航按钮的代码都基本相同,后面我们还会添加更多的导航链接。如果把这部分代码放到宏里,然后正在需要的地方根据指定的参数调用,就可以让模板更加整洁易读了。下面是用于渲染导航链接的nav_link()宏:

{% macro nav_link(endpoint, text) -%}
<li class="nav-item {% if request.endpoint
and request.endpoint == endpoint %}active{% endif %}">
<a class="nav-link" href="{{ url_for(endpoint, **kwargs) }}">{{ text }}</a>
</li>
{%- endmacro %}

nav_link()宏接收完整的端点值和按钮文本作为参数,返回完整的导航链接。因为错误页面没有端点值,当渲染错误页面的导航栏时,链接会出现request.endpoint为None的错误。为了避免这个错误,需要在nav_link()宏的if判断中额外添加一个判断条件,确保端点不为None。

借助nav_link宏,渲染导航链接的代码会变得非常简单:

{% from 'macros.html' import nav_link %}
...
<ul class="navbar-nav mr-auto">
{{ nav_link('index', 'Home') }}
{{ nav_link('about', 'About') }}
</ul>
...

不过在personalBlog的模板中我们并没有使用这个nav_link()宏,因为Bootstrap-Flask提供了一个更加完善的render_nav_item()宏,它的用法和我们创建的nav_link()宏基本相同。这个宏可以在模板中通过bootstrap/nav.html路径导入,它支持的常用参数如下所示:

3、Flash消息分类

我们目前的Flash消息应用了Bootstrap的alert-info样式,单一的样式使消息的类别和等级难以区分,更合适的做法是为不同类别的消息应用不同的样式。比如,当用户访问出错时显示一个黄色的警告消息;而不同的提示消息则使用蓝色的默认样式。bootstrap为提醒消息(Alert)提供了8种基本的样式类,即alert-primary、alert-secondary、alert-success、alert-danger、alert-warning、alert-light、alert-dark,如下所示:

要开启消息分类,我们首先要在消息渲染函数get_flashed_messages()中将with_categories参数设为True。这时会把消息迭代为一个类似于“(分类,消息)”的元祖,我们使用消息分类字符来构建样式类,如下所示:

personalBlog/templates/base.html: 渲染分类消息

<main class="container">
{% for message in get_flashed_messages(with_categories=True) %}
<div class="alert alert-{{ message[0] }}" role="alert">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message[1] }}
</div>
{% endfor %} {% block content %}{% endblock content %}
{% block footer %}
<footer>
</footer>
{% endblock footer %}
</main>

样式类通过“alert-{{ message[0] }}”形式构建,所以在调用flash()函数时,消息的类别作为第二个参数传入(primary、secondary、success、danger、warning、light、dark中的一个)。比如,下面的消息使用success分类,在渲染时会使用alert-success样式类:

flash(u'发表成功!', 'success')

如果你不想使用Bootstrap,或是想添加一个自定义分类,可以通过在css文件中添加新的消息样式的css类实现。比如下面的CSS类实现了一个自定义消息样式类alert-matrix:

.alert-matrix{
color: #66ff66;
background-color: #000000;
border-color: #ebccd1;
}

在调用flash()函数时,则使用“matrix”作为分类:

flash(u'发表成功!', 'matrix')

flask实战-个人博客-模板 --的更多相关文章

  1. flask实战-个人博客-虚拟环境、项目结构

    个人博客 博客是典型的CMS(Content Management system,内容管理系统),通常由两部分组成:一部分是博客前台,用来展示开放给所有用户的博客内容:另一部分是博客后台,这部分内容仅 ...

  2. flask实战-个人博客-使用蓝本模块化程序

    使用蓝本模块化程序 实例化flask提供的blueprint类就创建一个蓝本实例.像程序实例一样,我们可以为蓝本实例注册路由.错误处理函数.上下文处理函数,请求处理函数,甚至是单独的静态文件文件夹和模 ...

  3. flask实战-个人博客-编写博客前台

    编写博客前台 博客前台需要开放给所有用户,这里包括显示文章列表.博客信息.文章内容和评论等功能功能. 分页显示文章列表 为了在主页显示文章列表,我们要先在渲染主页模板的index视图的数据库中获取所有 ...

  4. flask实战-个人博客-电子邮件支持

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

  5. flask实战-个人博客-表单

    表单 下面我们来编写所有表单类,personalBlog中主要包含下面这些表单: 登录表单: 文章表单: 评论表单: 博客设置表单: 这里仅介绍登录表单.文章表单.分类表单和评论表单,其他的表单在实现 ...

  6. flask实战-个人博客-数据库-生成虚拟数据 --

    3.生成虚拟数据 为了方便编写程序前台和后台功能,我们在创建数据库模型后就编写生成虚拟数据的函数. 1)管理员 用于生成虚拟管理员信息的fake_admin()函数如下所示: personalBlog ...

  7. flask实战-个人博客-程序骨架、创建数据库模型、临接列表关系 --

    编写程序骨架 personalBlog的功能主要分为三部分:博客前台.用户认证.博客后台,其中包含的功能点如下图所示: 数据库 personalBlog一共需要使用四张表,分别存储管理员(Admin) ...

  8. flask实战-个人博客-视图函数

    视图函数 在上面我们创建了所有必须的模型类.模板文件和表单类.经过程序规划和设计后,我们可以创建大部分视图函数.这些视图函数暂时没有实现具体功能,仅渲染对应的模板,或是重定向到其他视图.以blog蓝本 ...

  9. flask实战-个人博客-使用工厂函数创建程序实例 --

    使用工厂函数创建程序实例 使用蓝本还有一个重要的好处,那就是允许使用工厂函数来创建程序实例.在OOP(Object-Oriented Programming,面向对象编程)中,工厂(factory)是 ...

随机推荐

  1. ORACLE--报错,ORA-38770

    一,问题描述 背景说明,Dg备库需要恢复到三天前,但是期间没有开闪回,临时开始,出现了这个错误.原因分析: To enable Flashback Database, you configure a ...

  2. There is no getter for property named 'id' in 'class java.lang.Integer

    There is no getter for property named 'id' in 'class java.lang.Integer 问题描述: 使用mybatis传入参数, 当参数类型是St ...

  3. Thread.currentThread().setContextClassLoader为什么不生效与java.lang.NoClassDefFoundError之Java类加载的Parent first Classloader

    众所周知,Java的类加载机制采用了双亲委派模型,导致在进行类加载的时候会有多个加载器,这种复杂的机制,有时候会导致‘Exception in thread main java.lang.NoClas ...

  4. WindowsServer -------------部署软件

    1.windowsServer 中创建 lls 在服务器中创建lls       参考 将文件扩展名调出 2.安装数据库 将数据库传递到服务器中 安装sqlserver 数据库 3.在系统中创建文件存 ...

  5. Vue.js 源码分析(十一) 基础篇 过滤器 filters属性详解

    Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化.过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持).过滤器应该被添加在 JavaScrip ...

  6. Shiro 使用 JWT Token 配置类参考

    项目中使用了 Shiro 进行验证和授权,下面是 Shiro 配置类给予参考. 后来并没有使用 Shiro,感觉使用 JWT 还是自己写拦截器比较灵活,使用 Shiro 后各种地方需要魔改,虽然功能也 ...

  7. Nginx 极简入门教程!

    上篇文章和大家聊了 Spring Session 实现 Session 共享的问题,有的小伙伴看了后表示对 Nginx 还是很懵,因此有了这篇文章,算是一个 Nginx 扫盲入门吧! 基本介绍 Ngi ...

  8. scrapy Crawl_spider

    命令行输入:scrapy genspider --list 可以看到scrapy给我们提供的爬虫模板: basiccrawlcsvfeedxmlfeed 一般都是用默认模板生成的spider,如果需要 ...

  9. 《 .NET并发编程实战》阅读指南 - 第13章

    先发表生成URL以印在书里面.等书籍正式出版销售后会公开内容.

  10. 【java】Java多线程总结之线程安全队列Queue【转载】

    原文地址:https://www.cnblogs.com/java-jun-world2099/articles/10165949.html ============================= ...