66import re
77
88from fastapi import Request , Response
9+ from fastapi .encoders import jsonable_encoder
910from redis .asyncio import Redis , ConnectionPool
10- from sqlalchemy .orm import class_mapper , DeclarativeBase
1111from fastapi import FastAPI
1212from starlette .middleware .base import BaseHTTPMiddleware , RequestResponseEndpoint
1313
1818pool : ConnectionPool | None = None
1919client : Redis | None = None
2020
21- def _serialize_sqlalchemy_object (obj : DeclarativeBase ) -> Dict [str , Any ]:
22- """
23- Serialize a SQLAlchemy DeclarativeBase object to a dictionary.
24-
25- Parameters
26- ----------
27- obj: DeclarativeBase
28- The SQLAlchemy DeclarativeBase object to be serialized.
29-
30- Returns
31- -------
32- Dict[str, Any]
33- A dictionary containing the serialized attributes of the object.
34-
35- Note
36- ----
37- - Datetime objects are converted to ISO 8601 string format.
38- - UUID objects are converted to strings before serializing to JSON.
39- """
40- if isinstance (obj , DeclarativeBase ):
41- data = {}
42- for column in class_mapper (obj .__class__ ).columns :
43- value = getattr (obj , column .name )
44-
45- if isinstance (value , datetime ):
46- value = value .isoformat ()
47-
48- if isinstance (value , UUID ):
49- value = str (value )
50-
51- data [column .name ] = value
52- return data
53-
54-
5521def _infer_resource_id (kwargs : Dict [str , Any ], resource_id_type : Union [type , str ]) -> Union [None , int , str ]:
5622 """
5723 Infer the resource ID from a dictionary of keyword arguments.
@@ -236,8 +202,8 @@ async def sample_endpoint(request: Request, resource_id: int):
236202 This decorator caches the response data of the endpoint function using a unique cache key.
237203 The cached data is retrieved for GET requests, and the cache is invalidated for other types of requests.
238204
239- Note:
240- - For caching lists of objects, ensure that the response is a list of objects, and the decorator will handle caching accordingly.
205+ Note
206+ ----
241207 - resource_id_type is used only if resource_id is not passed.
242208 """
243209 def wrapper (func : Callable ) -> Callable :
@@ -250,32 +216,26 @@ async def inner(request: Request, *args, **kwargs) -> Response:
250216
251217 formatted_key_prefix = _format_prefix (key_prefix , kwargs )
252218 cache_key = f"{ formatted_key_prefix } :{ resource_id } "
253-
254219 if request .method == "GET" :
255220 if to_invalidate_extra :
256221 raise InvalidRequestError
257222
258223 cached_data = await client .get (cache_key )
259224 if cached_data :
225+ print ("cache hit" )
260226 return json .loads (cached_data .decode ())
261-
227+
262228 result = await func (request , * args , ** kwargs )
263229
264230 if request .method == "GET" :
265- if to_invalidate_extra :
266- raise InvalidRequestError
231+ serializable_data = jsonable_encoder ( result )
232+ serialized_data = json . dumps ( serializable_data )
267233
268- if isinstance (result , list ):
269- serialized_data = json .dumps (
270- [_serialize_sqlalchemy_object (obj ) for obj in result ]
271- )
272- else :
273- serialized_data = json .dumps (
274- _serialize_sqlalchemy_object (result )
275- )
276-
277234 await client .set (cache_key , serialized_data )
278235 await client .expire (cache_key , expiration )
236+
237+ serialized_data = json .loads (serialized_data )
238+
279239 else :
280240 await client .delete (cache_key )
281241 if to_invalidate_extra :
0 commit comments