Flask学习之六 个人资料和头像

英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-vi-profile-page-and-avatars

中文翻译地址:http://www.pythondoc.com/flask-mega-tutorial/profile.html

开源中国社区:http://www.oschina.net/translate/the-flask-mega-tutorial-part-vi-profile-page-and-avatars

备注:我是三个一起看的,有些部分的中文翻译太拗口而且还有错,因此我选择是比较清晰的中文解释,而有些部分是直接翻译英文博客。

上一部分:Flask学习之五 用户登录

一、用户资料页面

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

这里就是视图函数(文件 app/views.py):

@app.route(‘/user/<nickname>‘)
@login_required
def user(nickname):
    user = User.query.filter_by(nickname = nickname).first()
    if user == None:
        flash(‘User ‘ + nickname + ‘ not found.‘)
        return redirect(url_for(‘index‘))
    posts = [
        { ‘author‘: user, ‘body‘: ‘Test post #1‘ },
        { ‘author‘: user, ‘body‘: ‘Test post #2‘ }
    ]
    return render_template(‘user.html‘,
        user = user,
        posts = posts)

这里的@app.route和之前有点不一样,有了一个参数 <nickname>

在函数里面它会转化成跟它同名的参数,当用户有请求的时候,例如这样的一个URL:URL/user/miguel,次视图函数就会识别为有一个名为nickname值为‘miguel‘的参数,即nickname = ‘miguel‘。

上面的实现过程很简单,首先把转化后的 nickname 参数作为条件,尝试着从数据库里载入用户。如果没有查询到数据,就给用户一个错误的提示并且重定向到主页去。

一旦找到了该用户,就把它传入到 render_template 调用,并且传入一些伪造的 blog。注意在用户信息页上只会显示该用户的 blog,因此伪造的 blog 的 author 域必须正确。

这部分最初的视图模版是十分简单的(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>User: {{user.nickname}}!</h1>
<hr>
{% for post in posts %}
<p>
  {{post.author.nickname}} says: <b>{{post.body}}</b>
</p>
{% endfor %}
{% endblock %}

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

    <div>Microblog:
        <a href="{{ url_for(‘index‘) }}">Home</a>
        {% if g.user.is_authenticated() %}
        | <a href="{{ url_for(‘user‘, nickname=g.user.nickname) }}">Your Profile</a>
        | <a href="{{ url_for(‘logout‘) }}">Logout</a>
        {% endif %}
    </div>

现在可以运行试一下,当然,记住得先登录

因为我们还没有到任何用户的信息页的链接,因此你必须手动键入你想要看到的用户信息的 URL。比如,你可以键入 http://localhost:5000/user/miguel,查看 miguel 用户信息。因为我们的blog是伪造的,所以不管哪个用户,你看到的话都是一样的。而如果那个用户也不在数据库中,那么就会看见重定向回到了index页面。

二、头像

不需要在我们自己的服务器处理大量的上传图片,我们依赖 Gravatar 服务为我们提供用户头像。(Gravatar好像已经被墙,反正我上不去)

因为返回一个头像是与用户相关的任务,我们把它放在 User 类(文件 app/models.py):

from hashlib import md5
# ...
class User(db.Model):
    # ...
    def avatar(self, size):
        return ‘http://www.gravatar.com/avatar/%s?d=mm&s=%d‘ % (md5(self.email.encode(‘utf-8‘)).hexdigest(), size)

User 的方法 avatar 返回用户图片的 URL,以像素为单位缩放成要求的尺寸。

有了 Gravatar 服务的协助,很容易处理头像。你只需要创建一个用户邮箱的 MD5 哈希,然后将其加入 URL中,像上面你看见的。在邮箱 MD5 后,你还需要提供一个定制头像尺寸的数字。d=mm 决定什么样的图片占位符当用户没有 Gravatar 账户。mm 选项将会返回一个“神秘人”图片,一个人灰色的轮廓。s=N 选项要求头像按照以像素为单位的给定尺寸缩放。

Gravatar 官方文档 对 avatar URL 有着更加详细的解释。

现在我们的 User 类知道怎样返回一个头像图片,我们把它融入到用户信息页的布局中(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
  <table>
      <tr valign="top">
          <td><img src="{{ user.avatar(128) }}"></td>
          <td><h1>User: {{ user.nickname }}</h1></td>
      </tr>
  </table>
  <hr>
  {% for post in posts %}
  <p>
    {{ post.author.nickname }} says: <b>{{ post.body }}</b>
  </p>
  {% endfor %}
{% endblock %}

User 类负责返回头像是一个很巧妙的事情,如果有一天决定不想要 Gravatar 头像,我们只要重构 avatar 返回不同的 URLs(即使指向我们自己的服务器,如果我们想要自己的头像服务器),所有我们的模版将会自动地开始显示新的头像。

我的邮箱没在Gravatar上注册过,所以返回的就是默认头像啦。

备注:如果你们和我一样懒得FQ,就修改URL,将Gravatar头像的服务器变为国内的,放在国内知名公司的服务器上或者专业的CDN服务器上。

User 类(文件 app/models.py):

from hashlib import md5
# ...
class User(db.Model):
    # ...
    def avatar(self, size):
        return ‘http://gravatar.duoshuo.com/avatar/%s?d=mm&s=%d‘ % (md5(self.email.encode(‘utf-8‘)).hexdigest(), size)

为什么用这个URL,见这篇文章 Gravatar头像不显示完美解决方案

三、在子模板中重用

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

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

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

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

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

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
  <table>
      <tr valign="top">
          <td><img src="{{ user.avatar(128) }}"></td>
          <td><h1>User: {{ user.nickname }}</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(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    posts = db.relationship(‘Post‘, backref=‘author‘, lazy=‘dynamic‘)
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime)

为了增加这两个新字段到数据库,需要运行升级脚本:

./db_migrate.py

接着,让我们修改用户信息页模板来展示这些字段(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
  <table>
    <tr valign="top">
      <td><img src="{{user.avatar(128)}}"></td>
      <td>
        <h1>User: {{user.nickname}}</h1>
          {% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
          {% if user.last_seen %}<p><i>Last seen on: {{ user.last_seen }}</i></p>{% endif %}
      </td>
    </tr>
  </table>
  <hr>
  {% for post in posts %}
    {% include ‘post.html‘ %}
  {% endfor %}
{% endblock %}

注意:利用 Jinja2 的条件语句来显示这些字段,只有当它们被设置的时候才会显示出来。

最后显示的字段(last_seen)就特别的好处理。记得在之前的章节中,我们创建了一个 before_request 函数,用来注册登录的用户到全局变量 flask.g 中。这个函数可以用来在数据库中更新用户最后一次的访问时间(文件 app/views.py):

from datetime import datetime
# ...
@app.before_request
def before_request():
    g.user = current_user
    if g.user.is_authenticated():
        g.user.last_seen = datetime.utcnow()
        db.session.add(g.user)
        db.session.commit()

如果你登录到你的信息页,最后出现时间会显示出来。每次刷新页面,最后出现时间都会更新,因为每次浏览器在发送请求之前,before_request 函数都会在数据库中更新时间。

注意的是我们是以标准的 UTC 时区写入时间。我们在之前的章节中讨论过这个问题,因此我们将会以 UTC 格式写入所有时间内容以保证它们的一致性。这种时间形式在前台显示,看起来会很别扭。我们将会在后面的章节中修正这种显示问题。

要显示用户的关于我的信息,我们必须给他们编辑的地方,就是“编辑个人信息”页面。

五、编辑用户信息

新增一个用户信息表单是相当容易的。我们开始创建网页表单(文件 app/forms.py):

from flask.ext.wtf import Form
from wtforms import StringField, BooleanField, TextAreaField
from wtforms.validators import DataRequired, Length

class EditForm(Form):
    nickname = StringField(‘nickname‘, validators=[DataRequired()])
    about_me = TextAreaField(‘about_me‘, validators=[Length(min=0, max=140)])

视图模板(文件 app/templates/edit.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
  <h1>Edit Your Profile</h1>
  <form action="" method="post" name="edit">
      {{form.hidden_tag()}}
      <table>
          <tr>
              <td>Your nickname:</td>
              <td>{{ form.nickname(size=24) }}</td>
          </tr>
          <tr>
              <td>About yourself:</td>
              <td>{{ form.about_me(cols=32, rows=4) }}</td>
          </tr>
          <tr>
              <td></td>
              <td><input type="submit" value="Save Changes"></td>
          </tr>
      </table>
  </form>
{% endblock %}

视图函数(文件 app/views.py):

from forms import LoginForm, EditForm

@app.route(‘/edit‘, methods=[‘GET‘, ‘POST‘])
@login_required
def edit():
    form = EditForm()
    if form.validate_on_submit():
        g.user.nickname = form.nickname.data
        g.user.about_me = form.about_me.data
        db.session.add(g.user)
        db.session.commit()
        flash(‘Your changes have been saved.‘)
        return redirect(url_for(‘edit‘))
    else:
        form.nickname.data = g.user.nickname
        form.about_me.data = g.user.about_me
    return render_template(‘edit.html‘, form=form)

方便用户编辑,我们需要在用户的个人资料页面添加一个到此页面的链接地址(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
  <table>
      <tr valign="top">
          <td><img src="{{ user.avatar(128) }}"></td>
          <td>
              <h1>User: {{user.nickname}}</h1>
              {% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
              {% if user.last_seen %}<p><i>Last seen on: {{ user.last_seen }}</i></p>{% endif %}
              {% if user.id == g.user.id %}<p><a href="{{ url_for(‘edit‘) }}">Edit</a></p>{% endif %}
          </td>
      </tr>
  </table>
  <hr>
  {% for post in posts %}
      {% include ‘post.html‘ %}
  {% endfor %}
{% endblock %}

编辑保存后:

再看回资料页:

时间: 2024-10-07 10:47:03

Flask学习之六 个人资料和头像的相关文章

Flask学习之六——用户认证

1. 密码安全性 使用Werkzeug实现密码hash generate_password_hash(password, method, salt_length) 将原始密码作为输入,以字符串形式输出密码的hash值,输出的值可保存在用户数据库中.method 和 salt_length的默认值就能满足大多数需求. check_password_hash(hash, password) 将数据库中取回的密码hash和用户输入的密码比较,如果密码正确则返回Trueapp/models.py:在Us

Flask 学习(四)静态文件

Flask 学习(四)静态文件 动态 web 应用也需要静态文件,一般是 CSS 和 JavaScript 文件.理想情况下你的服务器已经配置好提供静态文件的服务. 在开发过程中, Flask 也能做好这个工作. 静态文件引用 我们先来看下普通的 html 引用静态文件,如 css(js也同样,就不多加示例了),以下为一简单实例,直接打开html: flask 处理 —— static 若直接将该html 当成 flask 模板,相对路径自然就失效了,静态文件将不会被成功读取. 那在flask中

OpenCV学习之六: 使用方向梯度直方图估计图像旋转角度

在备份ltedecoder程序时,需要把此目录拷由到bak目录下,但decoder目录下有个大文件,不需要备份,还有日志问题,也不需要备份,如何实现呢?? 方法: cd /source-dir find . -name .snapshot -prune -o -print0 | cpio -pmd0 /dest-dir 解释: This command copies the contents of /source-dir to /dest-dir, but omits files and dir

PGM学习之六 从有向无环图(DAG)到贝叶斯网络(Bayesian Networks)

本文的目的是记录一些在学习贝叶斯网络(Bayesian Networks)过程中遇到的基本问题.主要包括有向无环图(DAG),I-Maps,分解(Factorization),有向分割(d-Separation),最小I-Maps(Minimal I-Maps)等.主要参考Nir Friedman的相关PPT. 1  概率分布(Probability Distributions) 令X1,...,Xn表示随机变量:令P是X1,...,Xn的联合分布(joint distribution).如果每

android学习之路资料集合

版权声明:本文为 stormzhang 原创文章,可以随意转载,但必须在明确位置注明出处!!! 这篇博客背后的故事 一路走来很不容易,刚好知乎上被人邀请回答如何自学android编程, 就借这个机会在知乎上记录一路走来的历程,很励志,希望能给那些正在或准备走编程行业的人一些正能量,内容有点长,感兴趣的可以当做励志小说阅读吧. 收到一些朋友的微博私信,说能不能给Android新手们一些指导,我只能说指导谈不上,毕竟我也很多东西正在学习中,与此同时一大学同学准备转行Android,可以说是从头开始,

[ZHUAN]Flask学习记录之Flask-SQLAlchemy

From: http://www.cnblogs.com/agmcs/p/4445583.html Flask-SQLAlchemy库让flask更方便的使用SQLALchemy,是一个强大的关系形数据库框架,既可以使用orm方式操作数据库,也可以使用原始的SQL命令. Flask-Migrate 是一个数据迁移框架,需要通过Flask-script库来操作. 一.配置Flask-SQLAlchemy 程序使用的数据库地址需要配置在SQLALCHEMY_DATABASE_URI中,SQLALch

Flask 学习(一)概述及安装

Flask 概述及安装 Flask 简介 Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 . 官方网址 :http://flask.pocoo.org/ 了解 Flask:首先,Flask 是Python 的一个Web 应用框架:其次,它是“微型”的 . 比起同类现有的web框架(如:Django),Flask 并不包含数据库抽象层,表单验证等.Flask 旨在保持代码简洁且易于扩展(Flask源码十

机器学习(Machine Learning)&amp;深度学习(Deep Learning)资料

机器学习(Machine Learning)&深度学习(Deep Learning)资料 <Brief History of Machine Learning> 介绍:这是一篇介绍机器学习历史的文章,介绍很全面,从感知机.神经网络.决策树.SVM.Adaboost到随机森林.Deep Learning. <Deep Learning in Neural Networks: An Overview> 介绍:这是瑞士人工智能实验室Jurgen Schmidhuber写的最新版本

CMG.SUITE.V2012.10全套学习教程+手册资料

CMG.SUITE.V2012.10全套学习教程手册资料.zip油藏数值模拟软件 CMG Suite v2012.0 Win64-ISO 1DVDCMG Suite v2012.0—最大的独立开发的油藏数值模拟软件 为油/气藏的建模与仿真提供实际的解决办法,先进的Windows ?软件,先进的采收率(提高采收率/返回抑制的)进程,油藏工程,为世界各地的客户提供咨询,培训和技术支持. chemcad.v6.1.2Chemcad.v6.01(lic解密,计算好用) CambridgeSoft.Che