### Install flit Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/contribution.md Install flit globally, which is required for the development setup. ```bash pip install flit ``` -------------------------------- ### Install development dependencies Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/contribution.md Install development libraries and pre-commit hooks. ```bash make install-full ``` -------------------------------- ### GET /test Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_route.md Example of using the route.get decorator to define a GET endpoint on a controller. ```APIDOC ## [GET] /test ### Description Returns a simple message 'test'. ### Method GET ### Endpoint /test ### Response #### Success Response (200) - **message** (string) - The message 'test'. #### Response Example { "message": "test" } ``` -------------------------------- ### Basic Throttling with AnonRateThrottle and UserRateThrottle Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/throttling.md Basic throttling setup using AnonRateThrottle and UserRateThrottle. Note that this example won't be throttled because the default scope for both throttles is 'none'. ```python from ninja_extra import NinjaExtraAPI from ninja_extra.throttling import UserRateThrottle, AnonRateThrottle api = NinjaExtraAPI() @api.get('/users', throttle=[AnonRateThrottle(), UserRateThrottle()]) def my_throttled_endpoint(request): return 'foo' ``` -------------------------------- ### GET /users Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/throttling.md Example of applying a DynamicRateThrottle with scope 'burst' to a GET endpoint. ```APIDOC ## [GET] /users ### Description Retrieves a list of users with a burst rate throttle applied. ### Method GET ### Endpoint /users ### Parameters #### Query Parameters - **throttle** (DynamicRateThrottle) - Required - Throttle scope set to 'burst' ### Request Example No request body required. ### Response #### Success Response (200) - **body** (string) - Returns 'foo' #### Response Example "foo" ``` -------------------------------- ### Basic API setup with NinjaExtraAPI Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/index.md Create an api.py file with a NinjaExtraAPI instance, a function-based endpoint, a class-based controller, and controller registration. ```python from ninja_extra import NinjaExtraAPI, api_controller, http_get api = NinjaExtraAPI() # Function-based endpoint example @api.get("/hello", tags=['Basic']) def hello(request, name: str = "World"): return {"message": f"Hello, {name}!"} # Class-based controller example @api_controller('/math', tags=['Math']) class MathController: @http_get('/add') def add(self, a: int, b: int): """Add two numbers""" return {"result": a + b} @http_get('/multiply') def multiply(self, a: int, b: int): """Multiply two numbers""" return {"result": a * b} # Register your controllers api.register_controllers(MathController) ``` -------------------------------- ### Install django-ninja-extra and ninja-schema Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller/01_getting_started.md Install the required packages. The ninja-schema package is optional but recommended for generating schemas. ```bash pip install django-ninja-extra ninja-schema ``` -------------------------------- ### GET /say-after Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_route.md Example of defining an asynchronous endpoint using the http_get decorator. ```APIDOC ## [GET] /say-after ### Description Asynchronous endpoint that sleeps for a given delay and returns a message with the provided word. ### Method GET ### Endpoint /say-after ### Parameters #### Query Parameters - **delay** (int) - Required - The number of seconds to sleep. - **word** (string) - Required - The word to include in the response. ### Response #### Success Response (200) - **saying** (string) - The provided word. #### Response Example { "saying": "hello" } ``` -------------------------------- ### Async Permission Example with Custom Permission Class Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_async_permission.md Complete example showing async permission usage with ninja-extra. Defines a custom AsyncBasePermission subclass and a controller with mixed permission levels: AllowAny on list, controller-level IsAuthenticated, and instance-based custom permission. Uses async methods and sync_to_async for database access. ```python from django.contrib.auth.models import User from asgiref.sync import sync_to_async from ninja_extra import api_controller, http_get, http_post, ControllerBase from ninja_extra.permissions import AsyncBasePermission, IsAuthenticated, AllowAny # Custom async permission class IsStaffOrOwnerAsync(AsyncBasePermission): async def has_permission_async(self, request, controller): return request.user.is_authenticated async def has_object_permission_async(self, request, controller, obj): # Either the user is staff or owns the object return request.user.is_staff or obj.owner_id == request.user.id # Controller using mixed permissions @api_controller("/users", permissions=[IsAuthenticated]) class UserController(ControllerBase): @http_get("/", permissions=[AllowAny]) async def list_users(self, request): # Public endpoint users = await sync_to_async(list)(User.objects.values('id', 'username')[:10]) return users @http_get("/{user_id}") async def get_user(self, request, user_id: int): # Protected by IsAuthenticated from the controller user = await self.aget_object_or_exception(User, id=user_id) return {"id": user.id, "username": user.username} @http_post("/update/{user_id}", permissions=[IsStaffOrOwnerAsync()]) async def update_user(self, request, user_id: int, data: dict): # Protected by custom async permission user = await self.aget_object_or_exception(User, id=user_id) # Update user data return {"status": "success"} ``` -------------------------------- ### Basic API setup with NinjaExtraAPI and api_controller Source: https://github.com/eadwincode/django-ninja-extra/blob/master/README.md Create a basic API with a function-based endpoint and a class-based controller. The controller is registered with api.register_controllers(). ```python from ninja_extra import NinjaExtraAPI, api_controller, http_get api = NinjaExtraAPI() # Function-based endpoint example @api.get("/hello", tags=['Basic']) def hello(request, name: str = "World"): return {"message": f"Hello, {name}!"} # Class-based controller example @api_controller('/math', tags=['Math']) class MathController: @http_get('/add') def add(self, a: int, b: int): """Add two numbers""" return {"result": a + b} @http_get('/multiply') def multiply(self, a: int, b: int): """Multiply two numbers""" return {"result": a * b} # Register your controllers api.register_controllers(MathController) ``` -------------------------------- ### Install django-ninja-extra with pip Source: https://github.com/eadwincode/django-ninja-extra/blob/master/README.md Install the django-ninja-extra package using pip. ```bash pip install django-ninja-extra ``` -------------------------------- ### Create api.py with a hello endpoint Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/index.md Defines a simple API with a GET endpoint using NinjaExtraAPI. ```python from ninja_extra import NinjaExtraAPI api = NinjaExtraAPI() # function definition using Django-Ninja default router @api.get("/hello") def hello(request): return "Hello world" ``` -------------------------------- ### Basic Filtering with Pagination Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/pagination.md Basic example combining pagination with FilterSchema. Filters are applied to the queryset before pagination. The @paginate decorator uses PageNumberPaginationExtra with filter_schema and page_size=20. Example API calls: GET /api/books/?page=1&page_size=20, GET /api/books/?title=Python&page=1, GET /api/books/?author=John&min_price=10&page=2. ```python from typing import Optional from ninja import FilterSchema from ninja_extra.pagination import paginate, PageNumberPaginationExtra, PaginatedResponseSchema from ninja_extra import api_controller, route, NinjaExtraAPI from ninja import ModelSchema from myapp.models import Book class BookSchema(ModelSchema): class Config: model = Book model_fields = ['id', 'title', 'author', 'price', 'published_date'] # Define a FilterSchema for your model class BookFilterSchema(FilterSchema): title: Optional[str] = None author: Optional[str] = None min_price: Optional[float] = None @api_controller('/books') class BookController: @route.get('', response=PaginatedResponseSchema[BookSchema]) @paginate(PageNumberPaginationExtra, filter_schema=BookFilterSchema, page_size=20) def list_books(self): return Book.objects.all() api = NinjaExtraAPI(title='Books API') api.register_controllers(BookController) ``` -------------------------------- ### Usage Example with PageNumberPaginationExtra and Default LimitOffsetPagination Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/pagination.md Complete usage example showing a controller with two paginated endpoints: one using PageNumberPaginationExtra with page_size=50 and PaginatedResponseSchema, and one using the default LimitOffsetPagination with List response. The api instance is created and the controller registered. ```python from typing import List from ninja_extra.pagination import ( paginate, PageNumberPaginationExtra, PaginatedResponseSchema ) from ninja_extra import api_controller, route, NinjaExtraAPI from ninja import ModelSchema from django.contrib.auth import get_user_model user_model = get_user_model() class UserSchema(ModelSchema): class Config: model = user_model model_fields = ['username', 'email'] @api_controller('/users') class UserController: @route.get('', response=PaginatedResponseSchema[UserSchema]) @paginate(PageNumberPaginationExtra, page_size=50) def get_users(self): return user_model.objects.all() @route.get('/limit', response=List[UserSchema]) @paginate def get_users_with_limit(self): # this will use default paginator class - ninja_extra.pagination.LimitOffsetPagination return user_model.objects.all() api = NinjaExtraAPI(title='Pagination Test') api.register_controllers(UserController) ``` -------------------------------- ### Install ninja-schema package Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller.md Command to install the ninja-schema package, which is required for automatic schema generation in Model Controllers. ```shell pip install ninja-schema ``` -------------------------------- ### Test GET request with TestClient Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/testing.md Use TestClient to test a synchronous GET route on a controller. The example shows a test method that instantiates the client with the controller and asserts the response status and JSON body. ```python import pytest from .controllers import UserController from ninja_extra.testing import TestClient @pytest.mark.django_db class TestMyMathController: def test_get_users(self): client = TestClient(UserController) response = client.get('/users') assert response.status_code == 200 assert response.json()[0] == { 'first_name': 'Ninja Extra', 'username': 'django_ninja', 'email': 'john.doe@gmail.com' } ``` -------------------------------- ### Using UserRateThrottle with Global Settings Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/throttling.md Using a single UserRateThrottle instance directly in the throttle parameter. This example relies on the global THROTTLE_RATES settings for the 'user' scope. ```python from ninja_extra import NinjaExtraAPI from ninja_extra.throttling import UserRateThrottle api = NinjaExtraAPI() @api.get('/users', throttle=UserRateThrottle()) def my_throttled_endpoint(request): return 'foo' ``` -------------------------------- ### Configure ModelController with LimitOffsetPagination Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller.md Example of setting up a ModelController with pagination using ninja's LimitOffsetPagination and NinjaPaginationResponseSchema. The model_config specifies the model, pagination class, and pagination schema. ```python from ninja.pagination import LimitOffsetPagination from ninja_extra.schemas import NinjaPaginationResponseSchema from ninja_extra import ( ModelConfig, ModelControllerBase, api_controller, ModelPagination ) @api_controller("/events") class EventModelController(ModelControllerBase): model_config = ModelConfig( model=Event, pagination=ModelPagination( klass=LimitOffsetPagination, pagination_schema=NinjaPaginationResponseSchema ), ) ``` -------------------------------- ### Query parameters in Django-Ninja Extra Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/query.md Defines an API controller with a GET endpoint that accepts query parameters. The URL example shows how to call the endpoint with offset and limit query parameters. ```python from ninja import constants from ninja_extra import api_controller, route @api_controller('', tags=['My Operations'], auth=constants.NOT_SET, permissions=[]) class MyAPIController: weapons = ["Ninjato", "Shuriken", "Katana", "Kama", "Kunai", "Naginata", "Yari"] @route.get("/weapons") def list_weapons(self, limit: int = 10, offset: int = 0): return self.weapons[offset: offset + limit] ``` ```http http://localhost:8000/api/weapons?offset=0&limit=10 ``` -------------------------------- ### Advanced Route Context Examples with HasTeamAccess and HasProjectAccess Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_permission.md Demonstrates combining multiple permission classes using the & operator. HasTeamAccess checks team permissions, while HasProjectAccess checks project permissions with a required role. Both use compute_route_parameters() to access path parameters. ```python from ninja_extra import permissions, api_controller, http_get, http_post, ControllerBase from django.http import HttpRequest from typing import Optional class HasTeamAccess(permissions.BasePermission): def has_permission(self, request: HttpRequest, controller: ControllerBase): # Compute parameters to access both path and query parameters controller.context.compute_route_parameters() team_id = controller.context.kwargs.get('team_id') role = controller.context.kwargs.get('role', 'member') # Default to 'member' return request.user.has_team_permission(team_id, role) class HasProjectAccess(permissions.BasePermission): def __init__(self, required_role: str): self.required_role = required_role def has_permission(self, request: HttpRequest, controller: ControllerBase): controller.context.compute_route_parameters() # Access multiple parameters project_id = controller.context.kwargs.get('project_id') team_id = controller.context.kwargs.get('team_id') return ( request.user.is_authenticated and request.user.has_project_permission(project_id, team_id, self.required_role) ) @api_controller("/teams") class TeamProjectController: @http_get("/{team_id}/projects/{project_id}", permissions=[HasTeamAccess() & HasProjectAccess("viewer")]) def get_project(self, team_id: int, project_id: int): return {"message": f"Access granted to project {project_id} in team {team_id}"} @http_post("/{team_id}/projects", permissions=[HasTeamAccess() & HasProjectAccess("admin")]) def create_project(self, team_id: int, name: str, description: Optional[str] = None): return { "message": f"Created project '{name}' in team {team_id}", "description": description } ``` -------------------------------- ### IsAuthenticatedOrReadOnly permission example Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_permission.md Use this to allow read-only access to unauthenticated users, but require authentication for write operations. The example shows a blog controller with a GET endpoint accessible to everyone and a POST endpoint restricted to authenticated users. ```python from ninja_extra import permissions, api_controller, http_get, http_post @api_controller("/posts", permissions=[permissions.IsAuthenticatedOrReadOnly]) class BlogController: @http_get("/") # Accessible to everyone def list_posts(self): return {"posts": ["Post 1", "Post 2"]} @http_post("/") # Only accessible to authenticated users def create_post(self, request, title: str): return {"message": f"Post '{title}' created by {request.user.username}"} ``` -------------------------------- ### IsAdminUser permission example Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_permission.md Use this to restrict access to admin users (users with `is_staff=True`). The example shows an admin controller with a stats endpoint. ```python from ninja_extra import permissions, api_controller, http_get @api_controller("/admin", permissions=[permissions.IsAdminUser]) class AdminController: @http_get("/stats") def get_stats(self): return {"active_users": 100, "total_posts": 500} ``` -------------------------------- ### AllowAny permission example Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_permission.md Use this to allow unrestricted access to any endpoint. The permission is applied at the controller level via the `permissions` argument. ```python from ninja_extra import permissions, api_controller, http_get @api_controller(permissions=[permissions.AllowAny]) class PublicController: @http_get("/public") def public_endpoint(self): return {"message": "This endpoint is public"} ``` -------------------------------- ### Define API v1 controller with NinjaExtraAPI Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/versioning.md Defines a versioned API controller for version 1.0.0. The controller includes two GET routes: '/hello' and '/example'. The NinjaExtraAPI instance is created with version='1.0.0' and the controller is registered. ```Python from ninja_extra import NinjaExtraAPI, route, api_controller @api_controller class MyV1Controller: @route.get('/hello') def hello(self): return {'message': 'Hello from V1'} @route.get('/example') def example(self): return {'message': 'Hello from V1 Example'} api = NinjaExtraAPI(version='1.0.0') api.register_controllers(MyV1Controller) ``` -------------------------------- ### Basic Model Controller for Event Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller/01_getting_started.md Basic Model Controller setup for the Event model. Automatically generates CRUD endpoints. If model_config.model is not set, the controller becomes a regular NinjaExtra controller. ```python from ninja_extra import ( ModelConfig, ModelControllerBase, api_controller, NinjaExtraAPI ) from .models import Event @api_controller("/events") class EventModelController(ModelControllerBase): model_config = ModelConfig( model=Event, ) # Register the controller with your API api = NinjaExtraAPI() api.register_controllers(EventModelController) ``` -------------------------------- ### Define list endpoint with path and query parameters Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller.md Example of using ModelEndpointFactory.list with a path that includes path parameters ({int:id}, {post_id}) and query parameters (query, query1). The handler function asserts the presence of these parameters and retrieves tags for a post. ```python list_post_tags = ModelEndpointFactory.list( path="/{int:id}/tags/{post_id}?query=int&query1=int", schema_out=model_config.retrieve_schema, queryset_getter=lambda self, **kw: self.list_post_tags_query(**kw) ) def list_post_tags_query(self, **kwargs): assert kwargs['id'] assert kwargs['query'] assert kwargs['query1'] post_id = kwargs['post_id'] return Post.objects.filter(id=post_id).first().tags.all() ``` -------------------------------- ### IsAuthenticated permission example Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_permission.md Use this to restrict access to authenticated users only. The example shows a profile endpoint that uses `request.user` to return user information. ```python from ninja_extra import permissions, api_controller, http_get @api_controller(permissions=[permissions.IsAuthenticated]) class PrivateController: @http_get("/profile") def get_profile(self, request): return { "username": request.user.username, "email": request.user.email } ``` -------------------------------- ### ModelService with Dependency Injection for Event Notifications and Activity Tracking Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller/03_model_service.md These two blocks form a complete example of dependency injection in a ModelService. The first defines EmailService and UserActivityService, which are injected into EventModelService. The second shows EventModelService overriding create, update, and delete to track activity and send notifications, using the injected services. ```python from datetime import datetime from typing import Any, Optional from django.core.mail import send_mail from ninja_extra import ModelService, api_controller, ModelConfig from pydantic import BaseModel from injector import inject class EmailService: """Service for handling email notifications""" def send_event_notification(self, event_data: dict, recipient_email: str): subject = f"Event Update: {event_data['title']}" message = ( f"Event Details:\n" f"Title: {event_data['title']}\n" f"Date: {event_data['start_date']} to {event_data['end_date']}\n" ) send_mail( subject=subject, message=message, from_email="events@example.com", recipient_list=[recipient_email], fail_silently=False, ) class UserActivityService: """Service for tracking user activities""" def track_activity(self, user_id: int, action: str, details: dict): UserActivity.objects.create( user_id=user_id, action=action, details=details, timestamp=datetime.now() ) ``` ```python class EventModelService(ModelService): """ Event service with email notifications and activity tracking. Dependencies are automatically injected by the framework. """ @inject def __init__( self, model: Event, email_service: EmailService, activity_service: UserActivityService ): super().__init__(model=model) self.email_service = email_service self.activity_service = activity_service def create(self, schema: BaseModel, **kwargs: Any) -> Any: # Create the event event = super().create(schema, **kwargs) # Track the creation activity if user_id := kwargs.get('user_id'): self.activity_service.track_activity( user_id=user_id, action="event_created", details={ "event_id": event.id, "title": event.title } ) # Send notification to organizer if organizer_email := kwargs.get('organizer_email'): self.email_service.send_event_notification( event_data=schema.model_dump(), recipient_email=organizer_email ) return event def update(self, instance: Event, schema: BaseModel, **kwargs: Any) -> Any: # Update the event updated_event = super().update(instance, schema, **kwargs) # Track the update activity if user_id := kwargs.get('user_id'): self.activity_service.track_activity( user_id=user_id, action="event_updated", details={ "event_id": updated_event.id, "title": updated_event.title, "changes": schema.model_dump() } ) # Notify relevant parties about the update if notify_participants := kwargs.get('notify_participants'): for participant in updated_event.participants.all(): self.email_service.send_event_notification( event_data=schema.model_dump(), recipient_email=participant.email ) return updated_event def delete(self, instance: Event, **kwargs: Any) -> Any: event_data = { "id": instance.id, "title": instance.title } # Delete the event super().delete(instance, **kwargs) # Track the deletion if user_id := kwargs.get('user_id'): self.activity_service.track_activity( user_id=user_id, action="event_deleted", details=event_data ) # Notify participants about cancellation if notify_participants := kwargs.get('notify_participants'): for participant in instance.participants.all(): self.email_service.send_event_notification( event_data={ **event_data, "message": "Event has been cancelled" }, recipient_email=participant.email ) ``` -------------------------------- ### Register the Services (TodoModule) Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/dependency-injection/index.md Defines an injector module to bind services with scopes. The repository and service are bound as singletons, with a commented example of using noscope for request-specific services. ```python from injector import Module, singleton, noscope, Binder class TodoModule(Module): def configure(self, binder: Binder) -> None: # Singleton scope - same instance for entire application # TodoRepository maintains application state (the todos list) binder.bind(TodoRepository, to=TodoRepository, scope=singleton) # Singleton scope - stateless service that only contains business logic binder.bind(TodoService, to=TodoService, scope=singleton) # Example of when to use noscope # binder.bind(RequestContextService, to=RequestContextService, scope=noscope) ``` -------------------------------- ### Example JSON request body with optional description Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/body_request.md Shows a valid JSON object for the Item model, including the optional description field. ```JSON { "name": "Katana", "description": "An optional description", "price": 299.00, "quantity": 10 } ``` -------------------------------- ### Asynchronous authentication with AsyncHttpBearer Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/authentication.md Rewrites the first auth example using AsyncHttpBearer and an async endpoint. When using asynchronous auth classes, the endpoint handler must be an async function; if applied to the controller, all route handlers must be async. ```Python from ninja_extra import api_controller, route from ninja_extra.security import AsyncHttpBearer from ninja.constants import NOT_SET class AuthBearer(AsyncHttpBearer): async def authenticate(self, request, token): # await some actions if token == "supersecret": return token @api_controller(tags=['My Operations'], auth=NOT_SET, permissions=[]) class MyController: @route.get("/bearer", auth=AuthBearer()) async def bearer(self): return {"token": self.context.request.auth} ``` -------------------------------- ### Search users with searching decorator Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/searching.md Example controller using the searching decorator to filter users by username and email, and a second endpoint using the '=' prefix for exact email matches. The search parameter is passed via the query string, e.g., ?search=someuser. ```python from typing import List from ninja_extra.searching import searching, Searching from ninja_extra import api_controller, route, NinjaExtraAPI from ninja import ModelSchema from django.contrib.auth import get_user_model user_model = get_user_model() class UserSchema(ModelSchema): class Config: model = user_model model_fields = ['username', 'email'] @api_controller('/users') class UserController: @route.get('', response=List[UserSchema]) @searching(Searching, search_fields=['username', 'email']) def get_users(self): return user_model.objects.all() @route.get('/iexact-email', response=List[UserSchema]) @searching(search_fields=['=email']) def get_users_with_search_iexact_email(self): return [u for u in user_model.objects.all()] api = NinjaExtraAPI(title='Searching Test') api.register_controllers(UserController) ``` -------------------------------- ### Create and activate virtual environment Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/contribution.md Create and activate a virtual environment for the project. The first command is for Linux/macOS, the second for Windows. ```bash python -m venv venv source venv/bin/activate # Linux/macOS ``` ```bash python -m venv venv .\venv\Scripts\activate # Windows ``` -------------------------------- ### GET /users/{id} Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/throttling.md Example of applying a DynamicRateThrottle with scope 'sustained' to a GET endpoint with a path parameter. ```APIDOC ## [GET] /users/{id} ### Description Retrieves a single user by ID with a sustained rate throttle applied. ### Method GET ### Endpoint /users/{id} ### Parameters #### Path Parameters - **id** (int) - Required - The user ID. #### Query Parameters - **throttle** (DynamicRateThrottle) - Required - Throttle scope set to 'sustained' ### Request Example No request body required. ### Response #### Success Response (200) - **body** (string) - Returns 'foo' #### Response Example "foo" ``` -------------------------------- ### GET /throttled-controller/endpoint_1 Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/throttling.md Example of a controller endpoint with an overridden throttle. ```APIDOC ## [GET] /throttled-controller/endpoint_1 ### Description Endpoint that overrides the controller-level throttle with a burst rate throttle. ### Method GET ### Endpoint /throttled-controller/endpoint_1 ### Parameters #### Query Parameters - **throttle** (DynamicRateThrottle) - Required - Throttle scope set to 'burst' (overrides controller-level throttle) ### Request Example No request body required. ### Response #### Success Response (200) - **body** (string) - Returns 'foo' #### Response Example "foo" ``` -------------------------------- ### Project structure for testing with dependency injection Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/dependency-injection/testing.md Recommended project structure for separating settings and services to support testing with dependency injection. ```text your_project/ ├── config/ │ ├── settings/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── development.py │ │ └── testing.py ├── your_app/ │ ├── services/ │ │ ├── __init__.py │ │ ├── todo_service.py │ │ └── mock_todo_service.py │ └── modules.py ``` -------------------------------- ### GET /throttled-controller/endpoint_3 Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/throttling.md Example of a controller endpoint inheriting the controller-level throttle. ```APIDOC ## [GET] /throttled-controller/endpoint_3 ### Description Endpoint that uses the controller-level throttle (sustained). ### Method GET ### Endpoint /throttled-controller/endpoint_3 ### Parameters #### Query Parameters - **throttle** (DynamicRateThrottle) - Required - Throttle scope set to 'sustained' (inherited from controller) ### Request Example No request body required. ### Response #### Success Response (200) - **body** (string) - Returns 'foo' #### Response Example "foo" ``` -------------------------------- ### GET /throttled-controller/endpoint_2 Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/throttling.md Example of a controller endpoint inheriting the controller-level throttle. ```APIDOC ## [GET] /throttled-controller/endpoint_2 ### Description Endpoint that uses the controller-level throttle (sustained). ### Method GET ### Endpoint /throttled-controller/endpoint_2 ### Parameters #### Query Parameters - **throttle** (DynamicRateThrottle) - Required - Throttle scope set to 'sustained' (inherited from controller) ### Request Example No request body required. ### Response #### Success Response (200) - **body** (string) - Returns 'foo' #### Response Example "foo" ``` -------------------------------- ### Pagination with Filtering using PageNumberPaginationExtra Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller/02_model_configuration.md Combines pagination with a FilterSchema to add filtering to the list endpoint. The example shows the resulting GET request with filter and pagination parameters. ```python from typing import Optional from ninja import FilterSchema from ninja_extra import ModelConfig, ModelPagination from ninja_extra.pagination import PageNumberPaginationExtra # Define a FilterSchema for your model class EventFilterSchema(FilterSchema): title: Optional[str] = None category: Optional[str] = None start_date__gte: Optional[str] = None @api_controller("/events") class EventModelController(ModelControllerBase): model_config = ModelConfig( model=Event, pagination=ModelPagination( klass=PageNumberPaginationExtra, filter_schema=EventFilterSchema, # Add filtering support paginator_kwargs={"page_size": 25} ) ) ``` ```http GET /api/events/?title=Conference&category=Tech&page=1&page_size=25 ``` -------------------------------- ### Test async route with TestAsyncClient Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/testing.md Use TestAsyncClient to test an asynchronous route function. The example defines an async controller with a GET route and tests it by passing query parameters. ```python from ninja_extra import api_controller, route from ninja_extra.testing import TestAsyncClient @api_controller('', tags=['Math']) class MyMathController: @route.get('/add',) async def add(self, a: int, b: int): """add a to b""" return {"result": a - b} class TestMyMathController: def test_get_users_async(self): client = TestAsyncClient(MyMathController) response = client.get('/add', query={"a": 3, "b": 5}) assert response.status_code == 200 assert response.json() == {"result": -2} ``` -------------------------------- ### Development settings (development.py) with real TodoModule Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/dependency-injection/testing.md Development settings file that uses the real TodoModule for dependency injection. ```python from .base import * NINJA_EXTRA = { 'INJECTOR_MODULES': [ 'your_app.modules.TodoModule' # Uses real implementation ] } ``` -------------------------------- ### Create a new Django project Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/index.md Creates a new Django project named 'myproject'. ```bash django-admin startproject myproject ``` -------------------------------- ### GET /users Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/index.md Handles GET request to the /users endpoint. Returns a list of users. ```APIDOC ## GET /users ### Description Handles GET request to the /users endpoint. Returns a list of users. ### Method GET ### Endpoint /users ### Response #### Success Response (200) - **response** (list of UserSchema) - List of users. #### Response Example ```json [ { "username": "string", "email": "string", "first_name": "string" } ] ``` ``` -------------------------------- ### Register the API (NinjaExtraAPI) Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/dependency-injection/index.md Creates a NinjaExtraAPI instance and registers the TodoController. ```python from ninja_extra import NinjaExtraAPI api = NinjaExtraAPI() api.register_controllers(TodoController) ``` -------------------------------- ### Create a Controller with ControllerBase and @api_controller Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/index.md Defines a controller by subclassing ControllerBase and decorating it with @api_controller('/users'). The class inherits core request handling, permission, and response utilities from ControllerBase. ```python from ninja_extra import ControllerBase, api_controller @api_controller('/users') class UserControllerBase(ControllerBase): ... ``` -------------------------------- ### Define a GET endpoint with route.get Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_route.md Defines a controller class with a GET endpoint at '/test' using the route.get decorator. The endpoint returns a JSON message. ```python from ninja_extra import route, api_controller @api_controller class MyController: @route.get('/test') def test(self): return {'message': 'test'} ``` -------------------------------- ### Async Routes Configuration Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller/02_model_configuration.md Configure async routes for a model controller. ```python @api_controller("/events") class AsyncEventModelController(ModelControllerBase): model_config = ModelConfig( model=Event, # Async-specific configurations async_routes=True, schema_config=ModelSchemaConfig( read_only_fields=["id"], depth=1 ) ) # Custom async service implementation service = AsyncEventModelService(model=Event) ``` -------------------------------- ### Apply DynamicRateThrottle to API endpoints (api.py) Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/throttling.md Shows how to use DynamicRateThrottle with different scopes on individual endpoints. The 'burst' scope is applied to GET /users and 'sustained' to GET /users/. ```python # api.py from ninja_extra import NinjaExtraAPI from ninja_extra.throttling import DynamicRateThrottle api = NinjaExtraAPI() @api.get('/users', throttle=DynamicRateThrottle(scope='burst')) def get_users(request): return 'foo' @api.get('/users/', throttle=DynamicRateThrottle(scope='sustained')) def get_user_by_id(request, id: int): return 'foo' ``` -------------------------------- ### Testing settings (testing.py) with MockTodoModule Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/dependency-injection/testing.md Testing settings file that uses the MockTodoModule to swap in mock implementations. ```python from .base import * NINJA_EXTRA = { 'INJECTOR_MODULES': [ 'your_app.modules.MockTodoModule' # Uses mock implementation ] } ``` -------------------------------- ### Define the Services (TodoRepository and TodoService) Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/dependency-injection/index.md Defines the data models, repository service, and business logic service. The repository maintains the in-memory todo list, and the service uses constructor injection with the @inject decorator. ```python from typing import List, Optional from datetime import datetime from pydantic import BaseModel from injector import inject # Data Models class TodoItem(BaseModel): id: int title: str completed: bool = False created_at: datetime # Repository Service class TodoRepository: def __init__(self): self._todos: List[TodoItem] = [] self._counter = 0 def add(self, title: str) -> TodoItem: self._counter += 1 todo = TodoItem( id=self._counter, title=title, created_at=datetime.now() ) self._todos.append(todo) return todo def get_all(self) -> List[TodoItem]: return self._todos def get_by_id(self, todo_id: int) -> Optional[TodoItem]: return next((todo for todo in self._todos if todo.id == todo_id), None) def toggle_complete(self, todo_id: int) -> Optional[TodoItem]: todo = self.get_by_id(todo_id) if todo: todo.completed = not todo.completed return todo # Business Logic Service class TodoService: @inject def __init__(self, repository: TodoRepository): self.repository = repository def create_todo(self, title: str) -> TodoItem: return self.repository.add(title) def get_todos(self) -> List[TodoItem]: return self.repository.get_all() def toggle_todo(self, todo_id: int) -> Optional[TodoItem]: return self.repository.toggle_complete(todo_id) ``` -------------------------------- ### Customize routes with allowed_routes Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller/01_getting_started.md Customize which routes are generated using the allowed_routes parameter. This example only generates list and find_one endpoints. ```python @api_controller("/events") class EventModelController(ModelControllerBase): model_config = ModelConfig( model=Event, allowed_routes=["list", "find_one"] # Only generate GET and GET/{id} endpoints ) ``` -------------------------------- ### Access route arguments Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/route_context.md Accesses route keyword arguments via the kwargs property of RouteContext. The comment shows an example output. ```python @route.get("/items/{item_id}") def get_item(self, item_id: int): # Access route parameters print(self.context.kwargs) # {'item_id': 123} return {"item_id": item_id} ``` -------------------------------- ### Using Dependency Injection with Async Permissions Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_async_permission.md Shows how to create an async permission class that uses dependency injection via the injector library. The FeaturePermission class injects a FeatureService and defines an async has_permission_async method. The FeatureController demonstrates using the permission with the create_as classmethod and combining permissions with the & operator. ```python from injector import inject from ninja_extra import api_controller, http_get, service_resolver from ninja_extra.permissions import AsyncBasePermission class FeatureService: def has_feature_access(self, user, feature): # Check if user has access to a specific feature return getattr(user, f'has_{feature}', False) class FeaturePermission(AsyncBasePermission): __features__ = {} feature: str = "basic" @inject def __init__(self, feature_service: FeatureService): self.feature_service = feature_service self.message = f"Must have access to {self.feature} feature" # Async version of permission check async def has_permission_async(self, request, controller): return self.feature_service.has_feature_access(request.user, self.feature) @classmethod def create_as(cls, feature: str) -> Type[FeaturePermission]: # Create a new permission class with the same attributes if feature in cls.__features__: return cls.__features__[feature] permission_type = type(f"{cls.__name__}_{feature}", (cls,), {"feature": feature}) cls.__features__[feature] = permission_type return permission_type @api_controller('features') class FeatureController(ControllerBase): @http_get('basic/', permissions=[FeaturePermission.create_as("basic")]) async def basic_feature(self): return {"feature": "basic"} @http_get('premium/', permissions=[FeaturePermission.create_as("premium")]) async def premium_feature(self): return {"feature": "premium"} # You can even combine injected permissions with operators @http_get('both/', permissions=[FeaturePermission.create_as("basic") & FeaturePermission.create_as("premium")]) async def both_features(self): return {"feature": "both"} ``` -------------------------------- ### Basic Route Context Usage with IsOwner Permission Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/api_controller_permission.md Shows how to use controller.context.compute_route_parameters() to access path parameters like user_id in a permission class. The permission checks if the request user's ID matches the user_id path parameter. ```python from ninja_extra import permissions, api_controller, http_get, ControllerBase from django.http import HttpRequest class IsOwner(permissions.BasePermission): def has_permission(self, request: HttpRequest, controller: ControllerBase): # Access route context and compute parameters controller.context.compute_route_parameters() # Now you can access path and query parameters user_id = controller.context.kwargs.get('user_id') return request.user.id == user_id @api_controller("/users") class UserController: @http_get("/{user_id}/profile", permissions=[IsOwner()]) def get_user_profile(self, user_id: int): return {"message": f"Access granted to profile {user_id}"} ``` -------------------------------- ### Add custom endpoint to Model Controller Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller.md Extends the read-only EventModelController with an additional GET /subtract endpoint that subtracts a from b and returns the result. ```python from ninja_extra import api_controller, http_get, ModelControllerBase, ModelConfig, ModelSchemaConfig from .models import Event @api_controller("/events") class EventModelController(ModelControllerBase): model_config = ModelConfig( model=Event, allowed_routes=['find_one', 'list'], schema_config=ModelSchemaConfig(read_only_fields=["id", "category"]), ) @http_get('/subtract',) def subtract(self, a: int, b: int): """Subtracts a from b""" return {"result": a - b} ``` -------------------------------- ### Advanced ModelControllerBase configuration with route_info options Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller/02_model_configuration.md Use this snippet to configure a model controller with custom route info for create, update, and delete operations. It sets permissions, throttling, status codes, and OpenAPI extras. Requires an Event model and imports from ninja_extra. ```python from ninja_extra import status from ninja_extra.permissions import IsAuthenticated, IsAdminUser from ninja_extra.throttling import AnonRateThrottle @api_controller("/events") class EventModelController(ModelControllerBase): model_config = ModelConfig( model=Event, create_route_info={ "summary": "Create a new event", "description": "Creates a new event with the provided data", "tags": ["events", "management"], "status_code": status.HTTP_201_CREATED, "permissions": [IsAuthenticated], "throttle": AnonRateThrottle(), "exclude_none": True, "openapi_extra": { "requestBody": { "content": { "application/json": { "examples": { "example1": { "summary": "Conference event", "value": { "title": "Tech Conference 2024", "start_date": "2024-06-01", "end_date": "2024-06-03" } } } } } } } }, update_route_info={ "summary": "Update event", "permissions": [IsAuthenticated, IsAdminUser], "exclude_unset": True, }, delete_route_info={ "summary": "Delete an event", "permissions": [IsAdminUser], "status_code": status.HTTP_204_NO_CONTENT, } ) ``` -------------------------------- ### Example JSON request body without optional description Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/tutorial/body_request.md Shows a valid JSON object for the Item model when the optional description is omitted. ```JSON { "name": "Katana", "price": 299.00, "quantity": 10 } ``` -------------------------------- ### Enable async routes with async_routes=True Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller.md Controller with async_routes=True in model_config to enable asynchronous endpoints. Dynamically switches between ModelAsyncEndpointFactory and ModelEndpointFactory. ```python from ninja_extra import ( ModelConfig, ModelControllerBase, ModelSchemaConfig, api_controller, ) from .service import EventModelService from .models import Event @api_controller("/events") class EventModelController(ModelControllerBase): service = EventModelService(model=Event) model_config = ModelConfig( model=Event, async_routes=True, schema_config=ModelSchemaConfig(read_only_fields=["id", "category"]), ) ``` -------------------------------- ### Define Django models for Event and Category Source: https://github.com/eadwincode/django-ninja-extra/blob/master/docs/api_controller/model_controller.md Defines the Category and Event models used in the example. The Event model has a OneToOneField to Category and date fields. ```python from django.db import models class Category(models.Model): title = models.CharField(max_length=100) class Event(models.Model): title = models.CharField(max_length=100) category = models.OneToOneField( Category, null=True, blank=True, on_delete=models.SET_NULL, related_name='events' ) start_date = models.DateField() end_date = models.DateField() def __str__(self): return self.title ```