|
1 | 1 | # **Controller** |
| 2 | +The term 'APIController' is borrowed from the C# ASP.NET environment, which uses the MVC framework. Although Django is not an MVC framework, it is still possible to mimic the concept by using similar patterns and principles. |
| 3 | +Django-Ninja-Extra's APIController is modeled after the C# ASP.NET ApiController, providing an object-oriented approach to creating controller models and implementing modern software design patterns in your Django project. This allows you to use similar concepts and design patterns as in C# ASP.NET environment in your Django project. |
2 | 4 |
|
3 | | -APIController is a borrowed term from the C# ASP.NET environment which is an MVC framework. Although Django is not an MVC framework, we can still mimic the concept generally just like any other programming concept. |
| 5 | +### Why APIController in Django. |
4 | 6 |
|
5 | | -Django-Ninja-Extra APIController is modelled after C# ASP.NET ApiController, giving all OOP sense in creating your controller models and adapting recent software design patterns in your Django project. |
| 7 | +Coming from a background of modeling objects using class-based approaches, and having experience working with various API tools such as DRF, FastAPI, and Flask-Restful, you have likely noticed that these libraries primarily use function-based or class-tailored function-based approaches in writing route functions. This approach may not fully utilize the concepts of object-oriented programming when designing RESTful APIs. But despite this, these libraries are still great. |
6 | 8 |
|
7 | | -### Why APIController in Django. |
| 9 | +I have designed the APIController in Django Ninja Extra to bring a more traditional controller-based approach to Django Ninja, providing more flexibility and adaptability to recent software design patterns. |
| 10 | +If you prefer using class-based controls for building APIs, Django Ninja Extra's APIController is a great option for you. |
8 | 11 |
|
9 | | -I come from a background where we model anything in class based object, and I have worked with many API tools out there in python: DRF, FastAPI, Flask-Restful. It is either function based or class tailored to function based. |
10 | | -Don't get me wrong, there are still great libraries. In fact, some features of Django-Ninja-Extra came from DRF. So I am a big fan. I needed more. |
| 12 | +## ControllerBase |
11 | 13 |
|
12 | | -I enjoyed Django ORM and I missed it while working with FastAPI but Django-Ninja became a saving grace. It brought FastAPI to Django in one piece. And it's super fast. |
| 14 | +The `ControllerBase` class is the base class for all controllers in Django Ninja Extra. |
| 15 | +It provides the core functionality for handling requests, validating input, and returning responses in a class-based approach. |
13 | 16 |
|
14 | | -So I designed APIController to extend Django-Ninja to class-based and have something more flexible to adapt to recent software design patterns out there. |
| 17 | +The class includes properties and methods that are common to all controllers, such as the `request` object, `permission_classes`, and `response` object which are part of the `RouteContext`. |
| 18 | +The request object contains information about the incoming request, such as headers, query parameters, and body data. |
| 19 | +The permission_classes property is used to define the permissions required to access the controller's routes, |
| 20 | +while the response object is used to construct the final response that is returned to the client. |
15 | 21 |
|
16 | | -So if you enjoy class-based controls for building API, welcome aboard. |
| 22 | +In addition to the core properties, the `ControllerBase` class also includes a number of utility methods that can be used to handle common tasks such as object permission checking (`check_object_permission`), creating quick responses (`create_response`), and fetching data from database (`get_object_or_exception`). |
| 23 | +These methods can be overridden in subclasses to provide custom behavior. |
17 | 24 |
|
18 | | -## ControllerBase |
| 25 | +The ControllerBase class also includes a **dependency injection** system that allows for easy access to other services and objects within the application, such as the repository services etc. |
19 | 26 |
|
20 | 27 | ```python |
21 | | -class ControllerBase(ABC): |
| 28 | +from ninja_extra import ControllerBase, api_controller |
| 29 | + |
| 30 | +@api_controller('/users') |
| 31 | +class UserControllerBase(ControllerBase): |
22 | 32 | ... |
23 | 33 | ``` |
24 | 34 |
|
25 | | -APIController decorates any class with `ControllerBase` if its not inheriting from it. |
| 35 | + |
| 36 | +## APIController Decorator |
| 37 | +The `api_controller` decorator is used to define a class-based controller in Django Ninja Extra. |
| 38 | +It is applied to a ControllerBase class and takes several arguments to configure the routes and functionality of the controller. |
| 39 | + |
| 40 | +The first argument, `prefix_or_class`, is either a prefix string for grouping all routes registered under the controller or the class object that the decorator is applied on. |
| 41 | + |
| 42 | +The second argument, `auth`, is a list of all Django Ninja Auth classes that should be applied to the controller's routes. |
| 43 | + |
| 44 | +The third argument, `tags`, is a list of strings for OPENAPI tags purposes. |
| 45 | + |
| 46 | +The fourth argument, `permissions`, is a list of all permissions that should be applied to the controller's routes. |
| 47 | + |
| 48 | +The fifth argument, `auto_import`, defaults to true, which automatically adds your controller to auto import list. |
| 49 | + |
| 50 | +for example: |
| 51 | + |
| 52 | +```python |
| 53 | +import typing |
| 54 | +from ninja_extra import api_controller, ControllerBase, permissions, route |
| 55 | +from django.contrib.auth.models import User |
| 56 | +from ninja.security import APIKeyQuery |
| 57 | +from ninja import ModelSchema |
| 58 | + |
| 59 | + |
| 60 | +class UserSchema(ModelSchema): |
| 61 | + class Config: |
| 62 | + model = User |
| 63 | + model_fields = ['username', 'email', 'first_name'] |
| 64 | + |
| 65 | + |
| 66 | +@api_controller('users/', auth=[APIKeyQuery()], permissions=[permissions.IsAuthenticated]) |
| 67 | +class UsersController(ControllerBase): |
| 68 | + @route.get('', response={200: typing.List[UserSchema]}) |
| 69 | + def get_users(self): |
| 70 | + # Logic to handle GET request to the /users endpoint |
| 71 | + users = User.objects.all() |
| 72 | + return users |
| 73 | + |
| 74 | + @route.post('create/', response={200: UserSchema}) |
| 75 | + def create_user(self, payload: UserSchema): |
| 76 | + # Logic to handle POST request to the /users endpoint |
| 77 | + new_user = User.objects.create( |
| 78 | + username=payload.username, |
| 79 | + email=payload.email, |
| 80 | + first_name=payload.first_name, |
| 81 | + ) |
| 82 | + new_user.set_password('password') |
| 83 | + return new_user |
| 84 | + |
| 85 | +``` |
| 86 | + |
| 87 | +In the above code, we have defined a controller called `UsersController` using the `api_controller` decorator. |
| 88 | +The decorator is applied to the class and takes two arguments, the URL endpoint `/users` and `auth` and `permission` classes. |
| 89 | +And `get_users` and `create_user` are route function that handles GET `/users` and POST `/users/create` incoming request. |
| 90 | + |
26 | 91 |
|
27 | 92 | !!!info |
28 | | -Inheriting from ControllerBase class gives you more IDE intellisense support. |
| 93 | + Inheriting from ControllerBase class gives you more IDE intellisense support. |
29 | 94 |
|
30 | 95 | ## Quick Example |
31 | 96 |
|
32 | | -Let's create an APIController to manage Django user model |
| 97 | +Let's create an APIController to properly manage Django user model |
33 | 98 |
|
34 | 99 | ```python |
35 | 100 | import uuid |
@@ -79,3 +144,25 @@ class UsersController(ControllerBase): |
79 | 144 | user = self.get_object_or_exception(self.user_model, id=user_id) |
80 | 145 | return user |
81 | 146 | ``` |
| 147 | + |
| 148 | +In the example above, the `UsersController` class defines several methods that correspond to different HTTP methods, |
| 149 | +such as `create_user`, `update_user`, `delete_user`, `list_user` and `get_user_by_id`. |
| 150 | +These methods are decorated with `http_post`, `http_generic`, `http_delete`, `http_get` decorators respectively. |
| 151 | + |
| 152 | +The `create_user` method uses `http_post` decorator and accepts a user argument of type `UserSchema`, |
| 153 | +which is a `ModelSchema` that is used to validate and serialize the input data. |
| 154 | +The method is used to create a new user in the system and return an `ID` of the user. |
| 155 | + |
| 156 | +The `update_user` method uses `http_generic` decorator and accepts a `user_id` argument of type int. |
| 157 | +The decorator is configured to handle both `PUT` and `PATCH` methods and |
| 158 | +provides a response argument of type `UserSchema` which will be used to serialize the user object. |
| 159 | + |
| 160 | +The `delete_user` method uses `http_delete` decorator and accepts a `user_id` argument of type int and a response argument of type |
| 161 | +Detail which will be used to return a 204 status code with an empty body on success. |
| 162 | + |
| 163 | +The `list_user` method uses `http_get` decorator and decorated with `pagination.paginate` decorator that paginate the results of the method using `PageNumberPaginationExtra` class with page_size=50. |
| 164 | +It also provides a response argument of type `pagination.PaginatedResponseSchema[UserSchema]` which will be used to serialize and paginate the list of users returned by the method. |
| 165 | + |
| 166 | +The `get_user_by_id` method uses `http_get` decorator and accepts a `user_id` argument of type int and a response argument of type UserSchema which will be used to serialize the user object. |
| 167 | + |
| 168 | +The UsersController also use `self.get_object_or_exception(self.user_model, id=user_id)` which is a helper method that will raise an exception if the user object is not found. |
0 commit comments