每个注册用户都需要有一个专门的个人资料页面,实现个性化信息管理。
用户资料这个功能模块主要分为两个部分,资料的展示和编辑。
1.资料的展示
目前User模型字段太少,新增添加location(地址),about_me(个人描述),member_since(注册时间),last_seen(最后访问时间)。修改app/model.py 里的User类
class User(UserMixin, db.Model):
#...
name = db.Column(db.String(64))
location = db.Column(db.String(64))
about_me = db.Column(db.Text())
member_since = db.Column(db.DateTime(), default = datetime.utcnow)
last_seen = db.Column(db.DateTime(), default = datetime.utcnow)
last_seen字段每次用户访问的时候会更新,因此,在User中新增更新方法
class User(UserMixin, db.Model):
#...
def ping(self):
self.last_seen = datetime.utcnow
db.session.add(self)
由于每次请求都需要更新last_seen字段,因此在蓝本auth中的before_app_request中调用该方法。修改app/auth/views.py
@auth.before_app_request
def before_request():
if current_user.is_authenticated():
current_user.ping()
if not current_user.confirmed \
and request.endpoint[:5] != 'auth'
return redirect(url_for('auth.unconfirmed'))
完成了用户访问时间的更新之后,下面实现用户的资料展示页面。用户的资料展示页面路由形式/user/username
@main.route('/user/<username>')
def user(username):
user = User.query.filter_by(username=username).first()
if user is None:
abort(404)
return render_template('user.html'.user=user)
{% block page_content %}
<div class="page-header">
<h1>{{ user.username }}</h1>
{% if user.username or user.location %}
<p>
{% if user.name %}{{ user.name }}{% endif%}
{% if user.location %}
From {{ user.location }}
{% endif %}
</p>
{% endif %}
{% if current_user.is_administrator() %}
<p><a href="mailto:{{ user.email }}">{{ user.email }}</a></p>
{% endif %}
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
<p>
member since {{ moment(user.member_since).format('L') }}
last seen {{ moment(user.last_seen).format('L') }}
</p>
</div>
{% endblock %}
在导航栏新增个人资料栏,修改base.html
{% if current_user.is_authenticated() %}
<li>
<a href="{{ url_for('main.user', username=current_user.username) }}">
Profile
</a>
</li>
{% endif %}
2.普通用户资料编辑
之所以分为普通用户资料编辑和管理员用户资料编辑这两个页面,是因为普通用户只能编辑自己资料,而管理员则可以编辑所有人资料,并且管理员还可以设置用户的角色权限更信息。
app/main/forms.py:资料编辑表单
class EditProfileForm(Form):
name = StringField('Real name', validators=[Length(0, 64)])
location = StringField('Location', validators=[Length(0, 64)])
about_me = TextAreaField('About me')
submit = SubmitField('Submit')
修改app/main/views.py,新增普通用户资料编辑路由
@main.route('/edit-profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
form = EditProfileForm()
if form.validate_on_submit():
current_user.name = form.name.data
current_user.location = form.location.data
current_user.7= form.about_me.data
db.session.add(current_user)
flash('Your profile has been updated.')
return redirect(url_for('.user', username=current_user.username))
form.name.data = current_user.name
form.location.data = current_user.location
form.about_me.data = current_user.about_me
return render_template('edit_profile.html', form=form)
同时在用户资料页面新增编辑资料按钮 app/templates/user.html
{% if user == current_user %}
<a class="btn btn-default" href="{{ url_for('.edit_profile') }}">
Edit Profile
</a>
{% endif %}
3.管理员编辑资料页面
管理员在表单能编辑用户的电子邮件、用户名、确认状态和角色。新增管理员使用的资料编辑表单,修改app/main/forms.py
class EditProfileAdminForm(Form):
email = StringField('Email', validators=[Required(), Length(1, 64),
Email()])
username = StringField('Username', validators=[
Required(), Length(1, 64), Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,
'Usernames must have only letters, '
'numbers, dots or underscores')])
confirmed = BooleanField('Confirmed') #单选框
role = SelectField('Role', coerce=int) #下拉选项
name = StringField('Real name', validators=[Length(0, 64)])
location = StringField('Location', validators=[Length(0, 64)])
about_me = TextAreaField('About me')
submit = SubmitField('Submit')
def __init__(self, user, *args, **kwargs):
super(EditProfileAdminForm, self).__init__(*args, **kwargs)
self.role.choices = [(role.id, role.name)
for role in Role.query.order_by(Role.name).all()] #设置choices值即设置下拉选项列表
self.user = user
def validate_email(self, field):
if field.data != self.user.email and \
User.query.filter_by(email=field.data).first():#表单数据变化了且数据在数据库不存在
raise ValidationError('Email already registered.')
def validate_username(self, field):
if field.data != self.user.username and \
User.query.filter_by(username=field.data).first():
raise ValidationError('Username already in use.')
管理员的资料编辑路由 app/main/views.py
@main.route('/edit-profile/<int:id>', methods=['GET', 'POST'])
@login_required
@admin_required
def edit_profile_admin(id):
user = User.query.get_or_404(id)
form = EditProfileAdminForm(user=user)
if form.validate_on_submit():
user.email = form.email.data
user.username = form.username.data
user.confirmed = form.confirmed.data
user.role = Role.query.get(form.role.data)
user.name = form.name.data
user.location = form.location.data
user.about_me = form.about_me.data
db.session.add(user)
flash('The profile has been updated.')
return redirect(url_for('.user', username=user.username))
form.email.data = user.email
form.username.data = user.username
form.confirmed.data = user.confirmed
form.role.data = user.role_id
form.name.data = user.name
form.location.data = user.location
form.about_me.data = user.about_me
return render_template('edit_profile.html', form=form, user=user)
4.用户头像
Gravatar 是一个行业领先的头像服务,能把头像和电子邮件地址关联起来。用户先要到http://gravatar.com 中注册账户,然后上传图片。生成头像的URL 时,要计算电子邮件地址的MD5 散列值。
(venv) $ python
>>> import hashlib
>>> hashlib.md5('john@example.com'.encode('utf-8')).hexdigest()
'd4c74594d841139328695756648b6bd6'