In the app we're developing using Django, in some cases we need to automatically assign permissions to users for some models, that has owners (there is no rule for field's name, it can be "user", "owner", "coach" etc., also there can by more than one field.) My solution is to create a decorator containing those fields names, that will be put before model definition, like this (not using django-specific code in samples):
@auto_assign_perms('owner', 'user')
class Test(Base):
pass
Let's assume that Base
is an abstract class deriving after Django's Model
class, where I add functionality to assign permissions after object is saved. For now I only print a list of users assigned to the class. Below you can find code for the decorator and Base
class:
class auto_assign_perms(object):
def __init__(self, *users):
self.users = users
def __call__(self, cls):
cls.owners.update(self.users)
return cls
class Base(object):
owners = set()
def save(self, *args, **kwargs):
for owner in self.owners:
print owner,
print
And my models could look like this:
@auto_assign_perms('owner', 'user')
class Test(Base):
pass
@auto_assign_perms('coach')
class Test2(Base):
pass
The problem is that both child classes contains all three fields ('owner', 'user', 'coach')
, altough print self.__class__.__name__
in Base.save()
method properly shows "Test" or "Test2". I tried to add classmethod get_owners()
in Base
class and then itera开发者_如何学编程ting over its results, but it doesn't helps.
How can I solve this? Maybe I should use metaclasses (I don't get them yet)? Thanks in advance.
You need to set the list of owners, not update:
class auto_assign_perms(object):
def __init__(self, *users):
self.users = users
def __call__(self, cls):
cls.owners = set(self.users) # <- here
return cls
#some tests
@auto_assign_perms('owner', 'user')
class Test(Base):
pass
@auto_assign_perms('coach')
class Test2(Base):
pass
t = Test()
t.save()
t = Test2()
t.save()
>>>
owner user
coach
You are using owners
as a class variable of Base
so whenever you change owners
the change will be seen in all the derived classes.
To fix that you should define the owners
variable as class variable of the derived classes:
class Base(object):
def save(self, *args, **kwargs):
for owner in self.owners:
print owner,
print
@auto_assign_perms('owner', 'user')
class Test(Base):
owners = set()
@auto_assign_perms('coach')
class Test2(Base):
owners = set()
Call me paranoia but i find this solution more elegant and that because i don't think you need owners to be a class variable at all:
def auto_assign_perms(*users):
def class_wrapper(cls):
class ClassWrapper(cls):
def __init__(self, owners=users):
super(cls, self).__init__(owners=owners)
ClassWrapper.__name__ = cls.__name__
ClassWrapper.__module__ = cls.__module__
return ClassWrapper
return class_wrapper
class Base(object):
def __init__(self, owners=None):
if owners is None:
owners = set()
self.owners = owners
def save(self, *args, **kwargs):
for owner in self.owners:
print owner,
print
@auto_assign_perms('owner', 'user')
class Test1(Base):
pass
@auto_assign_perms('coach')
class Test2(Base):
pass
class Test3(Base):
pass
t = Test1(); t.save() # owner user
t = Test2(); t.save() # coach
t = Test3(); t.save() #
精彩评论