2626from django .urls import path as django_path
2727from injector import inject , is_decorated_with_inject
2828from ninja import NinjaAPI , Router
29- from ninja .constants import NOT_SET
29+ from ninja .constants import NOT_SET , NOT_SET_TYPE
3030from ninja .security .base import AuthBase
3131from ninja .signature import is_async
32+ from ninja .throttling import BaseThrottle
3233from ninja .utils import normalize_path
3334
34- from ninja_extra .constants import ROUTE_FUNCTION , THROTTLED_FUNCTION
35+ from ninja_extra .constants import ROUTE_FUNCTION , THROTTLED_FUNCTION , THROTTLED_OBJECTS
3536from ninja_extra .exceptions import APIException , NotFound , PermissionDenied , bad_request
3637from ninja_extra .helper import get_function_name
3738from ninja_extra .operation import Operation , PathView
5152
5253if TYPE_CHECKING : # pragma: no cover
5354 from ninja_extra import NinjaExtraAPI
54- from ninja_extra .throttling import BaseThrottle
5555
5656 from .route .context import RouteContext
5757
@@ -126,10 +126,10 @@ def some_method_name(self):
126126 throttling_classes : List [Type ["BaseThrottle" ]] = []
127127 throttling_init_kwargs : Optional [Dict [Any , Any ]] = None
128128
129- Ok = Ok
130- Id = Id
131- Detail = Detail
132- bad_request = bad_request
129+ Ok = Ok # TODO: remove soonest
130+ Id = Id # TODO: remove soonest
131+ Detail = Detail # TODO: remove soonest
132+ bad_request = bad_request # TODO: remove soonest
133133
134134 @classmethod
135135 def get_api_controller (cls ) -> "APIController" :
@@ -294,6 +294,7 @@ def __init__(
294294 prefix : str ,
295295 * ,
296296 auth : Any = NOT_SET ,
297+ throttle : Union [BaseThrottle , List [BaseThrottle ], NOT_SET_TYPE ] = NOT_SET ,
297298 tags : Union [Optional [List [str ]], str ] = None ,
298299 permissions : Optional ["PermissionType" ] = None ,
299300 auto_import : bool = True ,
@@ -303,6 +304,7 @@ def __init__(
303304 self .auth : Optional [AuthBase ] = auth
304305
305306 self .tags = tags # type: ignore
307+ self .throttle = throttle
306308
307309 self .auto_import : bool = auto_import # set to false and it would be ignored when api.auto_discover is called
308310 # `controller_class` target class that the APIController wraps
@@ -348,8 +350,6 @@ def tags(self, value: Union[str, List[str], None]) -> None:
348350 self ._tags = tag
349351
350352 def __call__ (self , cls : ControllerClassType ) -> ControllerClassType :
351- from ninja_extra .throttling import throttle
352-
353353 self .auto_import = getattr (cls , "auto_import" , self .auto_import )
354354 if not issubclass (cls , ControllerBase ):
355355 # We force the cls to inherit from `ControllerBase` by creating another type.
@@ -360,8 +360,15 @@ def __call__(self, cls: ControllerClassType) -> ControllerClassType:
360360 assert isinstance (
361361 cls .throttling_classes , (list , tuple )
362362 ), f"Controller[{ cls .__name__ } ].throttling_class must be a list or tuple"
363- has_throttling_classes = len (cls .throttling_classes ) > 0
364- throttling_init_kwargs = cls .throttling_init_kwargs or {}
363+
364+ throttling_objects : Union [BaseThrottle , List [BaseThrottle ], NOT_SET_TYPE ]
365+ if cls .throttling_classes :
366+ throttling_init_kwargs = cls .throttling_init_kwargs or {}
367+ throttling_objects = [
368+ item (** throttling_init_kwargs ) for item in cls .throttling_classes
369+ ]
370+ else :
371+ throttling_objects = self .throttle
365372
366373 if not self .tags :
367374 tag = str (cls .__name__ ).lower ().replace ("controller" , "" )
@@ -386,10 +393,13 @@ def __call__(self, cls: ControllerClassType) -> ControllerClassType:
386393
387394 for _ , v in self ._controller_class_route_functions .items ():
388395 throttled_endpoint = v .as_view .__dict__ .get (THROTTLED_FUNCTION )
389- if not throttled_endpoint and has_throttling_classes :
390- v .route .view_func = throttle (
391- * cls .throttling_classes , ** throttling_init_kwargs
392- )(v .route .view_func )
396+
397+ if throttled_endpoint or throttling_objects is not NOT_SET :
398+ v .route .route_params .throttle = v .as_view .__dict__ .get (
399+ THROTTLED_OBJECTS , lambda : throttling_objects
400+ )()
401+ setattr (v .route .view_func , THROTTLED_FUNCTION , True )
402+
393403 self ._add_operation_from_route_function (v )
394404
395405 if not is_decorated_with_inject (cls .__init__ ):
@@ -463,6 +473,7 @@ def add_api_operation(
463473 view_func : Callable ,
464474 * ,
465475 auth : Any = NOT_SET ,
476+ throttle : Union [BaseThrottle , List [BaseThrottle ], NOT_SET_TYPE ] = NOT_SET ,
466477 response : Any = NOT_SET ,
467478 operation_id : Optional [str ] = None ,
468479 summary : Optional [str ] = None ,
@@ -504,13 +515,14 @@ def add_api_operation(
504515 url_name = url_name ,
505516 include_in_schema = include_in_schema ,
506517 openapi_extra = openapi_extra ,
518+ throttle = throttle ,
507519 )
508520 return operation
509521
510522
511523@overload
512524def api_controller (
513- prefix_or_class : Type [T ],
525+ prefix_or_class : Union [ ControllerClassType , Type [T ] ],
514526) -> Union [Type [ControllerBase ], Type [T ]]: # pragma: no cover
515527 ...
516528
@@ -519,16 +531,20 @@ def api_controller(
519531def api_controller (
520532 prefix_or_class : str = "" ,
521533 auth : Any = NOT_SET ,
534+ throttle : Union [BaseThrottle , List [BaseThrottle ], NOT_SET_TYPE ] = NOT_SET ,
522535 tags : Union [Optional [List [str ]], str ] = None ,
523536 permissions : Optional ["PermissionType" ] = None ,
524537 auto_import : bool = True ,
525- ) -> Callable [[Type [T ]], Union [Type [ControllerBase ], Type [T ]]]: # pragma: no cover
538+ ) -> Callable [
539+ [Union [Type , Type [T ]]], Union [Type [ControllerBase ], Type [T ]]
540+ ]: # pragma: no cover
526541 ...
527542
528543
529544def api_controller (
530- prefix_or_class : Union [str , ControllerClassType ] = "" ,
545+ prefix_or_class : Union [str , Union [ ControllerClassType , Type ] ] = "" ,
531546 auth : Any = NOT_SET ,
547+ throttle : Union [BaseThrottle , List [BaseThrottle ], NOT_SET_TYPE ] = NOT_SET ,
532548 tags : Union [Optional [List [str ]], str ] = None ,
533549 permissions : Optional ["PermissionType" ] = None ,
534550 auto_import : bool = True ,
@@ -540,6 +556,7 @@ def api_controller(
540556 tags = tags ,
541557 permissions = permissions ,
542558 auto_import = auto_import ,
559+ throttle = throttle ,
543560 )(prefix_or_class )
544561
545562 def _decorator (cls : ControllerClassType ) -> ControllerClassType :
@@ -549,6 +566,7 @@ def _decorator(cls: ControllerClassType) -> ControllerClassType:
549566 tags = tags ,
550567 permissions = permissions ,
551568 auto_import = auto_import ,
569+ throttle = throttle ,
552570 )(cls )
553571
554572 return _decorator
0 commit comments