2014-03-25 176 views
6

試圖找出如何爲Django管理命令編寫一些mixin,該命令將包裝 BaseCommand.option_list而不會丟失當前類或任何繼承的類/ mixins的值。目標是避免在我的命令中執行BaseCommand.option_list + MyCommonOptionMixin.option_list + MyOtherCommonOptionMixin.option_list + (local command options)Python mixin擴展類屬性

例子:

class BaseCommmand(object): 
    option_list = (
     # Default options here. 
    ) 

    # Rest of BaseCommand code. 

我定義一些常用選項的混入:

class MyCommonOptionMixin(object): 
    option_list = (
     # Some common option/options I wish to have available in many commands 
    ) 

    def __getattribute__(self, name): 
     values = super(MyCommonOptionMixin, self).__getattribute__(name) 
     if name == 'option_list': 
      for option in self.option_list: 
       if option not in values: 
        values += option, 
     return values 

也許我還有一個,只是爲了掩飾這種情況下,我有多個。該混入既覆蓋__getattribute__

class MyOtherCommonOptionMixin(object): 
    option_list = (
     # Maybe I have another mixin I want to define separately 
    ) 

    # Tried this, does not work with more than one mixin. 
    def __getattribute__(self, name): 
     values = super(MyOtherCommonOptionMixin, self).__getattribute__(name) 
     if name == 'option_list': 
      for option in self.option_list: 
       if option not in values: 
        values += option, 
     return values 

    # Works if the mixin defines the option_list under a different name, e.g. "_mymixin_options" 
    # Then access at self._mymixin_options instead of self.option_list 


class MyCommand(MyCommonOptionMixin, MyOtherCommonOptionMixin, BaseCommand): 
    option_list = BaseCommand.option_list + (
     # Local defined options. 
    ) 

我碰到的碰撞,如果混入使用相同的名稱爲option_list財產。有沒有一種更簡單的方法來實現這個目標,比在mixin中唯一命名option_list和覆蓋__getattribute__

回答

5

documentation中的建議是顯式連接各種選項列表。也就是說,如果你想走這條路線,我認爲自定義元類是正確的方法。例如:

class CommandMetaclass(type): 
    def __new__(mcl, name, bases, dct): 
     # start with the option_list in the class we're creating 
     # use set() to avoid duplicates 
     option_list = set(dct.get('option_list', tuple())) 

     # add the options from each base class 
     for base in bases: 
      option_list.update(base.__dict__.get('option_list', tuple())) 

     # replace the original option_list with our combined version 
     dct['option_list'] = tuple(option_list) 

     return type.__new__(mcl, name, bases, dct) 

class MyCommand(MyCommonOptionMixin, MyOtherCommonOptionMixin, BaseCommand): 
    __metaclass__ = CommandMetaclass 

    option_list = ( 
     # Local defined options. 
    )