### Start New Project with Django Shinobi Source: https://github.com/pmdevita/django-shinobi/blob/master/README.md Install Django and Django Shinobi, then start a new Django project named 'apidemo'. ```shell pip install django django-shinobi django-admin startproject apidemo ``` -------------------------------- ### Install Flit Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Install Flit, the build and packaging tool used by Django Shinobi. ```bash pip install flit ``` -------------------------------- ### Install Development Dependencies Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Install all project dependencies for development and testing using Flit. ```bash flit install --deps develop --symlink ``` -------------------------------- ### Install Django Shinobi Source: https://github.com/pmdevita/django-shinobi/blob/master/README.md Install the django-shinobi package using pip. This is the first step to integrate it into your Django project. ```shell pip install django-shinobi ``` -------------------------------- ### Install Uvicorn ASGI Server Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/async-support.md Install Uvicorn, a popular ASGI server, to run asynchronous Django applications. ```bash pip install uvicorn ``` -------------------------------- ### Install Django and Django Ninja Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/async-support.md Install Django version 3.1 or higher along with django-shinobi using pip. ```bash pip install Django>=3.1 django-shinobi ``` -------------------------------- ### Install and Configure ORJSON Parser Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/request-parsers.md Install the `orjson` library and create a custom parser by inheriting from `ninja.parser.Parser`. Implement `parse_body` to use `orjson.loads` for efficient JSON parsing. ```python import orjson from ninja import NinjaAPI from ninja.parser import Parser class ORJSONParser(Parser): def parse_body(self, request): return orjson.loads(request.body) api = NinjaAPI(parser=ORJSONParser()) ``` -------------------------------- ### Install Git Hooks for Style Checks Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Install pre-commit git hooks to automatically format and check code styles before committing. ```bash pre-commit install ``` -------------------------------- ### Start a New Django Project Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/tutorial/index.md Use the Django admin command to create a new Django project. This command initializes the basic structure for a Django application. ```bash django-admin startproject myproject ``` -------------------------------- ### Consolidated Initialization in Class Constructor Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/proposals/cbv.md Highlights the constructor of the `Tasks` class, demonstrating how common setup logic, including fetching the project and its tasks with permission checks, is centralized. ```python @router.path('/project/{project_id}/tasks') class Tasks: def __init__(self, request, project_id=int): user_projects = request.user.project_set self.project = get_object_or_404(user_projects, id=project_id)) self.tasks = self.project.task_set.all() ``` -------------------------------- ### Custom Throttle Implementation Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/throttling.md Create a custom throttle by overriding BaseThrottle and implementing the `allow_request` method. This example prevents throttling GET requests. ```Python from ninja.throttling import AnonRateThrottle class NoReadsThrottle(AnonRateThrottle): """Do not throttle GET requests""" def allow_request(self, request): if request.method == "GET": return True return super().allow_request(request) ``` -------------------------------- ### Run Django Development Server with Uvicorn Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/async-support.md Start the Uvicorn ASGI server for your Django project. Use the `--reload` flag for development. ```bash uvicorn your_project.asgi:application --reload ``` -------------------------------- ### Define a Simple API for Testing Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/testing.md This code defines a basic Django Ninja API with a GET endpoint '/hello' that returns a JSON response. It's used as an example for testing. ```python from ninja import NinjaAPI, Schema api = NinjaAPI() router = Router() class HelloResponse(Schema): msg: str @router.get("/hello", response=HelloResponse) def hello(request): return {"msg": "Hello World"} api.add_router("", router) ``` -------------------------------- ### Run Django Development Server Source: https://github.com/pmdevita/django-shinobi/blob/master/README.md Start the Django development server to make your API accessible locally. This command is used to test your API and view documentation. ```shell python manage.py runserver ``` -------------------------------- ### Returning Querysets Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Demonstrates how to return a Django ORM queryset directly, which will be automatically evaluated to a list. Includes an example for asynchronous operations. ```APIDOC ## GET /tasks ### Description Returns a list of tasks. ### Method GET ### Endpoint /tasks ### Response #### Success Response (200) - **tasks** (List[TaskSchema]) - A list of task objects. ### Request Example ```python @api.get("/tasks", response=List[TaskSchema]) def tasks(request): return Task.objects.all() ``` ### Async Request Example ```python @api.get("/tasks", response=List[TaskSchema]) async def tasks(request): return Task.objects.all() ``` ``` -------------------------------- ### Build and Run Environment Matrix Source: https://github.com/pmdevita/django-shinobi/blob/master/tests/env-matrix/README.md Use this command to build the Docker images and run tests across all configured environments. The first run may take longer due to image downloads and installations. ```bash docker-compose up --build ``` -------------------------------- ### Synchronous API View Example Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/async-support.md A basic synchronous API view that simulates a delay before returning a response. This can be inefficient for I/O-bound tasks. ```python import time @api.get("/say-after") def say_after(request, delay: int, word: str): time.sleep(delay) return {"saying": word} ``` -------------------------------- ### Define a Simple GET Endpoint Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/tutorial/index.md Create a basic GET endpoint using the @api.get decorator. This function handles requests to the /hello path and returns a string. ```python from ninja import NinjaAPI api = NinjaAPI() @api.get("/hello") def hello(request): return "Hello world" ``` -------------------------------- ### FastAPI Dependency Injection Example Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/motivation.md Illustrates dependency injection for database sessions and authentication in FastAPI. Note the potential for closed connection issues with Django ORM in async mode. ```python from fastapi import FastAPI, Depends, HTTPException from sqlalchemy.orm import Session # Assuming SessionLocal, Task, User, decode, oauth2_scheme are defined elsewhere app = FastAPI() # Dependency def get_db(): db = SessionLocal() try: yield db finally: db.close() async def get_current_user(token: str = Depends(oauth2_scheme)): user = decode(token) if not user: raise HTTPException(...) return user @app.get("/task/{task_id}", response_model=Task) def read_user( task_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): ... use db with current_user .... ``` -------------------------------- ### Asynchronous API View Example Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/async-support.md Converts the synchronous view to an asynchronous one by using `async` and `await` with `asyncio.sleep`. This is suitable for network and I/O-bound operations. ```python import asyncio @api.get("/say-after") async def say_after(request, delay: int, word: str): await asyncio.sleep(delay) return {"saying": word} ``` -------------------------------- ### Example JSON Response for Tasks Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Illustrates the JSON structure of a response from the /tasks endpoint, showing tasks with nested owner details or null owner. ```json [ { "id": 1, "title": "Task 1", "is_completed": false, "owner": { "id": 1, "first_name": "John", "last_name": "Doe" } }, { "id": 2, "title": "Task 2", "is_completed": false, "owner": null }, ] ``` -------------------------------- ### Using Async Elasticsearch Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/async-support.md Integrate with Elasticsearch asynchronously by using the `AsyncElasticsearch` client and `await`ing its methods. Ensure you have installed the `elasticsearch` library. ```python pip install elasticsearch>=7.8.0 ``` ```python from ninja import NinjaAPI from elasticsearch import AsyncElasticsearch api = NinjaAPI() es = AsyncElasticsearch() @api.get("/search") async def search(request, q: str): resp = await es.search( index="documents", body={"query": {"query_string": {"query": q}}}, size=20, ) return resp["hits"] ``` -------------------------------- ### Handling Multiple Input Types and Type Hinting Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/tutorial/step2.md Utilize Python type hints to automatically parse input types for query string arguments. This example shows string and integer type hints. ```python @api.get("/hello") def hello(request, name: str = "world"): return f"Hello {name}" @api.get("/math") def math(request, a: int, b: int): return {"add": a + b, "multiply": a * b} ``` -------------------------------- ### Required and Optional Query Parameters Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/query-params.md Declare required query parameters by simply including them, and optional ones by providing a default value. This example shows a required 'q' parameter and an optional 'offset' parameter. ```python @api.get("/items") def list_items(request, q, offset: int = 0): return items[offset:] ``` -------------------------------- ### Multiple Response Codes Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md This example demonstrates how to specify multiple possible response codes for a single API endpoint, avoiding schema duplication. ```APIDOC ## POST /login ### Description Handles user login with different response codes for authentication success, invalid credentials, and insufficient balance. ### Method POST ### Endpoint /login ### Parameters #### Request Body - **payload** (Auth) - Required - Authentication credentials. ### Response #### Success Response (200) - **token** (string) - The authentication token. #### Error Response (401) - **message** (string) - "Unauthorized" #### Error Response (402) - **message** (string) - "Insufficient balance amount. Please proceed to a payment page." ### Request Example ```json { "username": "user", "password": "password" } ``` ### Response Example #### 200 OK ```json { "token": "your_auth_token" } ``` #### 401 Unauthorized ```json { "message": "Unauthorized" } ``` #### 402 Payment Required ```json { "message": "Insufficient balance amount. Please proceed to a payment page." } ``` ``` -------------------------------- ### Add Django Ninja to INSTALLED_APPS Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/reference/management-commands.md Ensure Django Ninja is installed by adding 'ninja' to your Django project's INSTALLED_APPS setting. ```python INSTALLED_APPS = [ ... 'ninja', ] ``` -------------------------------- ### Apply paginate decorator Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/pagination.md Apply the `paginate` decorator to a function to enable pagination. This allows querying with `limit` and `offset` GET parameters. ```python from ninja.pagination import paginate @api.get('/users', response=List[UserSchema]) @paginate def list_users(request): return User.objects.all() ``` -------------------------------- ### Nested URL Parameters with Path Parameters Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/routers.md Utilize URL parameters in nested routers by defining parameters with `Path(...)`. This example demonstrates how path parameters are passed down through nested router calls. ```python from django.contrib import admin from django.urls import path from ninja import NinjaAPI, Path, Router api = NinjaAPI() router = Router() @api.get("/add/{a}/{b}") def add(request, a: int, b: int): return {"result": a + b} @router.get("/multiply/{c}") def multiply(request, c: int, a: int = Path(...), b: int = Path(...)): return {"result": (a + b) * c} api.add_router("add/{a}/{b}", router) urlpatterns = [ path("admin/", admin.site.urls), path("api/", api.urls), ] ``` -------------------------------- ### Define Django Model with CharField Choices Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/differences.md Example of a Django model using TextChoices for a CharField. Ninja maps this as a string, while Shinobi detects and uses the enum. ```python class NumberEnum(TextChoices): ONE = "ONE", "One" TWO = "TWO", "Two" THREE = "THREE", "Three" class MyModel(models.Model): number = models.CharField(max_length=10, choices=NumberEnum.choices) ``` -------------------------------- ### Example API Operations with Repeated Permission Checks Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/proposals/cbv.md Demonstrates a common pattern in Django Ninja where permission checks are repeated across multiple API operations for accessing project-specific tasks. This leads to code duplication. ```python router = Router() @router.get('/project/{project_id}/tasks/', response=List[TaskOut]) def task_list(request): user_projects = request.user.project_set project = get_object_or_404(user_projects, id=project_id)) return project.task_set.all() @router.get('/project/{project_id}/tasks/{task_id}/', response=TaskOut) def details(request, task_id: int): user_projects = request.user.project_set project = get_object_or_404(user_projects, id=project_id)) user_tasks = project.task_set.all() return get_object_or_404(user_tasks, id=task_id) @router.post('/project/{project_id}/tasks/{task_id}/complete', response=TaskOut) def complete(request, task_id: int): user_projects = request.user.project_set project = get_object_or_404(user_projects, id=project_id)) user_tasks = project.task_set.all() task = get_object_or_404(user_tasks, id=task_id) task.completed = True task.save() return task ``` -------------------------------- ### Custom Pagination Class with Skip Parameter Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/pagination.md Create a custom pagination class by subclassing `PaginationBase`. Define `Input` and `Output` schemas and override `paginate_queryset`. This example paginates using a 'skip' parameter with a default of 5 items per page. ```python from ninja.pagination import paginate, PaginationBase from ninja import Schema from typing import List, Any class CustomPagination(PaginationBase): # only `skip` param, defaults to 5 per page class Input(Schema): skip: int class Output(Schema): items: List[Any] # `items` is a default attribute total: int per_page: int def paginate_queryset(self, queryset, pagination: Input, **params): skip = pagination.skip return { 'items': queryset[skip : skip + 5], 'total': queryset.count(), 'per_page': 5, } @api.get('/users', response=List[UserSchema]) @paginate(CustomPagination) def list_users(request): return User.objects.all() ``` -------------------------------- ### Test API Endpoint with Django Test Client Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/testing.md Demonstrates how to use Django Ninja's TestClient to make a GET request to an API endpoint and assert the response status code and JSON body. ```python from django.test import TestCase from ninja.testing import TestClient class HelloTest(TestCase): def test_hello(self): # don't forget to import router from code above client = TestClient(router) response = client.get("/hello") self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), {"msg": "Hello World"}) ``` -------------------------------- ### Serve Documentation Locally Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Build and serve the project documentation locally using mkdocs to preview changes in real-time. ```bash cd docs/ mkdocs serve ``` -------------------------------- ### Switch to Redoc Documentation Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/api-docs.md To use Redoc for API documentation, instantiate the Redoc class and pass it to the NinjaAPI constructor. ```python from ninja import Redoc api = NinjaAPI(docs=Redoc()) ``` -------------------------------- ### Implement Nested Routers in Django Ninja Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/routers.md Nest routers within each other to further organize API logic. This example shows a three-level nesting structure, starting from the main `api` instance down to `third_router`. ```python from django.contrib import admin from django.urls import path from ninja import NinjaAPI, Router api = NinjaAPI() first_router = Router() second_router = Router() third_router = Router() @api.get("/add") def add(request, a: int, b: int): return {"result": a + b} @first_router.get("/add") def add(request, a: int, b: int): return {"result": a + b} @second_router.get("/add") def add(request, a: int, b: int): return {"result": a + b} @third_router.get("/add") def add(request, a: int, b: int): return {"result": a + b} second_router.add_router("l3", third_router) first_router.add_router("l2", second_router) api.add_router("l1", first_router) urlpatterns = [ path("admin/", admin.site.urls), path("api/", api.urls), ] ``` -------------------------------- ### Define API Endpoint in api.py Source: https://github.com/pmdevita/django-shinobi/blob/master/README.md Create an 'api.py' file in your Django project to define API endpoints using NinjaAPI. This example shows a simple GET endpoint for adding two integers. ```python from ninja import NinjaAPI api = NinjaAPI() @api.get("/add") def add(request, a: int, b: int): return {"result": a + b} ``` -------------------------------- ### Initialize Redoc with CDN Source: https://github.com/pmdevita/django-shinobi/blob/master/ninja/templates/ninja/redoc_cdn.html This JavaScript code snippet initializes Redoc by fetching configuration from the DOM, parsing it, and then calling Redoc.init with the OpenAPI JSON URL, configuration object, and the target DOM element. ```javascript const configJson = document.getElementById("redoc-settings").textContent; const configObject = JSON.parse(configJson); const element = document.getElementById('redoc-ui'); Redoc.init('{{ openapi_json_url }}', configObject, element); ``` -------------------------------- ### Run Formatting with Makefile Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Reformat code using the Makefile shortcut. ```bash make fmt ``` -------------------------------- ### Get Single Employee by ID Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/tutorial/other/crud.md Retrieve a single employee by their ID using a GET request. The response is validated against the EmployeeOut schema. ```python @api.get("/employees/{employee_id}", response=EmployeeOut) def get_employee(request, employee_id: int): employee = get_object_or_404(Employee, id=employee_id) return employee ``` ```python @api.get("/employees/{employee_id}", response=EmployeeOut) def get_employee(request, employee_id: int): employee = get_object_or_404(Employee, id=employee_id) return employee ``` -------------------------------- ### Initialize Main API Instance Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/routers.md Create the main NinjaAPI instance in a top-level api.py file. This instance will be used to aggregate all the routers from different applications. ```python from ninja import NinjaAPI api = NinjaAPI() ``` -------------------------------- ### Run Tests with Makefile Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Execute all project tests using the Makefile shortcut. ```bash make test ``` -------------------------------- ### Specify Headers for Test Client Request Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/testing.md Demonstrates how to set request headers, both globally when instantiating the TestClient and individually for a specific request. ```python client = TestClient(router, headers={"A": "a", "B": "b"}) # The request will be made with {"A": "na", "B": "b", "C": "nc"} headers response = client.get("/test-headers", headers={"A": "na", "C": "nc"}) ``` -------------------------------- ### Configure Redoc Settings Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/api-docs.md Custom settings for Redoc can be provided using the 'settings' parameter when initializing the Redoc docs class. ```python from ninja import Redoc api = NinjaAPI(docs=Redoc(settings={"disableSearch": True})) ``` -------------------------------- ### Return QuerySet Directly (Async) Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md For async operations, ensure ORM queries are called safely. Refer to the async support guide for details. ```python @api.get("/tasks", response=List[TaskSchema]) async def tasks(request): return Task.objects.all() ``` -------------------------------- ### Run Linting with Makefile Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Execute code style checks using the Makefile shortcut. ```bash make lint ``` -------------------------------- ### Create an XML Renderer Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/response-renderers.md Implement an XML renderer by using `StringIO` and `SimplerXMLGenerator`. The `render` method handles the XML generation, and a helper `_to_xml` method recursively processes data structures. ```python from io import StringIO from django.utils.encoding import force_str from django.utils.xmlutils import SimplerXMLGenerator from ninja import NinjaAPI from ninja.renderers import BaseRenderer class XMLRenderer(BaseRenderer): media_type = "text/xml" def render(self, request, data, *, response_status): stream = StringIO() xml = SimplerXMLGenerator(stream, "utf-8") xml.startDocument() xml.startElement("data", {}) self._to_xml(xml, data) xml.endElement("data") xml.endDocument() return stream.getvalue() def _to_xml(self, xml, data): if isinstance(data, (list, tuple)): for item in data: xml.startElement("item", {}) self._to_xml(xml, item) xml.endElement("item") elif isinstance(data, dict): for key, value in data.items(): xml.startElement(key, {}) self._to_xml(xml, value) xml.endElement(key) elif data is None: # Don't output any value pass else: xml.characters(force_str(data)) api = NinjaAPI(renderer=XMLRenderer()) ``` -------------------------------- ### Run Coverage Test with Makefile Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Execute tests with coverage reporting using the Makefile shortcut. ```bash make test-cov ``` -------------------------------- ### Create Custom Docs Viewer Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/api-docs.md To implement a custom documentation viewer, create a class inheriting from DocsBase and override the 'render_page' method. ```python from ninja.openapi.docs import DocsBase class MyDocsViewer(DocsBase) def render_page(self, request, api): ... # return http response ... api = NinjaAPI(docs=MyDocsViewer()) ``` -------------------------------- ### API Key Authentication in Query Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/authentication.md Authenticates requests by checking for an API key in the query string. The `param_name` argument specifies the GET parameter name; defaults to 'key'. ```python from ninja import NinjaAPI from ninja.security import APIKeyHeader class GlobalAPIKey(APIKeyHeader): param_name = "api_key" api = NinjaAPI(auth=GlobalAPIKey()) @api.get("/items/") def items(request): return [{"id": 1, "name": "Foo"}] ``` -------------------------------- ### Defining Operations with Decorators Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/operations.md Django Ninja provides specific decorators for each HTTP method (GET, POST, PUT, DELETE, PATCH) to define operations for a given path. ```APIDOC ## GET Operation ### Description Defines a GET operation for a specific path. ### Method GET ### Endpoint /path ## POST Operation ### Description Defines a POST operation for a specific path. ### Method POST ### Endpoint /path ## PUT Operation ### Description Defines a PUT operation for a specific path. ### Method PUT ### Endpoint /path ## DELETE Operation ### Description Defines a DELETE operation for a specific path. ### Method DELETE ### Endpoint /path ## PATCH Operation ### Description Defines a PATCH operation for a specific path. ### Method PATCH ### Endpoint /path ``` -------------------------------- ### Applying Pagination to Main API Instance Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/pagination.md To apply pagination to the main `api` instance, use the `default_router` argument when initializing `NinjaAPI` with `RouterPaginated`. ```python from ninja import NinjaAPI from ninja.pagination import RouterPaginated api = NinjaAPI(default_router=RouterPaginated()) @api.get(...) ``` -------------------------------- ### Query Parameters Without Type Annotations Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/query-params.md If query parameters are not annotated with types, they are treated as strings. This example shows how to access them directly, with comments indicating their string type. ```python @api.get("/weapons") def list_weapons(request, limit, offset): # type(limit) == str # type(offset) == str ``` -------------------------------- ### Run Tests with Coverage Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Run tests and check code coverage for the 'ninja' module. ```bash pytest --cov=ninja --cov-report term-missing tests ``` -------------------------------- ### Define News Operations with Router Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/routers.md Similar to events, define API operations for news using the Router class in news/api.py. This example shows the basic structure for list and detail endpoints. ```python from ninja import Router from .models import News router = Router() @router.get('/') def list_news(request): ... @router.get('/{news_id}') def news_details(request, news_id: int): ... ``` -------------------------------- ### Nested schema for related model Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/django-pydantic-create-schema.md When `depth` is used, related fields like ManyToMany are introspected, creating schemas for the related models as well. This example shows the generated schema for the `Group` model. ```python class Group(Schema): id: int name: str permissions: List[int] ``` -------------------------------- ### Define API with Different Business Logic and URL Namespaces Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/versioning.md Create separate NinjaAPI instances for different components or areas, specifying unique URL namespaces. ```python ... api = NinjaAPI(auth=token_auth, urls_namespace='public_api') ... api_private = NinjaAPI(auth=session_auth, urls_namespace='private_api') ... urlpatterns = [ ... path('api/', api.urls), path('internal-api/', api_private.urls), ] ``` -------------------------------- ### Customize URL Namespace Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/urls.md Demonstrates how to explicitly set a custom URL namespace for `NinjaAPI` instances. ```python api = NinjaAPI(auth=token_auth, version='2') api_private = NinjaAPI(auth=session_auth, urls_namespace='private_api') api_users_url = reverse_lazy("api-2:users") private_api_admins_url = reverse_lazy("private_api:admins") ``` -------------------------------- ### Using Native Async ORM Operations (Django 4.1+) Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/async-support.md For Django 4.1 and later, use the native asynchronous ORM operations (e.g., `aget` for getting a single object). This eliminates the need for `sync_to_async`. ```python @api.get("/blog/{post_id}") async def search(request, post_id: int): blog = await Blog.objects.aget(pk=post_id) ... ``` -------------------------------- ### Define Input Schema for User Creation Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Define the input schema for user creation, including username and password. ```python from ninja import Schema class UserIn(Schema): username: str password: str ``` -------------------------------- ### Reformat Code with Ruff Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Reformat code in the 'ninja' and 'tests' directories using ruff, and fix style issues. ```bash ruff format ninja tests ``` ```bash ruff check ninja tests --fix ``` -------------------------------- ### Create Employee with File Upload Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/tutorial/other/crud.md Implement a POST endpoint to create a new employee, including handling a file upload for the CV field. The file is saved using the model's save method. ```python from ninja import UploadedFile, File @api.post("/employees") def create_employee(request, payload: EmployeeIn, cv: File[UploadedFile]): payload_dict = payload.dict() employee = Employee(**payload_dict) employee.cv.save(cv.name, cv) # will save model instance as well return {"id": employee.id} ``` -------------------------------- ### Django Ninja Multiple API Versions Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/motivation.md Demonstrates how to set up and route multiple API versions within a single Django project using Django Ninja. This allows for managing different API iterations concurrently. ```python from ninja import NinjaAPI from django.urls import path # Assuming token_auth, session_auth are defined elsewhere api_v1 = NinjaAPI(version='1.0', auth=token_auth) ... api_v2 = NinjaAPI(version='2.0', auth=token_auth) ... api_private = NinjaAPI(auth=session_auth, urls_namespace='private_api') ... urlpatterns = [ ... path('api/v1/', api_v1.urls), path('api/v2/', api_v2.urls), path('internal-api/', api_private.urls), ] ``` -------------------------------- ### Create a Custom Text Renderer Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/response-renderers.md Inherit from `BaseRenderer` and override `render` to define a custom media type. Set the `media_type` attribute to 'text/plain'. ```python from ninja import NinjaAPI from ninja.renderers import BaseRenderer class MyRenderer(BaseRenderer): media_type = "text/plain" def render(self, request, data, *, response_status): return ... # your serialization here api = NinjaAPI(renderer=MyRenderer()) ``` -------------------------------- ### Define Event Operations with Router Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/routers.md Use the Router class instead of NinjaAPI to define API operations within an application's api.py file. This snippet shows how to define GET endpoints for listing events and retrieving event details. ```python from ninja import Router from .models import Event router = Router() @router.get('/') def list_events(request): return [ {"id": e.id, "title": e.title} for e in Event.objects.all() ] @router.get('/{event_id}') def event_details(request, event_id: int): event = Event.objects.get(id=event_id) return {"title": event.title, "details": event.details} ``` -------------------------------- ### Configure URL Routing for Multiple API Versions Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/versioning.md In urls.py, map different URL paths to the respective API version instances. ```python ... from api_v1 import api as api_v1 from api_v2 import api as api_v2 urlpatterns = [ ... path('api/v1/', api_v1.urls), path('api/v2/', api_v2.urls), ] ``` -------------------------------- ### Multiple Type Conversions for Query Parameters Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/query-params.md Demonstrates how different types like string, boolean, and date are handled. Booleans accept various truthy string representations, and dates can be Unix timestamps or date strings. ```python @api.get("/example") def example(request, b: bool, d: date): return {"b": b, "d": d} ``` -------------------------------- ### Specify OpenAPI Servers Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/reference/operations-parameters.md Configure the `servers` parameter during NinjaAPI initialization to define single or multiple server URLs for your OpenAPI specification. This allows for environment switching in interactive documentation. ```python from ninja import NinjaAPI api = NinjaAPI( servers=[ {"url": "https://stag.example.com", "description": "Staging env"}, {"url": "https://prod.example.com", "description": "Production env"}, ] ) ``` -------------------------------- ### Create Employee with File Upload Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/tutorial/other/crud.md Handles employee creation including a file upload for the CV. ```APIDOC ## POST /employees (with file upload) ### Description Creates a new employee record and saves an uploaded CV file. ### Method POST ### Endpoint /employees ### Parameters #### Request Body - **first_name** (str) - Required - The first name of the employee. - **last_name** (str) - Required - The last name of the employee. - **department_id** (int) - Optional - The ID of the department the employee belongs to. - **birthdate** (date) - Optional - The birthdate of the employee. - **cv** (File[UploadedFile]) - Required - The CV file to upload. ### Response #### Success Response (200) - **id** (int) - The ID of the newly created employee. ``` -------------------------------- ### Configure Swagger UI Settings Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/api-docs.md Custom settings for Swagger UI can be provided using the 'settings' parameter when initializing the Swagger docs class. ```python from ninja import Swagger api = NinjaAPI(docs=Swagger(settings={"persistAuthorization": True})) ``` -------------------------------- ### Create User API Endpoint with Response Schema Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Create a user API endpoint that accepts a UserIn schema and returns a UserOut schema, limiting output to id and username. ```python from ninja import Schema class UserIn(Schema): username: str password: str class UserOut(Schema): id: int username: str @api.post("/users/", response=UserOut) def create_user(request, data: UserIn): user = User(username=data.username) # User is django auth.User user.set_password(data.password) user.save() return user ``` -------------------------------- ### Enable Shinobi Schema Performance Improvements Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/differences.md Set NINJA_COMPATIBILITY to False in settings.py to enable Shinobi's schema performance improvements. This setting is True by default for backward compatibility. ```python # settings.py NINJA_COMPATIBILITY = False # True by default ``` -------------------------------- ### LimitOffsetPagination (default) Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/pagination.md Use `LimitOffsetPagination` for pagination, which is the default. It accepts `limit` and `offset` query parameters. ```python from ninja.pagination import paginate, LimitOffsetPagination @api.get('/users', response=List[UserSchema]) @paginate(LimitOffsetPagination) def list_users(request): return User.objects.all() ``` -------------------------------- ### Apply Authentication to All Router Operations Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/routers.md Use the `auth` argument when adding a router to apply authentication to all its operations. Alternatively, set the default authentication during router instantiation. ```python api.add_router("/events/", events_router, auth=BasicAuth()) ``` ```python router = Router(auth=BasicAuth()) ``` -------------------------------- ### Manually Check Code Style with Ruff and Mypy Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Perform manual style checks on the 'ninja' and 'tests' directories using ruff and mypy. ```bash ruff format --check ninja tests ``` ```bash ruff check ninja tests ``` ```bash mypy ninja ``` -------------------------------- ### Return Django Redirect Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Use redirect to send users to a different URL. Ensure necessary imports are included. ```python from django.http import HttpResponse from django.shortcuts import redirect @api.get("/something") def some_redirect(request): return redirect("/some-path") # !!!! ``` -------------------------------- ### Creating a User with a Specific Output Schema Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Demonstrates how to define input and output schemas for an API operation. The output schema (`UserOut`) limits the returned fields, excluding sensitive information like the password. ```APIDOC ## POST /users/ ### Description Creates a new user with the provided username and password. The response will only include the user's ID and username, excluding the password. ### Method POST ### Endpoint /users/ ### Parameters #### Request Body - **username** (str) - Required - The username for the new user. - **password** (str) - Required - The password for the new user. ### Response #### Success Response (200) - **id** (int) - The unique identifier of the created user. - **username** (str) - The username of the created user. ### Response Example ```json { "id": 1, "username": "john_doe" } ``` ``` -------------------------------- ### Accepting Input from Path Parameters Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/tutorial/step2.md Define API endpoints that accept parameters directly from the URL path using Python format-string syntax. These path parameters are passed as function arguments. ```python @api.get("/math/{a}and{b}") def math(request, a: int, b: int): return {"add": a + b, "multiply": a * b} ``` -------------------------------- ### FileField and ImageField Handling Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Explains how Django Ninja handles `FileField` and `ImageField` by default, converting them to string URLs, and shows how to define schemas for them. ```APIDOC ## Handling File and Image Fields ### Description This section details how `FileField` and `ImageField` are processed, typically converted to string URLs in responses. It also shows how to define a schema to represent these fields. ### Schema Definition Example ```python class PictureSchema(Schema): title: str image: str ``` ### Response Example ```json { "title": "Zebra", "image": "/static/images/zebra.jpg" } ``` ``` -------------------------------- ### Default Expression Combination (OR for fields, AND for schema) Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/filtering.md Demonstrates the default behavior where field-level expressions are joined by OR, and schema fields by AND. ```python class BookFilterSchema(FilterSchema): search: Optional[str] = Field(None, q=['name__icontains', 'author__name__icontains']) popular: Optional[bool] = None ``` -------------------------------- ### Create an ORJSON Renderer Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/response-renderers.md Utilize the `orjson` library for fast and accurate JSON serialization. Set the `media_type` to 'application/json' and use `orjson.dumps` in the `render` method. ```python import orjson from ninja import NinjaAPI from ninja.renderers import BaseRenderer class ORJSONRenderer(BaseRenderer): media_type = "application/json" def render(self, request, data, *, response_status): return orjson.dumps(data) api = NinjaAPI(renderer=ORJSONRenderer()) ``` -------------------------------- ### Provide Multi-line Operation Description with Docstrings Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/reference/operations-parameters.md For longer descriptions, use Python docstrings within the operation function. This is useful for detailing required parameters and structure. ```python @api.post("/orders/") def create_order(request, order: Order): """ To create an order please provide: - **first_name** - **last_name** - and **list of Items** *(product + amount)* """ return {"success": True} ``` -------------------------------- ### Test API Concurrency with ApacheBench Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/async-support.md Use ApacheBench (ab) to send 100 concurrent requests to the asynchronous API endpoint to test its performance under load. ```bash ab -c 100 -n 100 "http://127.0.0.1:8000/api/say-after?delay=3&word=hello" ``` -------------------------------- ### Multiple Path Parameters Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/path-params.md Pass multiple variables into the path by defining unique names in the URL and function arguments. ```python @api.get("/events/{year}/{month}/{day}") def events(request, year: int, month: int, day: int): return {"date": [year, month, day]} ``` -------------------------------- ### Import Form Class Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/form-params.md Ensure you import the `Form` class from the `ninja` library when working with form data. ```python from ninja import Form ``` -------------------------------- ### Combine Request Body, Path, and Query Parameters Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/body.md Declare endpoints with path, query, and request body parameters simultaneously. Django Ninja correctly identifies the source for each parameter. ```python from ninja import Schema class Item(Schema): name: str description: str | None = None price: float quantity: int @api.post("/items/{item_id}") def create_item_with_query(request, item_id: int, item: Item, q: str | None = None): return {"message": "Item created successfully", "item_id": item_id, "item": item, "q": q} ``` -------------------------------- ### Integrate API URLs in urls.py Source: https://github.com/pmdevita/django-shinobi/blob/master/README.md Include the Django Shinobi API URLs in your project's main urls.py file. This makes your API endpoints accessible. ```python from .api import api urlpatterns = [ path("admin/", admin.site.urls), path("api/", api.urls), # <---------- ! ] ``` -------------------------------- ### List of Files with Extra Info Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/file-params.md Combine multiple file uploads with extra fields by using `File[list[UploadedFile]]` for the files parameter. ```python @api.post('/users') def create_user(request, details: Form[UserDetails], files: File[list[UploadedFile]]): return [details.dict(), [f.name for f in files]] ``` -------------------------------- ### Define Form Data Parameters Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/form-params.md Import `Form` from `ninja` and use it as a default value for your parameters to parse `request.POST` data. ```python from ninja import NinjaAPI, Form @api.post("/login") def login(request, username: Form[str], password: Form[str]): return {'username': username, 'password': '*****'} ``` -------------------------------- ### Define API Version 1 Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/versioning.md Instantiate NinjaAPI with a specific version number for API v1. ```python from ninja import NinjaAPI api = NinjaAPI(version='1.0.0') @api.get('/hello') def hello(request): return {'message': 'Hello from V1'} ``` -------------------------------- ### Specify Cookies for Test Client Request Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/testing.md Shows how to set request cookies, both globally when instantiating the TestClient and individually for a specific request. ```python client = TestClient(router, COOKIES={"A": "a", "B": "b"}) # The request will be made with {"A": "na", "B": "b", "C": "nc"} cookies response = client.get("/test-cookies", COOKIES={"A": "na", "C": "nc"}) ``` -------------------------------- ### Add Operation Description with Argument Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/reference/operations-parameters.md Use the `description` argument to provide a more detailed explanation for an API operation. ```python @api.post("/orders/", description="Creates an order and updates stock") def create_order(request, order: Order): return {"success": True} ``` -------------------------------- ### Define a Data Model using Schema Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/body.md Create a data model by inheriting from Schema and using standard Python types. Attributes with a default value of None become optional. ```python class Item(Schema): name: str description: str | None = None price: float quantity: int ``` -------------------------------- ### Configure Schema Compatibility Mode Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/differences.md Override the global NINJA_COMPATIBILITY setting for individual schemas by setting the `_compatibility` attribute. Set to True to retain old behavior, False for performance improvements. ```python class MySchema(Schema): _compatibility = True ... ``` -------------------------------- ### Using `description` for Operation Details Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/reference/operations-parameters.md Provide more detailed information about an operation using the `description` argument or Python docstrings. Multi-line descriptions are best handled with docstrings. ```APIDOC ## POST /orders/ ### Description Creates an order and updates stock. To create an order please provide: - **first_name** - **last_name** - and **list of Items** *(product + amount)* ### Method POST ### Endpoint /orders/ ### Parameters #### Request Body - **order** (Order) - Required - The order details. ### Request Example { "example": "order object" } ### Response #### Success Response (200) - **success** (boolean) - Indicates if the operation was successful. ### Response Example { "example": { "success": true } } ### Code Example ```python @api.post("/orders/") def create_order(request, order: Order): """ To create an order please provide: - **first_name** - **last_name** - and **list of Items** *(product + amount)* """ return {"success": True} ``` ``` -------------------------------- ### Define API Endpoint in api.py Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/index.md Define an API endpoint using Django Shinobi, including type hints for parameters and return types. ```python from shinobi import API, NinjaAPI from typing import Optional api = API(title="My API", version="0.1.0") @api.get("/add") def add(a: int, b: int = 2) -> int: """Adds two numbers together. Parameters: a (int): The first number. b (int, optional): The second number. Defaults to 2. Returns: int: The sum of a and b. """ return a + b @api.get("/items/{item_id}") def get_item(item_id: str, q: Optional[str] = None): """Retrieves an item by its ID. Parameters: item_id (str): The ID of the item to retrieve. q (Optional[str], optional): An optional query parameter. Defaults to None. Returns: dict: A dictionary containing the item ID and query parameter if provided. """ if q: return {"item_id": item_id, "q": q} return {"item_id": item_id} ``` -------------------------------- ### Create a Custom YAML Parser Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/request-parsers.md Define a custom parser by inheriting from `ninja.parser.Parser` and implementing the `parse_body` method to handle YAML content using `PyYAML`. ```python import yaml from typing import List from ninja import NinjaAPI from ninja.parser import Parser class MyYamlParser(Parser): def parse_body(self, request): return yaml.safe_load(request.body) api = NinjaAPI(parser=MyYamlParser()) class Payload(Schema): ints: List[int] string: str f: float @api.post('/yaml') def operation(request, payload: Payload): return payload.dict() ``` -------------------------------- ### Run Tests with Pytest Source: https://github.com/pmdevita/django-shinobi/blob/master/CONTRIBUTING.md Execute all project tests using the pytest command. ```bash pytest . ``` -------------------------------- ### Initialize API with Custom Parser Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/request-parsers.md Configure your Django Ninja API instance to use a custom parser by passing an instance of your parser class to the `parser` argument during initialization. ```python api = NinjaAPI(parser=MyYamlParser()) ``` -------------------------------- ### Apply Tags to All Router Operations Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/routers.md Use the `tags` argument when adding a router to apply tags to all its operations. This helps in organizing endpoints in the OpenAPI documentation. Alternatively, set tags during router instantiation. ```python api.add_router("/events/", events_router, tags=["events"]) ``` ```python router = Router(tags=["events"]) ``` -------------------------------- ### Direct Serialization with .from_orm() Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Serialize model instances directly using `.from_orm()` on the schema. Use `.dict()` or `.json()` for dictionary or string output. ```python >>> person = Person.objects.get(id=1) >>> data = PersonSchema.from_orm(person) >>> data PersonSchema(id=1, name='Mr. Smith') >>> data.dict() {'id':1, 'name':'Mr. Smith'} >>> data.json() '{"id":1, "name":"Mr. Smith"}' ``` -------------------------------- ### Disable API Documentation Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/api-docs.md To hide the interactive API documentation, set the 'docs_url' argument to None during NinjaAPI initialization. ```python api = NinjaAPI(docs_url=None) ``` -------------------------------- ### Handle File Upload Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/tutorial/other/crud.md Implement a POST endpoint specifically for handling file uploads using Django Ninja's File type. The uploaded file is saved to the configured storage. ```python from django.core.files.storage import FileSystemStorage from ninja import UploadedFile, File STORAGE = FileSystemStorage() @api.post("/upload") def create_upload(request, cv: File[UploadedFile]): filename = STORAGE.save(cv.name, cv) # Handle things further ``` -------------------------------- ### Basic Query Parameter Declaration Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/query-params.md Declare query parameters by including them as function arguments that are not path parameters. By default, they are strings, but type annotations enable automatic conversion and validation. ```python from ninja import NinjaAPI api = NinjaAPI() @api.get("/weapons") def list_weapons(request, offset: int = 0, limit: int = 10): return weapons[offset : offset + limit] ``` -------------------------------- ### Define API Version 2 Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/versioning.md Instantiate NinjaAPI with a different version number for API v2. ```python from ninja import NinjaAPI api = NinjaAPI(version='2.0.0') @api.get('/hello') def hello(request): return {'message': 'Hello from V2'} ``` -------------------------------- ### Upload Array of Files Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/input/file-params.md To upload multiple files, declare the parameter as `File[List[UploadedFile]]`. This allows you to process a list of uploaded files. ```python from typing import List from ninja import NinjaAPI, File from ninja.files import UploadedFile @api.post("/upload-many") def upload_many(request, files: File[List[UploadedFile]]): return [f.name for f in files] ``` -------------------------------- ### Empty Response (204 No Content) Source: https://github.com/pmdevita/django-shinobi/blob/master/docs/docs/guides/response/index.md Shows how to define an endpoint that returns a 204 No Content status code with no response body. ```APIDOC ## POST /no_content ### Description An endpoint that successfully completes an action and returns a 204 No Content status, indicating no response body. ### Method POST ### Endpoint /no_content ### Response #### Success Response (204) - No content. ### Response Example #### 204 No Content (No body) ```