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