Reputation: 550
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?
Upvotes: 1
Views: 842
Reputation: 1121386
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
Upvotes: 2