1

I am a newbie of python, I want to create 3 class, as the following:

class ProtectTemplate(TemplateView):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectTemplate, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context['phone'] = xxx
        return context


class ProtectList(ListView):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectList, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context['phone'] = xxx
        return context


class ProtectDetail(DetailView):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectDetail, self).dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context['phone'] = xxx
        return context

I think it is terrible. So I try to do as the following:

login_class = [
    ('ProtectTemplate', TemplateView),
    ('ProtectList', ListView),
    ('ProtectDetail', DetailView),
]

for c, v in login_class:
    class c(v):
        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(self.__class__, self).dispatch(*args, **kwargs)

        def get_context_data(self, **kwargs):
            context['phone'] = xxx
            return context

But it does not work. Is there anyway to batch create the 3 class?

lizs
  • 540
  • 2
  • 8
  • 18
  • Please do tell us **how** the second attempt doesn't work. Note that `super(self.__class__, self)` **will** lead to an infinite recursion error as `self.__class__` is not the right class when subclassed. – Martijn Pieters Nov 16 '16 at 10:05
  • You might be interested in [this](https://www.youtube.com/watch?v=sPiWg5jSoZI) talk by David Beazley. – Adam Barnes Nov 16 '16 at 12:19

1 Answers1

2

Your loop is the right approach, but you are rebinding c (not setting a new global name for the class), you can't use a variable to name the class in a class statement (the class will just be called c), and using super(self.__class__, self) will lead to infinite recursion issues.

You can use a factory function instead, and use the globals() dictionary to then add your newly created class to the global namespace. The function provides a closure so we can pass in the actual class object to super():

def _create_login_class(name, base):
    class LoginCls(base):
        @method_decorator(login_required)
        def dispatch(self, *args, **kwargs):
            return super(LoginCls, self).dispatch(*args, **kwargs)

        def get_context_data(self, **kwargs):
            context['phone'] = xxx
            return context

    LoginCls.__name__ = name
    LoginCls.__qualname__ = name   # Python 3
    return LoginCls

_login_class = [
    ('ProtectTemplate', TemplateView),
    ('ProtectList', ListView),
    ('ProtectDetail', DetailView),
]

for _name, _base  in _login_class:
    globals()[_name] = _create_login_class(_name, _base)

The super(LoginCls, self) expression finds LoginCls as a non-local name in the parent function namespace, so always uses the correct context for finding the next class in the MRO.

I used leading-underscore names to indicate that the factory function and the _login_class, _name and _base names are implementation details for the module.

Demo:

>>> class Base:
...     def dispatch(self, *args, **kwargs): print('Base.dispatch({}, {}) called'.format(args, kwargs))
...
>>> class TemplateView(Base): pass
...
>>> class ListView(Base): pass
...
>>> class DetailView(Base): pass
...
>>> method_decorator = lambda *args: lambda f: f
>>> xxx = 'xxx'
>>> login_required = 'login_required'
>>> def _create_login_class(name, base):
...     class LoginCls(base):
...         @method_decorator(login_required)
...         def dispatch(self, *args, **kwargs):
...             return super(LoginCls, self).dispatch(*args, **kwargs)
...         def get_context_data(self, **kwargs):
...             context['phone'] = xxx
...             return context
...     LoginCls.__name__ = name
...     LoginCls.__qualname__ = name   # Python 3
...     return LoginCls
...
>>> _login_class = [
...     ('ProtectTemplate', TemplateView),
...     ('ProtectList', ListView),
...     ('ProtectDetail', DetailView),
... ]
>>> for _name, _base  in _login_class:
...     globals()[_name] = _create_login_class(_name, _base)
...
>>> ProtectTemplate
<class '__main__.ProtectTemplate'>
>>> class SubclassDemo(ProtectTemplate):
...     def dispatch(self, *args, **kwargs):
...         print('SubclassDemo.dispatch({}, {}) called'.format(args, kwargs))
...         super(SubclassDemo, self).dispatch(*args, **kwargs)
...
>>> SubclassDemo().dispatch(42, spam='ham')
SubclassDemo.dispatch((42,), {'spam': 'ham'}) called
Base.dispatch((42,), {'spam': 'ham'}) called
Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343