Python - заставить декоратор классов работать с производными классами - PullRequest
4 голосов
/ 17 июня 2011

В приложении, которое мы разрабатываем с использованием Django, в некоторых случаях нам нужно автоматически назначать разрешения пользователям для некоторых моделей, у которых есть владельцы (правила для имени поля не существует, это могут быть «пользователь», «владелец»), "тренер" и т. д., также может быть более чем одним полем.) Мое решение состоит в том, чтобы создать декоратор, содержащий имена этих полей, которые будут помещены перед определением модели, например, так (без использования специфического для django кода в примерах):

@auto_assign_perms('owner', 'user')
class Test(Base):
    pass

Предположим, что Base - это абстрактный класс, производный от класса Model Джанго, где я добавляю функциональность для назначения разрешений после сохранения объекта.На данный момент я печатаю только список пользователей, назначенных для класса.Ниже вы можете найти код для декоратора и Base класса:

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

И мои модели могут выглядеть так:

@auto_assign_perms('owner', 'user')
class Test(Base):
    pass

@auto_assign_perms('coach')
class Test2(Base):
    pass

Проблема в том, что оба дочерних класса содержат все триполя ('owner', 'user', 'coach'), хотя print self.__class__.__name__ в методе Base.save() правильно отображают «Test» или «Test2».Я пытался добавить classmethod get_owners() в Base class, а затем перебирать его результаты, но это не помогает.Как я могу решить это?Может быть, я должен использовать метаклассы (я их пока не получаю)?Заранее спасибо.

Ответы [ 3 ]

4 голосов
/ 17 июня 2011

Вам необходимо установить список владельцев, а не обновлять:

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
1 голос
/ 17 июня 2011

Вы используете owners в качестве переменной класса Base, поэтому при каждом изменении owners изменение будет отображаться во всех производных классах.

Чтобы исправить это, вы должны определить переменную owners как переменную класса производных классов:

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()
0 голосов
/ 17 июня 2011

Назовите меня паранойей, но я нахожу это решение более элегантным и потому, что я не думаю, что вам нужно, чтобы владельцы вообще были переменной класса:

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() # 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...