cached_property and classmethod doesnt work together, Django

Viewed 1278

I am trying to use cached_property and classmethod decorators together in a viewset but it doesnt work regardless of their mutual position. Is it any chance to make it work together or cached_property doesnt work with classmethod? Tnanks.

    @cached_property
    @classmethod
    def get_child_extra_actions(cls):
        """
        Returns only extra actions defined in this exact viewset exclude actions defined in superclasses.
        """
        all_extra_actions = cls.get_extra_actions()
        parent_extra_actions = cls.__base__.get_extra_actions()
        child_extra_actions = set(all_extra_actions).difference(parent_extra_actions)
        return (act.__name__ for act in child_extra_actions)
1 Answers

For cached property with classmethod usage i wrote that code a few days ago:

from django.utils.decorators import classproperty

class cached_classproperty(classproperty):
    def get_result_field_name(self):
        return self.fget.__name__ + "_property_result" if self.fget else None

    def __get__(self, instance, cls=None):
        result_field_name = self.get_result_field_name()

        if hasattr(cls, result_field_name):
            return getattr(cls, result_field_name)

        if not cls or not result_field_name:
            return self.fget(cls)

        setattr(cls, result_field_name, self.fget(cls))
        return getattr(cls, result_field_name)

It will be caching result in class-level.
Usage is similar as classproperty:

@cached_classproperty
def some_func(cls, *args, **kwargs):
    ...

If you do not have django in dependencies, then you may want prevent classproperty parent usage (sources). In that case you may use that decorator:

class cached_classproperty(classproperty):
    def __init__(self, method=None):
        self.fget = method

    def get_result_field_name(self):
        return self.fget.__name__ + "_property_result" if self.fget else None

    def __get__(self, instance, cls=None):
        result_field_name = self.get_result_field_name()

        if hasattr(cls, result_field_name):
            return getattr(cls, result_field_name)

        if not cls or not result_field_name:
            return self.fget(cls)

        setattr(cls, result_field_name, self.fget(cls))
        return getattr(cls, result_field_name)

    def getter(self, method):
        self.fget = method
        return self
Related