@@ -65,6 +65,139 @@ def compute_api_route_function(
6565 api_controller_instance .add_operation_from_route_function (cls_route_function )
6666
6767
68+ class ControllerBase (ABC ):
69+ """
70+ Abstract Controller Base implementation all Controller class should implement
71+
72+ Example:
73+ ---------
74+ ```python
75+ from ninja_extra import api_controller, ControllerBase, http_get
76+
77+ @api_controller
78+ class SomeController(ControllerBase):
79+ @http_get()
80+ def some_method_name(self):
81+ ...
82+ ```
83+ Inheritance Example
84+ -------------------
85+ ```python
86+
87+ @api_controller
88+ class AnotherController(SomeController):
89+ @http_get()
90+ def some_method_name(self):
91+ ...
92+ ```
93+ """
94+
95+ # `_api_controller` a reference to APIController instance
96+ _api_controller : Optional ["APIController" ] = None
97+
98+ # `api` a reference to NinjaExtraAPI on APIController registration
99+ api : Optional [NinjaAPI ] = None
100+
101+ # `context` variable will change based on the route function called on the APIController
102+ # that way we can get some specific items things that belong the route function during execution
103+ context : Optional ["RouteContext" ] = None
104+
105+ Ok = Ok
106+ Id = Id
107+ Detail = Detail
108+ bad_request = bad_request
109+
110+ @classmethod
111+ def get_api_controller (cls ) -> "APIController" :
112+ if not cls ._api_controller :
113+ raise MissingAPIControllerDecoratorException (
114+ "api_controller not found. "
115+ "Did you forget to use the `api_controller` decorator"
116+ )
117+ return cls ._api_controller
118+
119+ @classmethod
120+ def permission_denied (cls , permission : BasePermission ) -> None :
121+ message = getattr (permission , "message" , None )
122+ raise PermissionDenied (message )
123+
124+ def get_object_or_exception (
125+ self ,
126+ klass : Union [Type [Model ], QuerySet ],
127+ error_message : str = None ,
128+ exception : Type [APIException ] = NotFound ,
129+ ** kwargs : Any ,
130+ ) -> Any :
131+ obj = get_object_or_exception (
132+ klass = klass , error_message = error_message , exception = exception , ** kwargs
133+ )
134+ self .check_object_permissions (obj )
135+ return obj
136+
137+ def get_object_or_none (
138+ self , klass : Union [Type [Model ], QuerySet ], ** kwargs : Any
139+ ) -> Optional [Any ]:
140+ obj = get_object_or_none (klass = klass , ** kwargs )
141+ if obj :
142+ self .check_object_permissions (obj )
143+ return obj
144+
145+ def _get_permissions (self ) -> Iterable [BasePermission ]:
146+ """
147+ Instantiates and returns the list of permissions that this view requires.
148+ """
149+ if not self .context :
150+ return
151+
152+ for permission_class in self .context .permission_classes :
153+ permission_instance = permission_class ()
154+ yield permission_instance
155+
156+ def check_permissions (self ) -> None :
157+ """
158+ Check if the request should be permitted.
159+ Raises an appropriate exception if the request is not permitted.
160+ """
161+ for permission in self ._get_permissions ():
162+ if (
163+ self .context
164+ and self .context .request
165+ and not permission .has_permission (
166+ request = self .context .request , controller = self
167+ )
168+ ):
169+ self .permission_denied (permission )
170+
171+ def check_object_permissions (self , obj : Union [Any , Model ]) -> None :
172+ """
173+ Check if the request should be permitted for a given object.
174+ Raises an appropriate exception if the request is not permitted.
175+ """
176+ for permission in self ._get_permissions ():
177+ if (
178+ self .context
179+ and self .context .request
180+ and not permission .has_object_permission (
181+ request = self .context .request , controller = self , obj = obj
182+ )
183+ ):
184+ self .permission_denied (permission )
185+
186+ def create_response (
187+ self , message : Any , status_code : int = 200 , ** kwargs : Any
188+ ) -> HttpResponse :
189+ assert self .api and self .context and self .context .request
190+ content = self .api .renderer .render (
191+ self .context .request , message , response_status = status_code
192+ )
193+ content_type = "{}; charset={}" .format (
194+ self .api .renderer .media_type , self .api .renderer .charset
195+ )
196+ return HttpResponse (
197+ content , status = status_code , content_type = content_type , ** kwargs
198+ )
199+
200+
68201class APIController :
69202 _PATH_PARAMETER_COMPONENT_RE = r"{(?:(?P<converter>[^>:]+):)?(?P<parameter>[^>]+)}"
70203
@@ -267,147 +400,14 @@ def add_api_operation(
267400 return operation
268401
269402
270- class ControllerBase (ABC ):
271- """
272- Abstract Controller Base implementation all Controller class should implement
273-
274- Example:
275- ---------
276- ```python
277- from ninja_extra import api_controller, ControllerBase, http_get
278-
279- @api_controller
280- class SomeController(ControllerBase):
281- @http_get()
282- def some_method_name(self):
283- ...
284- ```
285- Inheritance Example
286- -------------------
287- ```python
288-
289- @api_controller
290- class AnotherController(SomeController):
291- @http_get()
292- def some_method_name(self):
293- ...
294- ```
295- """
296-
297- # `_api_controller` a reference to APIController instance
298- _api_controller : Optional [APIController ] = None
299-
300- # `api` a reference to NinjaExtraAPI on APIController registration
301- api : Optional [NinjaAPI ] = None
302-
303- # `context` variable will change based on the route function called on the APIController
304- # that way we can get some specific items things that belong the route function during execution
305- context : Optional ["RouteContext" ] = None
306-
307- Ok = Ok
308- Id = Id
309- Detail = Detail
310- bad_request = bad_request
311-
312- @classmethod
313- def get_api_controller (cls ) -> APIController :
314- if not cls ._api_controller :
315- raise MissingAPIControllerDecoratorException (
316- "api_controller not found. "
317- "Did you forget to use the `api_controller` decorator"
318- )
319- return cls ._api_controller
320-
321- @classmethod
322- def permission_denied (cls , permission : BasePermission ) -> None :
323- message = getattr (permission , "message" , None )
324- raise PermissionDenied (message )
325-
326- def get_object_or_exception (
327- self ,
328- klass : Union [Type [Model ], QuerySet ],
329- error_message : str = None ,
330- exception : Type [APIException ] = NotFound ,
331- ** kwargs : Any ,
332- ) -> Any :
333- obj = get_object_or_exception (
334- klass = klass , error_message = error_message , exception = exception , ** kwargs
335- )
336- self .check_object_permissions (obj )
337- return obj
338-
339- def get_object_or_none (
340- self , klass : Union [Type [Model ], QuerySet ], ** kwargs : Any
341- ) -> Optional [Any ]:
342- obj = get_object_or_none (klass = klass , ** kwargs )
343- if obj :
344- self .check_object_permissions (obj )
345- return obj
346-
347- def _get_permissions (self ) -> Iterable [BasePermission ]:
348- """
349- Instantiates and returns the list of permissions that this view requires.
350- """
351- if not self .context :
352- return
353-
354- for permission_class in self .context .permission_classes :
355- permission_instance = permission_class ()
356- yield permission_instance
357-
358- def check_permissions (self ) -> None :
359- """
360- Check if the request should be permitted.
361- Raises an appropriate exception if the request is not permitted.
362- """
363- for permission in self ._get_permissions ():
364- if (
365- self .context
366- and self .context .request
367- and not permission .has_permission (
368- request = self .context .request , controller = self
369- )
370- ):
371- self .permission_denied (permission )
372-
373- def check_object_permissions (self , obj : Union [Any , Model ]) -> None :
374- """
375- Check if the request should be permitted for a given object.
376- Raises an appropriate exception if the request is not permitted.
377- """
378- for permission in self ._get_permissions ():
379- if (
380- self .context
381- and self .context .request
382- and not permission .has_object_permission (
383- request = self .context .request , controller = self , obj = obj
384- )
385- ):
386- self .permission_denied (permission )
387-
388- def create_response (
389- self , message : Any , status_code : int = 200 , ** kwargs : Any
390- ) -> HttpResponse :
391- assert self .api and self .context and self .context .request
392- content = self .api .renderer .render (
393- self .context .request , message , response_status = status_code
394- )
395- content_type = "{}; charset={}" .format (
396- self .api .renderer .media_type , self .api .renderer .charset
397- )
398- return HttpResponse (
399- content , status = status_code , content_type = content_type , ** kwargs
400- )
401-
402-
403403@overload
404- def api_controller () -> Type [ControllerBase ]: # type: ignore # pragma: no cover
404+ def api_controller (prefix_or_class : Type ) -> Type [ControllerBase ]: # pragma: no cover
405405 ...
406406
407407
408408@overload
409409def api_controller (
410- prefix : str = "" ,
410+ prefix_or_class : str = "" ,
411411 auth : Any = NOT_SET ,
412412 tags : Union [Optional [List [str ]], str ] = None ,
413413 permissions : Optional ["PermissionType" ] = None ,
@@ -417,24 +417,24 @@ def api_controller(
417417
418418
419419def api_controller (
420- prefix : Union [str , Type ] = "" ,
420+ prefix_or_class : Union [str , Type ] = "" ,
421421 auth : Any = NOT_SET ,
422422 tags : Union [Optional [List [str ]], str ] = None ,
423423 permissions : Optional ["PermissionType" ] = None ,
424424 auto_import : bool = True ,
425425) -> Union [Type [ControllerBase ], APIController ]:
426- if isinstance (prefix , type ):
426+ if isinstance (prefix_or_class , type ):
427427 _api_controller = APIController (
428428 prefix = "" ,
429429 auth = auth ,
430430 tags = tags ,
431431 permissions = permissions ,
432432 auto_import = auto_import ,
433433 )
434- return _api_controller (prefix )
434+ return _api_controller (prefix_or_class )
435435
436436 return APIController (
437- prefix = prefix ,
437+ prefix = prefix_or_class ,
438438 auth = auth ,
439439 tags = tags ,
440440 permissions = permissions ,
0 commit comments