天天看点

Flask Web 开发 用户角色

进入第九章,内容就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。

Flask Web 开发 用户角色

这里可以看到,用来设置权限登记的,是通过位运算的,最终通过16进制表示

class Permission:
	FOLLOW = 0X01
	COMMENT = 0X02
	WRITE_ARTICLES = 0X04
	MODERATE_COMMENTS = 0X08
	ADMINISTER = 0X80
           

这里建一个权限类,来进行功能的标识,比如,关注功能,是0X01,评论功能,是0X02等等

而下面这个表,是针对各种用户角色,拥有多少权限,千万不要搞混淆!!!

比如新建用户,一般就只有3种功能,就是关注,评论,写文章,那么就是0X01,0X02,0X04三者进行位运算的或运算!!(可以理解为功能累加)

Flask Web 开发 用户角色

接下来重头戏来了,书上用了一个静态方法的例子

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,是没的,可以见下图.

Flask Web 开发 用户角色

角色创建完了以后,在我们登录的时候,还需要对角色进行验证,所以,还需要有函数来进行验证。

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)