1.为了让用户的资料页面更吸引人,我们在User模型中添加一些关于用户的其它信息(真实姓名/所在地/自我介绍/注册日期/最后访问日期)
FlaskWeb/app/models.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
from datetime import datetime
from . import db, loginmanager
from flask import current_app
from flask_login import UserMixin, AnonymousUserMixin
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from werkzeug.security import generate_password_hash, check_password_hash
@loginmanager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class Permission(object):
FOLLOW = 0x01
COMMENT = 0x02
WRITE_ARTICLES = 0x04
MANAGE_COMMENTS = 0x08
ADMINISTER_POWER = 0x80
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True, nullable=False, index=True)
permission = db.Column(db.Integer)
default = db.Column(db.Boolean, default=False, index=True)
users = db.relationship('User', backref='role', lazy='dynamic')
@staticmethod
def insert_roles():
roles = {
'user': (
Permission.FOLLOW |
Permission.COMMENT |
Permission.WRITE_ARTICLES, True),
'manager': (
Permission.FOLLOW |
Permission.COMMENT |
Permission.WRITE_ARTICLES |
Permission.MANAGE_COMMENTS, False),
'administrator': (
Permission.ADMINISTER_POWER, False)
}
for cur_role in roles:
role = Role.query.filter_by(name=cur_role).first()
if not role:
role = Role(name=cur_role)
role.permission = roles[cur_role][0]
role.default = roles[cur_role][-1]
db.session.add(role)
db.session.commit()
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, nullable=False, index=True)
username = db.Column(db.String(64), unique=True, nullable=False, index=True)
password_hash = db.Column(db.String(128), nullable=False)
is_confirmed = db.Column(db.Boolean, default=False)
realname = db.Column(db.String(64))
location = db.Column(db.String(128))
about_me = db.Column(db.Text())
register_date = db.Column(db.DateTime(), default=datetime.utcnow())
last_access_date = db.Column(db.DateTime(), default=datetime.utcnow())
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def refresh_access_date(self):
self.last_access_date = datetime.utcnow()
def allow(self, permission):
return self.role is not None and (self.role.permission & permission) == permission
def is_administrator(self):
return self.allow(Permission.ADMINISTER_POWER)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
if not self.role:
if self.email == current_app.config['FLASK_ADMIN']:
self.role = Role.query.filter_by(permission=128).first()
if not self.role:
self.role = Role.query.filter_by(default=True).first()
@property
def password(self):
raise AttributeError(u'password 不允许读取.')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
def generate_confirm_token(self, expires_in=3600):
s = Serializer(current_app.config['SECRET_KEY'], expires_in=expires_in)
data = s.dumps({'confirm_id': self.id})
return data
def verify_confirm_token(self, token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except BaseException, e:
return False
if data.get('confirm_id') != self.id:
return False
self.is_confirmed = True
db.session.add(self)
db.session.commit()
return True
class AnonymousUser(AnonymousUserMixin):
def allow(self, permission):
return False
def is_administrator(self):
return False
loginmanager.anonymous_user = AnonymousUser
说明:添加的about_me字段类型为db.Text(),register_date字段类型为db.DateTime(),db.String()和db.Text的区别在于后者不需要指定最大长度,两个时间戳的默认值都是当前时间,但是last_access_date需要每次访问时自动刷新,所以我们提供一个refresh_access_date的方法来刷新访问时间
FlaskWeb/app/auth/views.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
from .. import db
from . import auth
from ..models import User
from ..email import send_mail
from .forms import LoginForm, RegisterForm
from flask import render_template, flash, url_for, redirect, request
from flask_login import login_required, fresh_login_required, login_user, logout_user, current_user
@auth.before_app_request
def before_app_request():
if current_user.is_authenticated:
current_user.refresh_access_date()
if not current_user.is_confirmed \
and request.endpoint \
and request.endpoint[:5] != 'auth.'\
and request.endpoint != 'static':
return redirect(url_for('auth.unconfirmed'))
@auth.route('/unconfirmed')
def unconfirmed():
if current_user.is_anonymous or current_user.is_confirmed:
return redirect(url_for('main.index'))
return render_template('auth/unconfirmed.html')
@auth.route('/')
@fresh_login_required
def index():
pass
@auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.verify_password(form.password.data):
flash(u'已成功登录', 'success')
login_user(user, form.remeber_me.data)
return redirect(url_for('main.index'))
flash(u'用户名或密码错误', 'danger')
return redirect(url_for('auth.login'))
return render_template('auth/login.html', form=form)
@auth.route('/logout', methods=['GET', 'POST'])
@fresh_login_required
def logout():
logout_user()
flash(u'已退出登录', 'success')
return redirect(url_for('main.index'))
@auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
user = User(email=form.email.data,
username=form.username.data,
password=form.password.data)
db.session.add(user)
db.session.commit()
token = user.generate_confirm_token(expires_in=3600)
flash(u'确认已发送至你的邮箱,点击激活邮件', 'success')
send_mail(form.email.data,
u'Flasky - 注册确认邮件',
'auth/email/confirm',
user=user, token=token)
return redirect(url_for('main.index'))
return render_template('auth/register.html', form=form)
@auth.route('/confirm')
@login_required
def resend_confirmation():
token = current_user.generate_confirm_token()
send_mail(current_user.email,
u'Flasky - 注册确认邮件',
'auth/email/confirm',
user=current_user, token=token)
flash(u'一封新的确认邮件已经发送至你的邮箱')
return redirect(url_for('main.index'))
@auth.route('/confirm/<token>')
@login_required
def confirm(token):
if current_user.verify_confirm_token(token):
flash(u'邮箱验证通过', 'success')
return redirect(url_for('main.index'))
flash(u'确认连接已失效', 'danger')
return redirect(url_for('main.index'))
loginmanager.anonymous_user = AnonymousUser
说明:每次收到请求时调用current_user对象的refresh_access_date方法,由于auth中注册的是全局的before_app_request处理程序,在每次请求前运行,所以很容易实现更新访问时间的功能