第十一章 博客文章(一)

一. 提交和显示博客文章

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 %}
<div class="page-header">
    <h1>Hello, {% if current_user.is_authenticated %}{{ current_user.username }}{% else %}Stranger{% endif %}!</h1>
</div>
<div>
    {% if current_user.can(Permission.WRITE) %}
    {{ wtf.quick_form(form) }}
    {% endif %}
</div>
<ul class="posts">
    {% for post in posts %}
    <li class="post">
        <div class="post-thumbnail">
            <a href="{{ url_for('.user', username=post.author.username) }}">
                <img class="img-rounded profile-thumbnail" src="{{ post.author.gravatar(size=40) }}">
            </a>
        </div>
        <div class="post-content">
            <div class="post-date">{{ moment(post.timestamp).fromNow() }}</div>
            <div class="post-author"><a href="{{ url_for('.user', username=post.author.username) }}">{{ post.author.username }}</a></div>
            <div class="post-body">{{ post.body }}</div>
        </div>
    </li>
    {% endfor %}
</ul>
{% 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/<username>')
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 <ul>元素渲染博客文章列表。因此我们可以将生成文章列表的HTML片段移到一个单独的文件中,然后再index.html和user.html中使用Jinjia2提供的include指令将其导入。

app/templates/_posts.html:渲染博客文章列表的模板

<ul class="posts">
    {% for post in posts %}
    <li class="post">
        <div class="post-thumbnail">
            <a href="{{ url_for('.user', username=post.author.username) }}">
                <img class="img-rounded profile-thumbnail" src="{{ post.author.gravatar(size=40) }}">
            </a>
        </div>
        <div class="post-content">
            <div class="post-date">{{ moment(post.timestamp).fromNow() }}</div>
            <div class="post-author"><a href="{{ url_for('.user', username=post.author.username) }}">{{ post.author.username }}</a></div>
            <div class="post-body">{{ post.body }}</div>
        </div>
    </li>
    {% endfor %}
</ul>

注:_posts.html中下划线不是必须的,这是一种习惯写法,以区分完整模板和局部模板。

app/templates/user.html:显示有博客文章的资料页面模板

...
<h3>Posts by {{ user.username }}</h3>
{% include '_posts.html' %}
...

app/templates/index.html也是用{% include '_posts.html' %}进行相应调整。

运行结果(编辑链接和详情链接也请先忽略,后续介绍讲解):

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值