質問

I have to manage permissions and I have different user types, as for example a couple here.

def not_allowed(*args, **kwargs): return False

class User(object):
    def __init__(self, userid):
        self.userid = userid

    def __getattr__(self, attr):
        return not_allowed

    def view_profile(self)
        return True


class Admin(User):
    def edit_comment(self):
        return True

class Expert(User):
    def delete_post(self):
        return True


user = {'userid': 'user', 'roles': ['admin', 'expert']}

Now I want to be able to have MultiRole type, which in theory should simply be able to do everything that its roles are able to do.

I tried with something like this:

class MultiRoleUser(User):
    """A multirole user has all the power of all the roles together
    """
    def __init__(self, userid, roles):
        super(MultiRoleUser, self).__init__(userid)
        self.roles = roles

    def __getattr__(self, attr):
        all_funcs = [getattr(x, attr) for x in self.roles]
        return any(x() for x in all_funcs)

Which should be used as

u = MultiRoleUser('userid', [Admin, Expert])

But it doesn't work, ideally I would like to call the methods for all the classes passed in and do a or (with any).

The problem is that to call the methods I need to have an object of that type..

In theory I might even just use a dictionary for each role instead, but I liked the default to false trick which makes, and sometimes I also need a function to compute the permission.

Any suggestions?

役に立ちましたか?

解決

Although I tend to agree that you probably don't want to reinvent the wheel on this, below is version of your attempt that appears to work AFAIK.

Basically, I first I had switch to explicit new-style classes by making them derived from object so that the super() call would work, then second, I changed the self.roles = roles initialization in the MultiRoleUser class to create the instances needed. Lastly I changed how the MultiRoleUser.__getattr__()used them to handle role classes that didn't have the sought permission attribute.

def not_allowed(*args, **kwargs): return False

class User(object):
    def __init__(self, userid):
        self.userid = userid

    def __getattr__(self, attr):
        return not_allowed

    def view_profile(self):
        return True

class Admin(User):
    def edit_comment(self):
        return True

class Expert(User):
    def delete_post(self):
        return True

class MultiRoleUser(User):
    """A multirole user has all the power of all the roles together"""
    def __init__(self, userid, roles):
        super(MultiRoleUser, self).__init__(userid)
        self.roles = [role(userid) for role in roles] # create instances needed

    def __getattr__(self, attr):
        all_funcs = [getattr(x, attr, None) for x in self.roles]
        return any(x() for x in all_funcs if x) # check permission if there was one

u = MultiRoleUser('userid', [Admin, Expert])

print 'u.edit_comment:', u.edit_comment
print 'u.delete_post:', u.delete_post

BTW, I think a better Python implementation would use sets and operations with them to accomplish what you're trying to do.

他のヒント

Here's an alternative answer which uses multiple inheritance and properties to greatly simplify the implementation (because, once again, you were essentially reinventing wheels). It completely eliminates the need to have a roles attribute in the MultiRoleUser class, as well as the specialized getattr() method required to make use it.

The idea for using multiple inheritance occurred to me because it seemed odd to define class MultiRoleUser as having multiple instances of User sublclasses, rather than just being one itself. The idea of using properties came as a result of thinking about the fact that the gettattr() the class required by the class was calling the function attributes found to get their values, which is part of what properties are all about.

class User(object):
    def __init__(self, userid):
        self.userid = userid

    def __getattr__(self, attr):  # unknown/undefined permission
        return False

    @property
    def view_profile(self):
        return True

class Admin(User):
    @property
    def edit_comment(self):
        return True

class Expert(User):
    @property
    def delete_post(self):
        return True

def multi_role_user(cls_name, *roles):
    """Factory function to create a multirole user class which has the combined
       power of all the User subclasses given"""
    if not roles:
        raise TypeError('at least one subclass of class User must be specified')
    if not all(issubclass(role, User) for role in roles):
        raise TypeError('all roles must be subclasses of class User')
    return type(cls_name, roles, {})

MultiRoleUser = multi_role_user('MultiRoleUser', Admin, Expert)
u = MultiRoleUser('my_userid')

print 'u.userid:', u.userid
print 'u.view_profile:', u.view_profile
print 'u.edit_comment:', u.edit_comment
print 'u.delete_post:', u.delete_post
print 'u.can_spam:', u.can_spam
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top