Question

Trying to figure out how to write some mixins for Django management command that will wrap the BaseCommand.option_list without losing the value of the current class or any inherited classes/mixins. The goal is to avoid doing BaseCommand.option_list + MyCommonOptionMixin.option_list + MyOtherCommonOptionMixin.option_list + ( local command options ) in my commands.

Example:

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

    # Rest of BaseCommand code. 

I define a mixin with some common options:

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

Maybe I have one more, just to cover that case where I have multiple. The mixins both override __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.
    )

I've run into collision if the mixins use the same name for the option_list property. Is there a cleaner way to achieve this goal than naming the option_list uniquely inside the mixins and overriding __getattribute__?

Was it helpful?

Solution

The advice in the documentation is to explicitly concatenate the various option lists. That said, if you want to go this route I think a custom metaclass is the right approach. Something like:

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.
    )
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top