In my django project (django 1.6 - soon upgrading to 1.9, python 2.7),
I'd like to apply a decorator on all my project's model classes (20-30 classes). All of these classes already inherit from a parent class called 'LinkableModel', which I wrote for a certain (non-related) purpose.
Now, I'd like to apply a class decorator to all these models. (specifically I'm referring to decorator 'python_2_unicode_compatible': https://docs.djangoproject.com/en/1.9/ref/utils/#django.utils.encoding.python_2_unicode_compatible).
When I add this decorator to their parent class 'LinkableModel', it's not inherited. Is there any way to apply a decorator to multiple classes, without adding it to each and every class?
(Theoretically I even don't mind if this decorator will be by default applied to all classes in my project...)
Code snippet:
@python_2_unicode_compatible
class LinkableModel(models.Model):
...
...
...
class MyModel1(LinkableModel):
...
...
...
class MyModel2(LinkableModel):
...
...
...
In Python 3.7 now you can do it this way:
class ParentClass:
def __init_subclass__(cls, **kwargs):
return your_decorator(_cls=cls)
it will apply decorator for each subclass of ParentClass
UPDATED: full example:
def your_decorator(_cls):
print("Hello, I'm decor!")
def wrapper():
return _cls()
return wrapper
class ParentClass:
def __init_subclass__(cls, **kwargs):
return your_decorator(_cls=cls)
class A(ParentClass):
pass
a = A()
There is (as far as I know) no simple way, because you cannot inherit decorators.
The simplest solution I can imagine is:
globals_ = globals()
for name, cls in globals_.items():
if subclass(cls, Base):
globals_[name] = decorator(cls)
It simply iterates over every global variable already defined in current module, and if it happens to be class inheriting from Base
(or Base
itself), it decorates it with decorator
.
Note that subclass will not be decorated if:
Alternatively, you can use metaclass:
class Decorate(type):
def __new__(mcls, name, bases, attrs):
return decorator(super().__new__(name, bases, attrs))
class Base(metaclass=Decorate):
pass
When you write class Base(metaclass=Decorate):
, Python uses Decorate
to create Base
and its subclasses.
All that Decorate
does, is to decorate class using decorator
before returning it.
If you use this, you will probably have a problem if you try to inherit from 2 (or more) classes, each with different metaclass.
I used the answer by @GingerPlusPlus, and created the following function, to apply a decorator to all subclasses of a class:
def apply_decorator_to_all_subclasses(globals_, base_class, decorator):
"""
Given a 'globals_' dictionary, a base class, and a decorator - this function applies the decorator to all the defined classes that derive from the base class
Note!: this function should be called only *after* the subclassess were declared
:param globals_: the given output of globals(), in the caller's context
:param base_class: the class whose descendants require the decorator
:param decorator: the decorator to apply
"""
for name, cls in globals_.items():
# Applying only on *class* items, that are descandants of base_class
if inspect.isclass(cls) and issubclass(cls, base_class) and cls != base_class:
globals_[name] = decorator(cls)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With