Flask开发轻博客(六):用户首页和发布博客

作者:chen_h
微信号 & QQ:862251340
微信公众号:coderpai


目录

Flask开发轻博客(一):欢迎来到 Flask 的世界

Flask开发轻博客(二):Flask 模板

Flask开发轻博客(三):Flask 的 Web 表单

Flask开发轻博客(四):数据库

Flask开发轻博客(五):用户登录

Flask开发轻博客(六):用户首页和发布博客

Flask开发轻博客(七):分页


上节回顾

上一章中,我们已经完成了登录系统,因此我们可以使用昵称或邮件登录以及登出。

今天,我们将要完成个人信息页。首先,我们将创建用户信息页,显示用户信息以及最近的 blog。作为其中一部分,我们将会学习到显示用户头像。接着,我们将要用户 web 表单用来编辑用户信息

一、用户信息首页

创建一个用户信息不需要引入新的概念。我们只要创建一个新的视图函数以及与它配套的 HTML 模版。

扫描二维码关注公众号,回复: 2867034 查看本文章

添加用户信息类,并定义用户信息字段(修改forms.py文件):

class AboutMeForm(Form):
    describe = TextAreaField('about me', validators=[
        Required(), Length(max=140)])
    submit = SubmitField('YES!')

添加用户新信息的视图函数(文件 app/views.py )

@app.route('/user/<int:user_id>', methods=["POST", "GET"])
@login_required
def users(user_id):
    form = AboutMeForm()
    user = User.query.filter(User.id == user_id).first()
    if not user:
        flash("The user is not exist.")
        redirect("/index")
    blogs = user.posts.all()

    return render_template(
        "user.html",
        form=form,
        user=user,
        blogs=blogs)

我们用于这个视图函数的装饰器与之前的有些不同。在这个例子中,我们有一个 参数 在里面,用 来表示。这将转化为一个同名的参数添加到视图函数。比如当客户端以URL /user/1 请求的时候,视图函数将收到一个 user_id = 1参数从而而被调用。

视图函数的实现没有让人惊喜的。首先,我们使用接收到参数 user_id 试着从数据库载入用户。如果没有找到用户的话,我们将会抛出错误信息,重定向到主页,我们还添加了@login_required装饰器,如果没有登陆的用户,向通过URL直接访问该页面,那么我们会直接在页面上报错,阻止其访问。

一旦我们找到用户,我们把它传入到 render_template 调用,并且传入user.posts.all()找出的该用户的blogs。注意如果没有找到用户,模板会显示小小的提示The user is not exist.,并跳转到主页。

我们最初的视图模版是十分简单的(创建文件 app/templates/user.html )

{% extends "base.html" %}

{% block content %}
<p>Name: {{ user.nickname }}</p>
<p>Email: {{ user.email }}</p>

<hr>
{% if blogs | length %}
    {% for blog in blogs %}
    <p>{{ blog.body }}</p>
    <p>{{ blog.timestamp.strftime("%a, %d %b %Y %H:%M:%S") }}</p>
    <hr />
    {% endfor %}
{% else %}
    <p style="color:blue;">the guy is so lazy.....</p>
{% endif %}
{% endblock %}

用户信息页现在已经完成了,但是缺少对它的链接。为了让用户很容易地检查他的或者她的信息,我们直接把用户信息页的链接放在导航栏中(修改文件 app/templates/base.html)

<div>Microblog: 
    <a href="{{ url_for('index') }}">Home</a>
    {% if not current_user.is_authenticated() %}
    | <a href="{{ url_for('login') }}">Log in</a>
    or <a href="{{ url_for('sign_up') }}">Sign up</a>
    {% else %}
    | <a href="{{ url_for('users', user_id = current_user.id) }}">Profile</a>
    | <a href="{{ url_for('logout') }}">Logout</a>
    {% endif %}
</div>

试试应用程序吧。注册登陆后点击导航栏中的个人资料链接,会把你带到用户信息页。但是我们还没有添加博客发布页面,所以可能会出现:the guy is so lazy.....

二、博客发布

好的,我们就下来就来实现 publish blogs 的功能。

首先在 forms.py 文件中添加博客内容的字段:

class PublishBlogForm(Form):
    body = TextAreaField('blog content', validators=[Required()])
    submit = SubmitField('Submit')

并我们需要在app/views.py中加入如下函数:

from string import strip
import datetime

from forms import LoginForm,  SignUpForm, AboutMeForm, PublishBlogForm

@app.route('/publish/<int:user_id>', methods=["POST", "GET"])
@login_required
def publish(user_id):
    form = PublishBlogForm()
    posts = Post()
    if form.validate_on_submit():
        blog_body = request.form.get("body")
        if not len(strip(blog_body)):
            flash("The content is necessray!")
            return redirect(url_for("publish", user_id=user_id))
        posts.body = blog_body
        posts.timestamp = datetime.datetime.now()
        posts.user_id = user_id

        try:
            db.session.add(posts)
            db.session.commit()
        except:
            flash("Database error!")
            return redirect(url_for("publish", user_id=user_id))

        flash("Publish Successful!")
        return redirect(url_for("publish", user_id=user_id))

    return render_template(
        "publish.html",
        form=form)

同样接收当前用户的user_id用于填充Post表的user_id字段,以便在用户主页显示该用户所属的blogs。为了防止blog内容为空,除了在forms.py里添加validator的限制外,我们还要在后台再一次对输入数据的验证strip(blog_body)就是为了防止用户只输入空格的情况,它会将字符串两边的空格去掉,如果内容仅仅为空格的话,那么长度肯定是为0的,一旦这种事情发生了,就立即报错,并刷新当前页面。

将数据库的对应的字段赋值完毕之后,使用db.session.add(posts)db.session.commint()将值写入数据库中,因为操作数据库的时候可能会出现一些意想不到的问题,所以我们应该用try….except….
来处理这些问题,提高适用性。

publish的页面十分的简单,只是需要一个TextAreaFiled,和submit就行了。(app/publish.html)


{% extends "base.html" %}

{% block content %}
<form action="{{ url_for("publish", user_id=current_user.id) }}" method="POST" name="publish">
    {{ form.hidden_tag() }}
<p>{{ form.body }}</p>
<p>{{ form.submit }}</p>
</form>
{% endblock %}

效果图如下:

三、在子模板中重用

 1. 非常实用的小技巧

我们已经实现了用户信息页,它能够显示用户的 blog。我们的首页也应该显示任何一个用户这个时候的 blog 。这样我们有两个页需要显示用户的 blog。当然我们可以直接拷贝和复制处理渲染 blog 的模板,但这不是最理想的。因为当我们决定要修改 blog 的布局的时候,我们要更新所有使用它的模板。

相反,我们将要制作一个渲染 blog 的子模板,我们在使用它的模板中包含这个子模板。

我们创建一个 blog 的子模板,这是一个再普通不过的模板(文件 /app/templates/post.html):

    <table>
        <tr valign="top">
            <td>![]({{post.author.avatar(50)}})</td><td><i>{{post.author.nickname}} says:</i><br>{{post.body}}</td>
        </tr>
    </table>

接着我们使用 Jinja2 的 include 命令在我们的用户模板中调用这个子模板(文件app/templates/user.html)

{% extends "base.html" %}

{% block content %}
  <table>
      <tr valign="top">
          <td><h1>User: {{ user.nickname }}</h1></td>
          <td><h1>User: {{ user.email }}</h1></td>
      </tr>
  </table>
<hr>
{% for post in posts %}
    {% include 'post.html' %}
{% endfor %}
{% endblock %}

一旦我们有一个功能上完全实现的首页,我们将会调用这个子模板,但是现在不准备这么做,将会把它留在后面的章节。

四、更多有趣的信息

尽然我们现在已经有一个不错的用户信息页,我们还有更多的信息需要在上面显示。像用户自我说明可以显示在用户信息页上,因此我们将会让用户写一些自我介绍,并将它们显示在用户资料页上。我们也将追踪每个用户访问页面的最后一次的时间,因此我们将会把它显示在用户信息页上。

为了增加这些,我们必须开始修改数据库。更具体地说,我们必须在我们的 User 类上增加两个字段(文件 app/models.py)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(15), index=True, unique=True)
    email = db.Column(db.String(128), index=True, unique=True)
    role = db.Column(db.SmallInteger, default=ROLE_USER)
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime)

前面的章节我们已经讲述过数据库的迁移。因此为了增加这两个新字段到数据库,需要运行升级脚本

python db_migrate.py

脚本会返回如下信息:

New migration saved as db_repository/versions/003_migration.py
Current database version: 3

我们的两个新字段加入到我们的数据库。如果我们没有迁移的支持,我们必须手动地编辑数据库,最差的方式就是删除表再重新创建。接着,让我们修改用户信息页模板来展示这些字段(文件 app/templates/user.html):

{% extends "base.html" %}

{% block content %}
<p>Name: {{ user.nickname }}</p>
<p>Email: {{ user.email }}</p>

{% if user.about_me %}
<p onclick="about_me()">about me: {{ user.about_me }}</p>
{% else %}
<p style="color:#4499EE;" onclick="about_me()">about me: I'm a person. ---- this info from the system.</p>
{% endif %}

<div id="aboutMe" style="display:none;">
    <form action="{{ url_for('about_me', user_id=current_user.id) }}" method="POST">
        {{ form.hidden_tag() }}
        {{ form.describe }}
        {{ form.submit }}
    </form>
</div>

<p style="color:#4c4c4c;">last log: {{ user.last_seen.strftime("%a, %d %b %Y %H:%M:%S") }}</p>

<a href="{{ url_for('publish', user_id=user.id) }}">Want to publish blogs?</a>

<hr />
{% if blogs | length %}
    {% for blog in blogs %}
    <p>{{ blog.body }}</p>
    <p>{{ blog.timestamp.strftime("%a, %d %b %Y %H:%M:%S") }}</p>
    <hr />
    {% endfor %}
{% else %}
    <p style="color:blue;">the guy is so lazy.....</p>
{% endif %}

{% endblock %}

{% block js %}
<script>
function about_me() {
    target = document.getElementById("aboutMe");
    if (target.style.display == "block") {
        target.style.display = "none";
    } else {
        target.style.display = "block";
    }
}
</script>
{% endblock %}

注意,在user.html中多出了一段js代码,这段js代码可以使的我们点击about me的时候,弹出一个编辑框以便我们修改自己的个人描述,当然我们得在base.html中添加一个block:

<html>
  <head>
    {% if title %}
    <title>{{title}} - microblog</title>
    {% else %}
    <title>microblog</title>
    {% endif %}
  </head>
  <body>
    <div>Microblog: 
        <a href="{{ url_for('index') }}">Home</a>
        {% if not current_user.is_authenticated() %}
        | <a href="{{ url_for('login') }}">Log in</a>
        or <a href="{{ url_for('sign_up') }}">Sign up</a>
        {% else %}
        | <a href="{{ url_for('users', user_id = current_user.id) }}">Profile</a>
        | <a href="{{ url_for('logout') }}">Logout</a>
        {% endif %}
    </div>
    <hr />
    {% with messages = get_flashed_messages() %}
    {% if messages %}
    <ul>
        {% for message in messages %}
        <li>{{ message }}</li>
        {% endfor %}
    </ul>
    {% endif %}
    {% endwith %}
    {% block content %}{% endblock %}
  </body>

  {% block js %}{% endblock %}
</html>

最后,我们希望输入新的个人信息,击yes后,能将够刷新当前页面并且显示新的个人描述,我们看看views.py:

@app.route('/user/about-me/<int:user_id>', methods=["POST", "GET"])
@login_required
def about_me(user_id):
    user = User.query.filter(User.id == user_id).first()
    if request.method == "POST":
        content = request.form.get("describe")
        if len(content) and len(content) <= 140:
            user.about_me = content
            try:
                db.session.add(user)
                db.session.commit()
            except:
                flash("Database error!")
                return redirect(url_for("users", user_id=user_id))
        else:
            flash("Sorry, May be your data have some error.")
    return redirect(url_for("users", user_id=user_id))

我们这里和原来写的不太一样,原来我们的表单提交都是在当前页面进行处理的,当我们点击yes后,会通过post的方式将数据发送到/user/about-me/2页面上去处理,所以我们使用request.method == “POST”进行判定之后,获取表单数据,当然也要判断content的长度,并进行相应的处理,最后跳转回用户主页面。

猜你喜欢

转载自blog.csdn.net/CoderPai/article/details/80520906