Flask 学习 九 用户资料
资料信息
app/models.py
class User(UserMixin,db.Model): #...... name = db.Column(db.String(64)) location = db.Column(db.String(64)) about_me=db.Column(db.Text())# 和String的区别是不需要指定最大长度 member_since=db.Column(db.DateTime(),default=datetime.utcnow)# default 可以接受函数为默认值,在需要的时候回自定调用指定的函数,所以不需要加() last_seen=db.Column(db.DateTime(),default=datetime.utcnow)# 初始值是当前时间 #..... def ping(self): self.last_seen=datetime.utcnow() # 获取当前时间 db.session.add(self)# 提交时间到数据库
app/auth/views.py
利用auth蓝本中的before_app_first_request方法,来完成每次请求前都要运行ping方法,来实现更新已登录用户的访问时间
@auth.before_app_first_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'))
用户资料页面
为每个用户都创建资料页面
app/mian/views.py 资料页面路由
from flask import abort @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)
user.html
{% extends 'base.html' %} {% block title %}flasky -page-not-find{% endblock %} {% block page_content %} <div class="page-header"> <img class="img-rounded profile-thumbnail" src="{{ user.gravatar(size=256) }}"> <div class="profile-header"> <h1>{{ user.username }}</h1> {% if user.name or user.location %} <p> {% if user.name %}{{ user.name }}{% endif %} {% if user.location %} From <a href="http://map.baidu.com/?{{ user.location }}">{{ user.location }}</a> {% 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> 注册时间:{{ moment(user.member_since).format('L') }}. 最后登陆时间:{{ moment(user.last_seen).fromNow() }} </p> </div> </div> {% endblock %}
base.html 添加一条导航,更方便的访问自己的个人资料,未认证的用户看不到导航条
<ul class="nav navbar-nav"> <li><a href="/">主页</a></li> {% if current_user.is_authenticated %} <li> <a href="{{ url_for('main.user',username=current_user.username) }}">个人资料</a> </li> {% endif %} </ul>
资料编辑器-用户级别的资料编辑器
app/main/forms.py 资料编辑表单
class EditProflieForm(FlaskForm):
name = StringField('真实姓名', validators=[Length(0,64)])
location = StringField('位置', validators=[Length(0,64)])
about_me = TextAreaField('关于我')
submit = SubmitField('提交')
app/main/views.py 资料编辑路由
@main.route('/edit-profile',methods=['get','post']) @login_required def edit_profile(): form = EditProflieForm() if form.validate_on_submit(): current_user.name = form.name.data current_user.location=form.location.data current_user.about_me=form.about_me.data db.session.add(current_user) flash('你的资料已经更新') 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)
user.html 资料编辑链接
<p> {% if user ==current_user.user %} <a class="btn btn-default" href="{{ url_for('.edit_profile') }}">编辑资料</a> {% endif %} </p>
管理员级别的用户编辑器
main/forms.py
class EditProflieAdminForm(FlaskForm): email=StringField('邮箱', validators=[DataRequired(), Length(1, 64),Email()]) username = StringField('用户名', validators=[DataRequired(), Length(1, 64), Regexp('^[A-Za-z][A-Za-z0-9_.]*$',0,'用户名必须是字母,数字,点号,下划线')]) confirmed = BooleanField('确认') role = SelectField('角色',coerce=int)# 因为角色id是个整数,所以把字段值转化为整数而不是字符串 name = StringField('真实姓名', validators=[Length(0, 64)]) location = StringField('位置', validators=[Length(0, 64)]) about_me = TextAreaField('关于我') submit = SubmitField('提交') def __init__(self,user,*args,**kwargs): super(EditProflieAdminForm,self).__init__(*args,**kwargs) #选项由元祖组成,选项的标识符和显示空间中的文本字符串 self.role.choices=[(role.id,role.name) for role in Role.query.order_by(Role.name).all()] self.user=user def validate_email(self, filed): #首先检查字段是否发生了变化,并保证新值不和其他用户的字段值重复 if filed.data != self.user.email and User.query.filter_by(email=filed.data).first(): raise ValidationError('邮箱已被注册') def validate_username(self, filed): # 首先检查字段是否发生了变化,并保证新值不和其他用户的字段值重复 if filed.data != self.username and User.query.filter_by(username=filed.data).first(): raise ValidationError('用户名已被使用')
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 =EditProflieAdminForm(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('资料已经更新') return redirect(url_for('.user',username = user.username)) form.email.data=user.email form.username.data = user.username form.confirmed.data=user.confirmed # choice属性设置的元祖列表使用数字标识符表示各选项 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)
user.html 管理员使用的资料编辑链接
<p> {% if current_user.is_administrator() %} <a class="btn btn-danger" href="{{ url_for('.edit_profile_admin',id=user.id) }}">编辑资料[管理员]</a> {% endif %} </p>
edit_profile.html
{% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Flasky - 编辑资料{% endblock %} {% block page_content %} <div class="page-header"> <h1>编辑你的资料</h1> </div> <div class="col-md-4"> {{ wtf.quick_form(form) }} </div> {% endblock %}
用户头像 app/models.py
import hashlib from flask import request def gravatar(self,size=100,default='identicon',rating='g'): if request.is_secure: url = 'https://secure.gravatar.com/avatar' else: url='http://www.gravatar.com/avatar' hash = self.avatar_hash or hashlib.md5(self.email.encode('utf-8')).hexdigest() return '{url}/{hash}?s={size}&d={default}&r={rating}'.format(url=url,hash=hash,size=size,default=default,rating=rating)
添加资料页面头像 user.html
<img class="img-rounded profile-thumbnail" src="{{ user.gravatar(size=256) }}">
将邮件生成的md5值保存到数据库,如果更新继续创建
class User(UserMixin,db.Model): def __init__(self,**kwargs): if self.email is not None and self.avatar_hash is None: self.avatar_hash=hashlib.md5(self.email.encode('utf-8')).hexdigest() avatar_hash = db.Column(db.String(32))# 头像哈希值存储到数据库 def change_email(self, token): .... self.email = new_email self.avatar_hash=hashlib.md5(self.email.encode('utf-8')).hexdigest() db.session.add(self) return True def gravatar(self,size=100,default='identicon',rating='g'): if request.is_secure: url = 'https://secure.gravatar.com/avatar' else: url='http://www.gravatar.com/avatar' hash = self.avatar_hash or hashlib.md5(self.email.encode('utf-8')).hexdigest() return '{url}/{hash}?s={size}&d={default}&r={rating}'.format(url=url,hash=hash,size=size,default=default,rating=rating) self.last_seen=datetime.utcnow() db.session.add(self)