33import typing
44from collections .abc import Callable
55from http import HTTPStatus
6- from typing import Any
6+ from typing import Any , Dict , ClassVar
77
88from pydantic import BaseModel
99
@@ -43,6 +43,8 @@ class BaseRouter:
4343 It can include sub-routers and generate an OpenAPI specification from
4444 the declared routes.
4545 """
46+ # Class-level cache for model schemas to avoid redundant processing
47+ _model_schema_cache : ClassVar [Dict [str , dict ]] = {}
4648
4749 def __init__ (
4850 self ,
@@ -140,7 +142,7 @@ def generate_openapi(self) -> dict:
140142 }
141143 definitions = {}
142144 for path , method , endpoint in self ._routes :
143- openapi_path = re .sub (r"<(?:w:)?(\w+)>" , r"{\1}" , path )
145+ openapi_path = re .sub (r"<(?:\ w:)?(\w+)>" , r"{\1}" , path )
144146 operation = self ._build_operation (
145147 endpoint , definitions , openapi_path , method
146148 )
@@ -259,18 +261,35 @@ def _serialize_response(result: Any) -> Any:
259261 return {k : BaseRouter ._serialize_response (v ) for k , v in result .items ()}
260262 return result
261263
262- @staticmethod
263- def _get_model_schema (model : type [BaseModel ], definitions : dict ) -> dict :
264- model_schema = model .model_json_schema (
265- ref_template = "#/components/schemas/{model}"
266- )
267- for key in ("definitions" , "$defs" ):
268- if key in model_schema :
269- definitions .update (model_schema [key ])
270- del model_schema [key ]
271- if model .__name__ not in definitions :
272- definitions [model .__name__ ] = model_schema
273- return {"$ref" : f"#/components/schemas/{ model .__name__ } " }
264+ @classmethod
265+ def _get_model_schema (cls , model : type [BaseModel ], definitions : dict ) -> dict :
266+ """
267+ Get the OpenAPI schema for a Pydantic model, with caching for better performance.
268+ """
269+ model_name = model .__name__
270+ cache_key = f"{ model .__module__ } .{ model_name } "
271+
272+ # Check if the schema is already in the class-level cache
273+ if cache_key not in cls ._model_schema_cache :
274+ # Generate the schema if it's not in the cache
275+ model_schema = model .model_json_schema (
276+ ref_template = "#/components/schemas/{model}"
277+ )
278+
279+ # Process and store nested definitions
280+ for key in ("definitions" , "$defs" ):
281+ if key in model_schema :
282+ definitions .update (model_schema [key ])
283+ del model_schema [key ]
284+
285+ # Add schema to the cache
286+ cls ._model_schema_cache [cache_key ] = model_schema
287+
288+ # Make sure the schema is in the definitions dictionary
289+ if model_name not in definitions :
290+ definitions [model_name ] = cls ._model_schema_cache [cache_key ]
291+
292+ return {"$ref" : f"#/components/schemas/{ model_name } " }
274293
275294 @staticmethod
276295 def render_swagger_ui (openapi_json_url : str ) -> str :
@@ -351,4 +370,4 @@ def resolve_endpoint_params(
351370 def openapi (self ) -> dict :
352371 if self ._openapi_schema is None :
353372 self ._openapi_schema = self .generate_openapi ()
354- return self ._openapi_schema
373+ return self ._openapi_schema
0 commit comments