PythonFlask搭建个⼈博客详细回顾—(3.1博客前台页⾯)
个⼈博客Demo: .
GitHub项⽬完整链接:
回顾上⼀节主要讲了以下个⽅⾯内容:
⼯⼚函数create_app()以及作⽤于app实例的处理函数
定义博客表单类的 ⽂件
定义辅助函数的 ⽂件
3.1.1 基模板 base.html
基模板定义了博客的基本样式,导航栏和页脚,其他页⾯都要继承⾃基模板
除了在基模板中定义基本html结构,我们还在基模板中加载css,javascript⽂件以及bootstrap,moment.js所需的资源⽂件,并创建⼀些块block⽤于⼦模板的继承,修改与追加
基模板中还涉及到⽤户登录和未登录时⽤current_user.is_authenticated显⽰不同的组件
HTML代码中涉及许多bootstrap样式,可参考菜鸟教程的bootstrap4教程:
先贴上代码,再进⾏进⼀步阐述:
{% from 'bootstrap/nav.html' import render_nav_item %}
<!DOCTYPE html>
<html lang="en">
<head>
{% block head%}
<meta charset="UTF-8">
<title>{% block title %}{% endblock title %} - {{ admin.blog_title|default('MyBlog')}}</title>
<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">
{% endblock head%}
</head>
<body>
{% block nav%}
<div class="row">
<div class="col-sm-12">
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<div class=" container">
<a class="navbar-brand"href="{{url_for('blog.index')}}">{{ admin.blog_title|default('Blog') }}</a>
<ul class="nav navbar-nav navbar-right">
<li class="nav-item dropdown ">
<a href="#" class="nav-link dropdown-toggle "role="button"
data-toggle="dropdown" >⽂章分类<span class="caret"></span></a>
<div class="dropdown-menu ">
{% for category in categories%}
<a class="dropdown-item"
href="{{ url_for('blog.show_category',category_id=category.id)}}">{{ category.name }}</a>
{% endfor %}
</div>
</li>
{% if current_user.is_authenticated %}
<li class="nav-item dropdown ">
<a href="#"class="nav-link dropdown_toggle"data-toggle="dropdown"role="button">
新建<span class="caret"></span>
</a>
<div class="dropdown-menu">
<a class="dropdown-item"href="{{ url_for('w_post')}}">⽂章</a>
<a class="dropdown-item"href="{{ url_for('w_category')}}">分类</a>
<a class="dropdown-item"href="{{ url_for('w_category')}}">分类</a>
</div>
</li>
<li class="nav-item dropdown">
<a href="#"class="nav-link dropdown-toggle"data-toggle="dropdown"role="button">
管理<span class="caret"></span>
{% if unread_comments %}
<span class="badge badge-success">new</span>
{% endif %}
</a>
<div class="dropdown-menu">
<a class="dropdown-item"href="{{ url_for('admin.manage_post')}}">⽂章</a>
<a class="dropdown-item"href="{{ url_for('admin.manage_category')}}">分类</a>
<a class="dropdown-item"href="{{ url_for('admin.manage_comments')}}">评论
{% if unread_comments %}
<span class="badge badge-success">{{ unread_comments }}</span>
{% endif %}
</a>
</div>
</li>
{{ render_nav_item('admin.settings', '设置')}}
{% endif %}
</ul>
</div>
</nav>
</div>
</div>
{% endblock nav %}
<main class="container">
{% for message in get_flashed_messages(with_categories=True) %}
<!--alert:信息提⽰框-->
<div class="alert alert-{{ message[0] }}"role="alert">
<!--设置消息提⽰框关闭按钮-->
<button type="button"class="close"data-dismiss="alert">×</button>
{{ message[1] }}
</div>
{% endfor %}
{% block content %}
{% endblock content%}
</main>
{% block footer %}
<div class="row">
<div class="container">
<hr>
<a class="text-dark"href="#"title="created by HJ"><small>© 2019 HJ</small></a> -
<a class="text-dark"href="github/theminimize"title="HJ's GitHub count"><small>GitHub</small></a> -
<a class="text-dark"href="blog.csdn/one_Salted_FishGG"title="HJ's CSDN count"><small>CSDN</small></a>        {% if current_user.is_authenticated %}
<a class="text-dark float-right "href="{{url_for('login.logout', next=request.full_path)}}"><small>注销</
small></a>
{% else %}
<a class=" text-dark float-right"href="{{url_for('login.login', next=request.full_path)}}"><small>登录</small></a>
{% endif %}
</div>
</div>
{% endblock footer %}
{% 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='js/bootstrap.min.js') }}"></script>
<script type="text/javascript"src="{{ url_for('static', filename='js/script.js') }}"></script>
<!-- 引⼊Moment.js库,⽤于渲染时间 -->
{{ moment.include_moment(local_js=url_for('static', filename='js/moment-with-locales.min.js')) }}
{% endblock %}
</body>
</html>
{% block head%}块⾥⾯主要定义⽹页标题{% block title %}和CSS资源位置,JS资源⽂件{% scripts %}独⽴在最下⾯的原因是⽅便追加新的js资源
{% block nav %}定义导航栏代码,⽤户未登录或游客看到的导航栏包含博客标题以及⽂章分类下拉菜单。⽤户登录后在导航栏追加显⽰新建,管理和设置三个下拉菜单。设置下拉菜单采⽤bootstrap-flask提供的快速渲染样式render_nav_item进⾏快速渲染。
{% block nav%}块下⽅< div >表⽰消息闪现位置
{% block content %}块空⽩,⽅便⼦模板继承添加内容
{% block footer %}页脚添加上友情链接和登录/注销按钮,登录注销按钮附加 next参数保存上⾐页⾯完整链接,⽤于返回上⼀页
3.1.2 ⽂章列表显⽰局部模板 _post.html
局部模板能够很⽅便的插⼊到其他模板之中,由于⽂章列表显⽰在博客主页以及⽂章分类页都能⽤到,因此将其独⽴成局部模板,减少代码重复
代码如下:
{% if posts %}
<ul class="list-group">
{% for post in posts %}
<li class="list-group-item border border-0"id="list-group-padding">
<h2><a href="{{ url_for('.show_post', post_id=post.id)}}"class="text-dark">{{ post.title }}</a></h2>
<p >{{ post.body|truncate|striptags }}
<a href="{{ url_for('.show_post', post_id=post.id)}}"><small>Read More</small></a>
</p>
菜鸟教程python函数<!--<p><small> {{ post.timestamp }}</small></p>-->
<p class="text-muted"><small>{{ moment(post.timestamp).format('LL') }}</small></p>
</li>
{% endfor %}
</ul>
{% else %}
<!-- 没有⽂章时,则tip提⽰⽆⽂章 -->
<div class="tip">
<h5>这⾥⼀篇⽂章也没有...</h5>
{% if current_user.is_authenticated %}
<a href="{{ url_for('w_post') }}">马上写⼀篇...</a>
{% endif %}
</div>
{% endif %}
{% if posts %}⽤于判断是否有⽂章,有则通过for循环迭代列表显⽰⽂章,没有则tip提⽰⽆⽂章
将⽂章标题和read more按钮都定向到⽂章详情页⾯,⽂章摘要通过{{ post.body| truncate| striptags}}实现,truncate⽤于裁剪字符串, striptags⽤于清除html标签,因为富⽂本编辑器ckeditor采⽤html标记⽂本
3.1.3 博客主页index和分类详情页category
博客主页和分类详情页都插⼊了局部模板_post.html因此放在⼀起,同时将与视图函数⼀起讲,代码都
⽐较少
index.html代码如下:
{% extends 'base.html' %}
{% from 'bootstrap/pagination.html' import render_pagination%}
{% block title %}主页{% endblock title%}
{% block content %}
<div class="row">
<div class="container ">
{% include 'blog/_post.html'%}
{% if posts %}
{{ render_pagination(pagination) }}
{% endif %}
</div>
</div>
{% endblock content%}
index模板继承base.html基模板,{% block title %}更改标题,在{% content %}块内⽤{% include xxx%}插⼊局部模板
通过bootstrap提供的快熟渲染样式,渲染分页标签{{ render_pagination(pagination) }},渲染分页标签需要传⼊pagination分页对象,由于视图函数控制模板渲染(render_template),因此需要在视图函数中定义分页对象并传⼊模板
bluemaps/blog.py下index视图函数代码如下:
from flask import Blueprint, request, current_app, render_template
dels import Post
blog_bm = Blueprint('blog', __name__)
@ute('/')
def index():
page = ('page',1,type=int)# 从查询字符串获取当前页数
per_page = fig['BLOG_POST_PER_PAGE']# 每页⽂章数
pagination = der_by(Post.timestamp.desc()).paginate(page, per_page=per_page)# 分页对象
posts = pagination.items # 当前页数的记录列表
return render_template('blog/index.html', pagination=pagination, posts=posts)
为了实现分页,查询执⾏参数order_by()后加⼊pagiante()⽅法,它接收两个主要参数⽤来决定把记录分成⼏页(per_page)以及返回哪⼀页的记录(即查询字符串获取的当前页数参数page)
对分页对象pagination调⽤items属性会以列表形式返回对应页数的记录,然后在模板中for循环迭代显
⽰列表中记录即可
分类⽂章页的视图函数和模板代码类似,不在赘述
分类⽂章页category.html的代码如下:
{% extends 'base.html' %}
{% from 'bootstrap/pagination.html' import render_pagination %}
{% block title %}{{ category.name }}{% endblock title %}
{% block content %}
<div class="page-header">
<h1>{{ category.name }} : {{ posts|length}} 篇⽂章</h1>
</div>
{% include 'blog/_post.html' %}
{{ render_pagination(pagination) }}
{% endblock content %}
分类⽂章页视图函数代码如下:
@ute('/category/<int:category_id>', methods=['GET','POST'])# <int:xxx>为url转换器,将id值转换为整型
def show_category(category_id):
category = _or_404(category_id)
page = ('page',1,type=int)
per_page = fig['MYBLOG_POST_PER_PAGE']
pagination = Post.query.with_parent(category).order_by(Post.timestamp.desc()).paginate(page, per_page=per_page)
posts = pagination.items
return render_template('blog/category.html', category=category, pagination=pagination, posts=posts)
视图函数需要传⼊指定分类id值(category_id)⽤于显⽰指定分类记录,获取⽅式为:模板中重定向到show_category视图时,传⼊的参数,如category_id = ,⽽模板中的category对象⼜是从哪来的,从for循环迭代对象categories中获取,categories⼜在⼯⼚函数注册的模板上下⽂中定义
如基模板中导航栏块中for循环迭代categories:
- 以及__init__.py⽂件中对模板上下⽂的注册包含categories对象:
# 模板上下⽂
def register_template_context(app):
@t_processor
def make_template_context():
admin = Admin.query.first()
categories = der_by(Category.name).all()
posts = der_by(Post.timestamp).all()
if current_user.is_authenticated:
unread_comments = Comment.query.filter_by(reviewed=False).count()
else:
unread_comments =None
return dict(
admin=admin,
categories=categories,
posts=posts,
unread_comments=unread_comments
)
3.1.4 ⼩结
视图函数与模板之间参数传递,渲染的先后关系需要弄清楚,HTML页⾯结构还需多联系
后⾯将继续介绍前台页⾯中⽂章详情页和登录页⾯的实现

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。