Flask从入门到做出一个博客的大型教程(五)
在开始之前,先来看下项目的整体结构。
flask/
├── app
│ ├── forms.py
│ ├── __init__.py
│ ├── models.py
│ ├── routes.py
│ └── templates
│ ├── base.html
│ ├── index.html
│ ├── login.html
│ ├── register.html
│ └── test.html
├── config.py
├── migrations
│ ├── alembic.ini
│ ├── env.py
│ ├── README
│ ├── script.py.mako
│ └── versions
│ ├── 063e1979f08c_questions.py
├── question.py
6 用户个人资料页
登录模块完成了,接下来做一做用户个人资料页的显示。
app/routes.py : 添加用户方法
@app.route('/user/<username>')
@login_required
def user(username):
user = User.query.filter_by(username=username).first_or_404()
posts = [
{'author':user,'body':'测试Post #1号'},
{'author':user,'body':'测试Post #2号'}
]
return render_template('user.html',user=user,posts=posts)
既然添加了方法,肯定要添加对应的模板了。
app/templates/user.html : 用户个人中心模板
{% extends "base.html" %}
{% block content %}
<h1>用户: {{ user.username }}</h1>
<hr>
{% for post in posts %}
<p>
{{ post.author.username }} 说: <b>{{ post.body }}</b>
</p>
{% endfor %}
{% endblock %}
对基类模板进行小小的改动,当用户是未登录状态看不到用户中心的超链接,登录后看到。这会很有感意思。
app/templates/base.html : 完善基类模板
<div>博客 :
<a href="{{ url_for('index') }}">首页</a>
{% if current_user.is_anonymous %}
<a href="{{ url_for('login') }}">登录</a>
{% else %}
<a href="{{ url_for('user', username=current_user.username) }}">用户中心</a>
<a href="{{ url_for('logout') }}">退出</a>
{% endif %}
</div>
好了,现在登录看看效果吧!
用户头像,我使用的是Gravatar来提供,使用这个网站生成头像是https:// s.gravatar.com/avatar/(hash) 这种格式,会把邮箱地址用md5加密拼接在后面。如果感兴趣可以去上传一个玩一玩,如果不想去的话可以直接使用我提供的。https://www.gravatar.com/avatar/6b541a0a667f5558208aad7309c22936,默认像素是80x80,但是你可以在这个地址后面添加参数?s=128,会变成128x128像素,当然你可以尝试改成更大的数字也可以,很是方便。可以根据你的需求改变大小。接下来就来驶入它吧!
app/models.py : 添加avatar地址
from hashlib import md5
# ...
class User(UserMixin, db.Model):
# ...
def avatar(self, size):
digest = md5(self.email.lower().encode('utf-8')).hexdigest()
return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(
digest, size)
方法写好了,接下来就修改模板,在网页中显示吧!
app/templates/user.html : 显示用户头像
{% extends "base.html" %}
{% block content %}
<table>
<tr valign="top">
#可以更改用户头像的大小
<td><img src="{{ user.avatar(128) }}"></td>
<td><h1>用户: {{ user.username }}</h1></td>
</tr>
</table>
<hr>
{% for post in posts %}
<table>
<tr valign="top">
<td><img src="{{ post.author.avatar(36) }}"></td>
<td>{{ post.author.username }} 说:<br>{{ post.body }}</td>
</tr>
</table>
{% endfor %}
{% endblock %}
现在用户个人资料页面设计好了,这样用户写的帖子以及他们的头像都可以显示出来。 现在我想让首页也有类似的布局,但是复制/粘贴处理这种方法显然不太合适,因此新建模板用来被调用。
app/templates/_post.html : 用来放posts里面的内容
<table>
<tr valign="top">
<td><img src="{{ post.author.avatar(36) }}"></td>
<td>{{ post.author.username }} 说:<br>{{ post.body }}</td>
</tr>
</table>
app/templates/user.html : 在用户模板中被调用。
{% extends "base.html" %}
{% block content %}
<table>
<tr valign="top">
<td><img src="{{ user.avatar(128) }}"></td>
<td><h1>User: {{ user.username }}</h1></td>
</tr>
</table>
<hr>
{% for post in posts %}
{% include '_post.html' %}
{% endfor %}
{% endblock %}
把格式完善的更丰满些。
app/models.py : 增加新的字段
class User(UserMixin, db.Model):
# ...
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
修改了模型,还记得大明湖畔的migrate吗?
(venv) duke@coding:~/flask_tutorial/flask$ flask db migrate -m 'User增加新的字段'
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added column 'users.about_me'
INFO [alembic.autogenerate.compare] Detected added column 'users.last_seen'
Generating /home/duke/prequestion/migrations/versions/cb586afdff6a_user增加新的字段.py ... done
继续下一步,写入数据库中!
(venv) duke@coding:~/flask_tutorial/flask$ flask db upgrade
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade 063e1979f08c -> cb586afdff6a, User增加新的字段
好的,现在user模型又新增了字段,那么模板也要相应的改变啦。
app/templates/user.html : 添加字段
{% extends "base.html" %}
{% block content %}
<table>
<tr valign="top">
<td><img src="{{ user.avatar(128) }}"></td>
<td>
<h1>User: {{ user.username }}</h1>
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
{% if user.last_seen %}<p>最近登录: {{ user.last_seen }}</p>{% endif %}
</td>
</tr>
</table>
...
{% endblock %}
模型、模板都改了,那中间最重要的视图部分当然也要修改啦。
app/routes.py : 添加对应的方法
#...
from datetime import datetime
@app.before_request
def before_request():
if current_user.is_authenticated:
current_user.last_seen = datetime.utcnow()
db.session.commit()
#...
刚才在User模型添加了2个字段,上面完成了一个最近登录,还有一个关于我about_me,首先要在forms.py进行设计。
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Length
# ...
class EditProfileForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired(message='请输入用户名!')])
about_me = TextAreaField('关于我', validators=[Length(min=0, max=140)])
submit = SubmitField('提交')
既然forms里进行修改,那么就要新增加一个专门的编辑个人资料的页面更合适些。
app/templates/edit_profile.html : 个人资料编辑
{% extends "base.html" %}
{% block content %}
<h1>个人资料编辑</h1>
<form action="" method="post">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.about_me.label }}<br>
{{ form.about_me(cols=50, rows=4) }}<br>
{% for error in form.about_me.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}
这些都完成差不多了,接下来就是增加视图函数了。
app/routes.py : 编辑个人资料的视图函数
from app.forms import EditProfileForm
# ....
@app.route('/edit_profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
form = EditProfileForm()
if form.validate_on_submit():
current_user.username = form.username.data
current_user.about_me = form.about_me.data
db.session.commit()
flash('你的提交已变更.')
return redirect(url_for('edit_profile'))
elif request.method == 'GET':
form.username.data = current_user.username
form.about_me.data = current_user.about_me
return render_template('edit_profile.html', title='个人资料编辑',
form=form)
在用户界面添加一个可以点击的超链接,就完成个人资料编辑的页面啦。
app/templates/user.html : 编辑页面的链接
<td>
<h1>用户名: {{ user.username }}</h1>
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
{% if user.last_seen %}<p>最近登录: {{ user.last_seen }}</p>{% endif %}
{% if user == current_user %}
<p><a href="{{ url_for('edit_profile') }}">个人资料编辑</a></p>
{% endif %}
</td>
当当当~,最后对整个的显示如下。