进入第九章,内容就3小节,不过需要补的东西还挺多的
class Role(db.Model):
__tablename__='roles'
id=db.Column(db.Integer,primary_key=True)
name=db.Column(db.String(64),unique=True)
default = db.Column(db.Boolean,default=False,index=True) #用户默认角色,default是False
permissions = db.Column(db.Integer) #用户权限设置,是一个数值
users = db.relationship('User', backref='role',lazy='dynamic') #和User类来进行连接
只有一个角色的default 字段要设为True,其他都设为False。用户注册时,其角色会被设为默认角色。
这个模型的第二处改动是添加了permissions 字段,其值是一个整数,表示位标志。各操作都对应一个位位置,能执行某项操作的角色,其位会被设为1。
这里可以看到,用来设置权限登记的,是通过位运算的,最终通过16进制表示
class Permission:
FOLLOW = 0X01
COMMENT = 0X02
WRITE_ARTICLES = 0X04
MODERATE_COMMENTS = 0X08
ADMINISTER = 0X80
这里建一个权限类,来进行功能的标识,比如,关注功能,是0X01,评论功能,是0X02等等
而下面这个表,是针对各种用户角色,拥有多少权限,千万不要搞混淆!!!
比如新建用户,一般就只有3种功能,就是关注,评论,写文章,那么就是0X01,0X02,0X04三者进行位运算的或运算!!(可以理解为功能累加)
接下来重头戏来了,书上用了一个静态方法的例子
class Role(db.Model):
__tablename__='roles'
id=db.Column(db.Integer,primary_key=True)
name=db.Column(db.String(64),unique=True)
default = db.Column(db.Boolean,default=False,index=True)
permissions = db.Column(db.Integer)
users = db.relationship('User', backref='role',lazy='dynamic')
@staticmethod
def insert_roles():
roles = {
'User':(Permission.FOLLOW|Permission.COMMENT|Permission.WRITE_ARTICLES,True),
'Moderator':(Permission.FOLLOW|Permission.COMMENT|Permission.WRITE_ARTICLES|Permission.MODERATE_COMMENTS,False)
'ADMINISTER':(0xff,False)
}
for r in roles: #历遍roles字典
role = Role.query.filter_by(name = r).first() #查询Role类里是否存在这种name的角色
if role is None: #如果Role类里面没有找到
role = Role(name = r) #则新建角色,以r的值为名字(其实是用户组的名字)
role.permissions = roles[r][0] #为该role的权限组分配值,从字典取值
role.default = roles[r][1] #为该role的默认权限组分配布尔值,默认是False
db.session.add(role) #增加角色
db.session.commit() #提交申请(切记缩进,表示历遍完成以后一起提交申请)
insert_roles() 函数并不直接创建新角色对象,而是通过角色名查找现有的角色,然后再进行更新。只有当数据库中没有某个角色名时才会创建新角色对象。如此一来,如果以后更新了角色列表,就可以执行更新操作了。要想添加新角色,或者修改角色的权限,修改roles 数组,再运行函数即可。注意,“匿名”角色不需要在数据库中表示出来,这个角色的作用就是为了表示不在数据库中的用户。
以上这段话什么意思呢?
静态方法,允许程序在不创建实例的情况下就可以使用,你可以看到,这个函数里面,是没有self参数的
所以,他先历遍roles字典,看看里面有几个对象,目前来说有3个 User , Moderator , ADMINISTER
然后以name为过滤条件,看看有没有name分别为User, Moderator ,ADMINISTER的对象
如果没有,创建一个role,name属性为User (后面2次历遍出来的结果依次再赋值属性为Moderator, ADMINISTER)
随后,在创建完role对象以后,再为role的permissions 属性赋值,值为User对应的Permission的值,也就是roles里找到[r]这个key,[0]再找到对应的值
接着为default属性赋值,值是roles[r]的第二个参数[1],只有在默认用户的时候,才是True
若想把角色写入数据库,可使用shell 会话:
(venv) $ python manage.py shell
>>> Role.insert_roles()
>>> Role.query.all()
[<Role u'Administrator'>, <Role u'User'>, <Role u'Moderator'>]
这样就等于为Role类添加了3个对象,而对象的name分别如上
我另外还做了如下测试,我按照name='Test' 来进行过滤,但是这个对象是找不到其他属性的,比如id,是没的,可以见下图.
角色创建完了以后,在我们登录的时候,还需要对角色进行验证,所以,还需要有函数来进行验证。
from flask.ext.login import UserMixin, AnonymousUserMixin
#...
def can(self,permissions):
return self.role is not None and (self.role.permissions & permissions) == permissions
#检测对象的role属性不是None的同时,对象的权限数值和要求检验的数值符合
def is_administrator(self):
return self.can(Permission.ADMINISTER)
#直接赋值管理员的权限数值,看是否符合要求
class AnonymousUser(AnonymousUserMixin):
def can(self,permissions):
return False
def is_administrator(self):
return False
login_manager.anonymous_user = AnonymousUser #将login_manager.anonymous_user设为AnonymousUser类对象,实际上就是未登录状态的current_user
如果你想让视图函数只对具有特定权限的用户开放,可以使用自定义的修饰器。
from functools import wraps
from flask import abort
from flask.ext.login import current_user
def permission_required(permission):
def decorator(f):
@wraps(f) #这个装饰器保证了返回函数的__name__属性不变
def decorated_function(*args,**kwargs):
if not current_user.can*(permission): #如果当前用户的权限检查没有通过,则生成403错误
abort(403)
return f(*args,**kwargs)
return decorated_function
return decorator
def admin_required(f): #参数直接输入管理员的权限数值,用来校验
return permission_required(Permission.ADMINISTER)(f)
以下的例子就是将上面的装饰器,用在了路由功能里面,针对一些页面设置了权限
from decorators import admin_required, permission_required
from .models import Permission
@main.route('/admin')
@login_required
@admin_required
def for_admins_only():
return "For administrators!"
@main.route('/moderator')
@login_required
@permission_required(Permission.MODERATE_COMMENTS)
def for_moderators_only():
return "For comment moderators!"
在模板中可能也需要检查权限,所以Permission 类为所有位定义了常量以便于获取。为了避免每次调用render_template() 时都多添加一个模板参数,可以使用上下文处理器。上下文处理器能让变量在所有模板中全局可访问。
这个部分不是很理解,放在后面再看吧
@main.app_context_processor
def inject_permissions():
return dict(Permission=Permission)