app/models.py:博客文章的数据库模型:Post模型
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
class User(UserMixin, db.Model):
__tablename__ = "users"
...
posts = db.relationship('Post', backref='author', lazy='dynamic')
博客文章包含正文,时间戳以及和User模型之间的一对多关系。
app/main/forms.py:应用首页的博客文章表单:
class PostForm(FlaskForm):
body = TextAreaField("What's on your mind?", validators=[DataRequired()])
submit = SubmitField('Submit')
index()视图函数处理博客文章表单并把以前发布的博客文章列表传给模板,文章列表按时间戳进行降序排列。
app/main/views.py:处理博客文章的首页路由
@main.route('/', methods=['POST', 'GET'])
def index():
form = PostForm()
if current_user.can(Permission.WRITE) and form.validate_on_submit():
post = Post(body=form.body.data, author=current_user._get_current_object())
db.session.add(post)
db.session.commit()
return redirect(url_for('.index'))
posts = Post.query.order_by(Post.timestamp.desc()).all()
return render_template('index.html', form=form, posts=posts)
注意,新文章对象的author属性值为表达式current_user._get_current_object()。变量current_user由Flask-Login提供,与所有上下文变量一样,实现为线程内的代理对象。这个对象的表现类似用户对象,但实际上确是一个轻度包装,包含真正的用户对象。而数据库要真正的用户对象,因此要在代理对象上调用_get_current_object()方法。
app/templates/index.html:显示博客列表的首页模板
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
Hello, {% if current_user.is_authenticated %}{{ current_user.username }}{% else %}Stranger{% endif %}!
{% if current_user.can(Permission.WRITE) %}
{{ wtf.quick_form(form) }}
{% endif %}
{% for post in posts %}
-
{{ moment(post.timestamp).fromNow() }}
{{ post.body }}
{% endfor %}
{% endblock %}
博客文章列表通过HTML无序列表实现,并自定义了一个CSS类。页面左侧会显示作者的小头像,头像和作者的用户名都渲染成链接,指向用户的资料页面。所用的CSS样式都存储在static目录下的style.css文件中:
.profile-thumbnail {
position: absolute;
}
.profile-header {
min-height: 260px;
margin-left: 280px;
}
ul.posts {
list-style-type: none;
padding: 0px;
margin: 16px 0px 0px 0px;
border-top: 1px solid #e0e0e0;
}
ul.posts li.post {
padding: 8px;
border-bottom: 1px solid #e0e0e0;
}
ul.posts li.post:hover {
background-color: #f0f0f0;
}
div.post-date {
float: right;
}
div.post-author {
font-weight: bold;
}
div.post-thumbnail {
position: absolute;
}
div.post-content {
margin-left: 48px;
min-height: 48px;
}
运行结果(请先忽略每篇博客的编辑链接和详情链接):
app/main/views.py:获取博客文章的资料页面路由
@main.route('/user/')
def user(username):
user = User.query.filter_by(username=username).first_or_404()
posts = user.posts.order_by(Post.timestamp.desc()).all()
return render_template('user.html', user=user, posts=posts)
用户发布的博客文章列表通过User.posts关系获取。User.posts返回结果类似于查询对象,因此可以像常规查询对象那样在其上调用过滤器order_by()。
与index.html模板一样,user.html模板也要使用一个HTML
app/templates/_posts.html:渲染博客文章列表的模板
{% for post in posts %}
-
{{ moment(post.timestamp).fromNow() }}
{{ post.body }}
{% endfor %}
注:_posts.html中下划线不是必须的,这是一种习惯写法,以区分完整模板和局部模板。
app/templates/user.html:显示有博客文章的资料页面模板
...
Posts by {{ user.username }}
{% include '_posts.html' %}
...
app/templates/index.html也是用{% include '_posts.html' %}进行相应调整。
运行结果(编辑链接和详情链接也请先忽略,后续介绍讲解):