Python的Flask框架中实现登录用户的个人资料和头像的教程


Posted in Python onApril 20, 2015

用户资料页面

在用户资料页面,基本上没有什么特别要强调和介绍的新概念。只需要创建一个含有HTML的新视图函数模板页面即可。

下面是视图函数(项目目录/views.py):
 

@app.route('/user/<nickname>')
@login_required
def user(nickname):
  user = User.query.filter_by(nickname = nickname).first()
  if user == None:
    flash('不存在用户:' + nickname + '!')
    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参数作为条件,尝试着从数据库里把此用户的数据调用出来。如果没有查询到数据,我们就像之前那样,给用户一个错误的提示并且跳转到主页去。

一旦我们找到了改用户,我们就在模板下面来显示该用户的文章。要注意下的是在用户资料页面我们只让显示该用户的文章,所以文章的作者要是该用户。

初始化的视图模板非常的简单(项目目录/templates/user.html):
 

<!-- extend base layout -->
{% extends "base.html" %}
 
{% block content %}
<h1>用户昵称: {{user.nickname}}!</h1>
<hr>
{% for post in posts %}
<p>
 {{post.author.nickname}} 发布了: <b>{{post.body}}</b>
</p>
{% endfor %}
{% endblock %}

用户资料页面就做好了,不过在站点中还没有指向改页面的链接地址。为了让用户很方便的来查看自己的资料信息我们就把链接地址放到最上面的导航上去(项目目录/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) }}">你的个人资料</a>
    | <a href="{{ url_for('logout') }}">退出登陆</a>
    {% endif %}
  </div>

注意一下我们已经给函数传参了之后的和之前的URL。

现在就来试一试这个项目。点击上面的“你的资料”链接就会跳转到用户资料页面。由于我们还没有指向一个随意用户资料页面的链接地址,所以在这里如果你想看他人的资料,就需要自己手动输入一下地址了。比如你想看miguel的资料,那么地址就是:http://localhost:5000/user/miguelt

头像部分

我相信你会觉得目前的用户资料页面看起来很单调。为了好看,我们就来添加用户头像的功能。

为了避免我们服务器需要来处理大量上传后的头像图片,我们在这里就使用Gravatar给咋们提供的用户头像即可。

鉴于返回一个用户头像是属于用户这块的,所以我们就把代码放在theUserclass里面(项目目录/models.py):

 

from hashlib import md5
# ...
class User(db.Model):
  # ...
  def avatar(self, size):
    return 'http://www.gravatar.com/avatar/' + md5(self.email).hexdigest() +
    '?d=mm&s=' + str(size)

avatar将会返回用户头像图片的地址, 根据你的需要来请求你想要的图片尺寸像素。

从Gravatar上得到图像图片很简单。你只需要用md5把用户的邮箱hash加密之后合并成上面的那种url形式即可。当然你也可以自由选择自 定义图像大小。其中“d=mm”是设置用户在没有Gravatar账号的情况下显示的默认头像。“mm”选项会返回一张只有人轮廓的灰色图片,称之为“谜 样人”。而“s=”选项是用来设置返回你给定的图片尺寸像素。

当然Gravatar也有自己的文档来描述URL的拼接技术!

到这里Userclass就可以返回一个用户头像的图片了,我们就需要把这个整合到用户资料布局去(项目目录/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.nickname}}</h1></td>
  </tr>
</table>
<hr>
{% for post in posts %}
<p>
 {{post.author.nickname}} 发布了: <b>{{post.body}}</b>
</p>
{% endfor %}
{% endblock %}

我们设计Userclass来返回用户头像的亮点在于:如果某一天我们要是觉得Gravatar网站上的头像不是我们所想要的头像的时候,我们只需要我们只需要重写一下头像处理的函数来返回我们想要的地址即可(即使有人盗链指向我们的服务器,我们也可以保护好自己的主机),这样一来只需要修改这么点点,所以的模板都还是自动正常运行。

我们已经把用户头像部分添加到了用户资料详情页面的顶部去了,不过在页面底部我们还有显示文章的没做,在文章前面我们也需要显示一下用户的头像。当 然在用户资料页面需要对所以的文章都显示同样的头像,不过要是把头像函数移动到主页去来给所以的文章都显示作者的头像,那该多好。

我们只需要稍稍修改模板文件即可实现给文章显示相应作者头像的功能(项目目录/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.nickname}}</h1></td>
  </tr>
</table>
<hr>
{% for post in posts %}
<table>
  <tr valign="top">
    <td><img src="{{post.author.avatar(50)}}"></td><td><i>{{post.author.nickname}}
   发布了:</i><br>{{post.body}}</td>
  </tr>
</table>
{% endfor %}
{% endblock %}

这就是到此为止我们的用户资料页面:

微博客用户详情页面

Python的Flask框架中实现登录用户的个人资料和头像的教程

重复使用子模板

用户资料页面显示了用户自己的文章,不过网站的首页需要显示此刻不同用户文章。在这里就有两个用于显示 用户文章的模板文件了。我们可以直接复制把处理显示文章的那段代码然后直接粘贴到新的模板,其实那并不是最理想的方法,倘若有一天我们需要修改下显示文章 那块,我们就需要来更新所以的那些含有文章显示代码的模板文件。

反之,我们将会去新建一个子模板文件来处理文章显示的功能,之后在需要显示文章的时候包含一下这个文件即可。

开始我们还是来创建一个子空模板文件,然后把用户资料页面中展示文章的那段代码复制过来(项目目录/templates/post.html):
 

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

然后我们使用Jinja2的包含功能调用一下该子模板文件(项目目录/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.nickname}}</h1></td>
  </tr>
</table>
<hr>
{% for post in posts %}
  {% include 'post.html' %}
{% endfor %}
{% endblock %}

一旦有了完整的页面我们就可以按照上面的方法去调用下子模板来显示文章,不过现在还不急说,将在后面章节的教程中说到。

更多相关个人信息

尽管到此用户信息页面比较精密了,不过还是有许多信息没有显示出来。用户大多喜欢在网站上显示自己更多的信息,因此我们就可以让用户填写自己的信息显示在这里。当然我们也可以记录下用户每次登陆到本站的的时间,显示到他们自己的资料详情页。

为了显示等多的信息我们就需要更新下数据库。特别需要在Userclass里面新加字段(项目目录/models.py):

class User(db.Model):
  id = db.Column(db.Integer, primary_key = True)
  nickname = db.Column(db.String(64), unique = True)
  email = db.Column(db.String(120), 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)

我们每次修改数据库的时候就需要生成新的记录(migration)。需要注意下我们在处理数据更新到数据库的时候记得创建一个(migration),现在我们就来看下最后结果。我们只需要下面的代码就可以把新加的两个字段更新到数据库里去了:
 

./db_migrate.py

当然相应的响应脚本是
 

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

现在我们新加的两个字段就保存到数据库了。不过需要提醒的是在window系统下面调用此脚本是有点不一样的。

如果不支持数据迁移的话你还得手动修改数据库,否则你就需要删除之后重新从头开始创建数据。

接下来我们就需要修改下用户详情页来显示我们刚添加的字段(项目目录/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.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)就特别的好处理。需要注意的是在上面我们已经设置了用于注册用户请求的(flask.g和global, asg.user)接收参数。也就是需要在这个最佳段来记录用户的登陆时间(项目目录/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()

如果你登陆了之后就会在资料详情页面显示最后的登陆时间,当然如果你每刷新一次页面的话相应的登陆时间就会自动更新。这是由于当浏览器没刷新一次就会去请求我们上面设置的接收参数并更新数据库的处理函数。

在这里我们记录的是国际标准时间UTC时区。在之前的章节中我们也提到了怎么去储存一个适合所有时区的时间戳,这就会在有一个负面的错误信息,因为在所有用户的资料页面显示的都是UTC的时区的时间,这个问题我会在接下来说关于出来时间和日期的章节中详细讲解。

想显示关于用户的更多信息,我们得给他们一个链接, 最适合放在用户资料的编辑页面。
 
编辑用户详细资料

创建一个用户资料编辑页面那真是太简单了,我们只需要创建下面的web表单即可(项目目录/forms.py):
 

from flask.ext.wtf import Form, TextField, BooleanField, TextAreaField
from flask.ext.wtf import Required, Length
 
class EditForm(Form):
  nickname = TextField('nickname', validators = [Required()])
  about_me = TextAreaField('about_me', validators = [Length(min = 0, max = 140)])

视图模板文件(项目目录/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>你的昵称:</td>
      <td>{{form.nickname(size = 24)}}</td>
    </tr>
    <tr>
      <td>关于自己:</td>
      <td>{{form.about_me(cols = 32, rows = 4)}}</td>
    </tr>
    <tr>
      <td></td>
      <td><input type="submit" value="保存"></td>
    </tr>
  </table>
</form>
{% endblock %}

最后当然就是创建视图的方法(项目目录/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)

方便用户编辑,我们需要在用户的个人资料页面添加一个到此页面的链接地址(项目目录/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.nickname}}</h1>
      {% if user.about_me %}<p>{{user.about_me}}</p>{% endif %}
      {% if user.last_seen %}<p><i>最后登陆时间: {{user.last_seen}}</i></p>{% endif %}
      {% if user.id == g.user.id %}<p><a href="{{url_for('edit')}}">修改资料</a></p>{% endif %}
    </td>
  </tr>
</table>
<hr>
{% for post in posts %}
  {% include 'post.html' %}
{% endfor %}
{% endblock %}

不过你需要判断的一下,条件就是只有当用户浏览自己的个人资料页面的时候才显示该链接,而不是浏览任何人的个人资料页面都显示出来。

下面是最新个人资料页面的截图,包含了我们新加的所以字段,也含有“关于我”的文字:

Python的Flask框架中实现登录用户的个人资料和头像的教程

最后一点留给你自己去研究了

貌似通过上面的一些列制作,个人资料页面感觉已经很完善了,对不?仔细想来,是这样不过我们还有一些bug需要修复下。

不知道你有没有发现?

提醒下你吧,在之前的章节中我们浏览用户登陆的时候其实我就已经提到过这个bug的。现在我们在上面的代码片中也犯了同样错。

仔细想想吧,如果你知道是什么问题的话可以在下面评论中说下。我将会在下一个章节详细地说此bug,并说怎么去修正它。

跟以前一样我会把今天说讲到的代码打包提供下载

下载地址 microblog-0.6.zip.

Python 相关文章推荐
Python通过正则表达式选取callback的方法
Jul 18 Python
Django入门使用示例
Dec 12 Python
python实现自动登录
Sep 17 Python
python3实现网络爬虫之BeautifulSoup使用详解
Dec 19 Python
python可视化实现代码
Jan 15 Python
使用python制作一个解压缩软件
Nov 13 Python
在python中创建指定大小的多维数组方式
Nov 28 Python
Keras-多输入多输出实例(多任务)
Jun 22 Python
python+pygame实现坦克大战小游戏的示例代码(可以自定义子弹速度)
Aug 11 Python
python实现经纬度采样的示例代码
Dec 10 Python
jupyter notebook指定启动目录的方法
Mar 02 Python
Python通过m3u8文件下载合并ts视频的操作
Apr 16 Python
Python的Flask框架中实现简单的登录功能的教程
Apr 20 #Python
Python的Flask框架与数据库连接的教程
Apr 20 #Python
Python的Flask框架中web表单的教程
Apr 20 #Python
在Python的Flask框架中使用模版的入门教程
Apr 20 #Python
使用Node.js和Socket.IO扩展Django的实时处理功能
Apr 20 #Python
利用Python的Django框架中的ORM建立查询API
Apr 20 #Python
对于Python的框架中一些会话程序的管理
Apr 20 #Python
You might like
解析在PHP中使用mysqli扩展库对mysql的操作
2013/07/03 PHP
php实现可以设置中奖概率的抽奖程序代码分享
2014/01/19 PHP
php结合正则获取字符串中数字
2015/06/19 PHP
php pdo oracle中文乱码的快速解决方法
2016/05/16 PHP
使用laravel和ajax实现整个页面无刷新的操作方法
2019/10/03 PHP
用jQuery技术实现Tab页界面之二
2009/09/21 Javascript
无缝滚动js代码通俗易懂(自写)
2013/06/19 Javascript
jquery+ajax验证不通过也提交表单问题处理
2014/12/12 Javascript
window.open()实现post传递参数
2015/03/12 Javascript
微信小程序 教程之注册程序
2016/10/17 Javascript
ThinkPHP+jquery实现“加载更多”功能代码
2017/03/11 Javascript
详解vue2.0 transition 多个元素嵌套使用过渡
2017/06/19 Javascript
原生js二级联动效果
2017/06/20 Javascript
vue组件生命周期详解
2017/11/07 Javascript
JS实现的数组去除重复数据算法小结
2017/11/17 Javascript
微信小程序实现登录遮罩效果
2018/11/01 Javascript
前端Electron新手入门教程详解
2019/06/21 Javascript
TypeScript中使用getElementXXX()的示例代码
2019/09/12 Javascript
Vue3 中的数据侦测的实现
2019/10/09 Javascript
使用vant的地域控件追加全部选项
2020/11/03 Javascript
pyQt4实现俄罗斯方块游戏
2018/06/26 Python
利用Python如何批量修改数据库执行Sql文件
2018/07/29 Python
pycharm 批量修改变量名称的方法
2019/08/01 Python
PyTorch 普通卷积和空洞卷积实例
2020/01/07 Python
解决python 执行sql语句时所传参数含有单引号的问题
2020/06/06 Python
HTML5 video循环播放多个视频的方法步骤
2020/08/06 HTML / CSS
ProBikeKit德国:在线公路自行车专家
2018/06/03 全球购物
Jack Rogers官网:美国经典的女性鞋靴品牌
2019/09/04 全球购物
为什么要优先使用同步代码块而不是同步方法?
2013/01/30 面试题
EJB3推出JPA的原因
2013/10/16 面试题
大学生职业生涯规划范文
2014/01/08 职场文书
违反工作规定检讨书范文
2014/12/14 职场文书
档案工作个人总结
2015/03/03 职场文书
综合素质评价个性与发展自我评价
2015/03/06 职场文书
幽默口才训练经典句子(48句)
2019/08/19 职场文书
使用qt quick-ListView仿微信好友列表和聊天列表的示例代码
2021/06/13 Python