Skip to content

Controller

Ninja-Extra APIController is responsible for handling incoming requests and returning responses to the client.

In Ninja-Extra, there are major components to creating a controller model

  • ControllerBase
  • APIController Decorator

ControllerBase

The ControllerBase class is the base class for all controllers in Django Ninja Extra. It provides the core functionality for handling requests, validating input, and returning responses in a class-based approach.

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. The request object contains information about the incoming request, such as headers, query parameters, and body data. The permission_classes property is used to define the permissions required to access the controller's routes, while the response object is used to construct the final response that is returned to the client.

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). These methods can be overridden in subclasses to provide custom behavior.

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.

from ninja_extra import ControllerBase, api_controller

@api_controller('/users')
class UserControllerBase(ControllerBase):
    ...

APIController Decorator

The api_controller decorator is used to define a class-based controller in Django Ninja Extra. It is applied to a ControllerBase class and takes several arguments to configure the routes and functionality of the controller.

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.

The second argument, auth, is a list of all Django Ninja Auth classes that should be applied to the controller's routes.

The third argument, tags, is a list of strings for OPENAPI tags purposes.

The fourth argument, permissions, is a list of all permissions that should be applied to the controller's routes.

The fifth argument, auto_import, defaults to true, which automatically adds your controller to auto import list.

for example:

import typing
from ninja_extra import api_controller, ControllerBase, permissions, route
from django.contrib.auth.models import User
from ninja.security import APIKeyQuery
from ninja import ModelSchema


class UserSchema(ModelSchema):
    class Config:
        model = User
        model_fields = ['username', 'email', 'first_name']


@api_controller('users/', auth=[APIKeyQuery()], permissions=[permissions.IsAuthenticated])
class UsersController(ControllerBase):
    @route.get('', response={200: typing.List[UserSchema]})
    def get_users(self):
        # Logic to handle GET request to the /users endpoint
        users = User.objects.all()
        return users

    @route.post('create/', response={200: UserSchema})
    def create_user(self, payload: UserSchema):
        # Logic to handle POST request to the /users endpoint
        new_user = User.objects.create(
            username=payload.username,
            email=payload.email,
            first_name=payload.first_name,
        )
        new_user.set_password('password')
        return new_user

In the above code, we have defined a controller called UsersController using the api_controller decorator. The decorator is applied to the class and takes two arguments, the URL endpoint /users and auth and permission classes. And get_users and create_user are route function that handles GET /users and POST /users/create incoming request.

Info

Inheriting from ControllerBase class gives you more IDE intellisense support.

Quick Example

Let's create an APIController to properly manage Django user model

import uuid
from ninja import ModelSchema
from ninja_extra import (
    http_get, http_post, http_generic, http_delete,
    api_controller, status, ControllerBase, pagination
)
from ninja_extra.controllers.response import Detail
from django.contrib.auth import get_user_model


class UserSchema(ModelSchema):
    class Config:
        model = get_user_model()
        model_fields = ['username', 'email', 'first_name']


@api_controller('/users')
class UsersController(ControllerBase):
    user_model = get_user_model()

    @http_post()
    def create_user(self, user: UserSchema):
        # just simulating created user
        return dict(id=uuid.uuid4())

    @http_generic('/{int:user_id}', methods=['put', 'patch'], response=UserSchema)
    def update_user(self, user_id: int):
        """ Django Ninja will serialize Django ORM model to schema provided as `response`"""
        user = self.get_object_or_exception(self.user_model, id=user_id)
        return user

    @http_delete('/{int:user_id}', response=Detail(status_code=status.HTTP_204_NO_CONTENT))
    def delete_user(self, user_id: int):
        user = self.get_object_or_exception(self.user_model, id=user_id)
        user.delete()
        return self.create_response('', status_code=status.HTTP_204_NO_CONTENT)

    @http_get("", response=pagination.PaginatedResponseSchema[UserSchema])
    @pagination.paginate(pagination.PageNumberPaginationExtra, page_size=50)
    def list_user(self):
        return self.user_model.objects.all()

    @http_get('/{user_id}', response=UserSchema)
    def get_user_by_id(self, user_id: int):
        user = self.get_object_or_exception(self.user_model, id=user_id)
        return user

In the example above, the UsersController class defines several methods that correspond to different HTTP methods, such as create_user, update_user, delete_user, list_user and get_user_by_id. These methods are decorated with http_post, http_generic, http_delete, http_get decorators respectively.

The create_user method uses http_post decorator and accepts a user argument of type UserSchema, which is a ModelSchema that is used to validate and serialize the input data. The method is used to create a new user in the system and return an ID of the user.

The update_user method uses http_generic decorator and accepts a user_id argument of type int. The decorator is configured to handle both PUT and PATCH methods and provides a response argument of type UserSchema which will be used to serialize the user object.

The delete_user method uses http_delete decorator and accepts a user_id argument of type int and a response argument of type Detail which will be used to return a 204 status code with an empty body on success.

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. 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.

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.

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.