fastapi-cache icon indicating copy to clipboard operation
fastapi-cache copied to clipboard

Not working on class method?

Open beyondskyway opened this issue 1 year ago • 3 comments

Not working example:

class A:

    @cache(namespace="app", expire=3600)
    async def test(self, a=True):
        print("test")
        return [], [], []

Working example:

class A:

    @staticmethod
    @cache(namespace="app", expire=3600)
    async def test(a=True):
        print("test")
        return [], [], []

Is any way to cache data on class method?

beyondskyway avatar Jun 27 '22 13:06 beyondskyway

The same here :c

crisycochea avatar Sep 13 '22 17:09 crisycochea

The cache key is generated using parameters including self which is an object reference.

By supplying custom key_builder, we can convert the object reference to string composed of attribute you want to use as a key for caching.

from abc import ABC, abstractmethod
from enum import Enum


class KeyGenMixIn(ABC):
    @abstractmethod
    def to_key(self):
        raise NotImplementedError

def custom_key_builder(
    func,
    namespace: Optional[str] = "",
    request: Optional[Request] = None,
    response: Optional[Response] = None,
    args: Optional[tuple] = None,
    kwargs: Optional[dict] = None,
):
    """
    Handle Enum and object reference.
    """
    prefix = f"{FastAPICache.get_prefix()}:{namespace}:"
    # convert Enum parameters to strings
    arguments = {}
    for key, value in kwargs.items():
        arguments[key] = value.value if isinstance(value, Enum) else value
    new_args = []
    for arg in args:
        if isinstance(arg, KeyGenMixIn):
            new_args.append(arg.to_key())
        else:
            new_args.append(arg)
    cache_key = (
        prefix
        + hashlib.md5(
            f"{func.__module__}:{func.__name__}:{new_args}:{arguments}".encode()
        ).hexdigest()
    )
    return cache_key

The custom key builder function above replaces object reference with output of to_key when the class overrides KeyGenMixIn abstract class.

Now, I've updated your example by inheriting KeyGenMixIn. Note that to_key ignores self.db_session, and specify custom_key_builder as key_builder.

class A(KeyGenMixIn):
    def __init__(self, name, db_session):
        self.name = name
        self.db_session = db_session

    def to_key(self):
        return self.name

    @cache(namespace="app", expire=3600, key_builder=custom_key_builder)
    async def test(self, a=True):
        print("test")
        return [], [], []

nksma avatar Feb 15 '23 11:02 nksma

This should probably be covered in documentation. I have ideas for marking up arguments for key use that could also help here, see #129.

mjpieters avatar May 14 '23 21:05 mjpieters