Django Shinobi

repository·master·Indexed 43 days ago

https://github.com/pmdevita/django-shinobi

A high-performance web framework for building APIs with Django, utilizing Python 3.6+ type hints and Pydantic for fast execution and automatic validation. It features automatic OpenAPI schema generation with Swagger UI and Redoc, async view support for Django 3.1+, and performance improvements for Schemas.

Tokens
37K
Snippets
153
Records
177
Agent score
50%

What's inside Django Shinobi

  1. Host API documentation via CDN or local staticfiles

    master

    You can choose how the documentation's JavaScript and CSS assets are served:

    1. CDN (Default): Do not add ninja to your INSTALLED_APPS. The interactive UI will be hosted via a CDN.
    2. Local Static Files: Add "ninja" to your INSTALLED_APPS. This allows standard Django staticfiles mechanics to host the documentation assets from your own server.
  2. Combine request body, path, and query parameters

    master

    Django Ninja automatically determines the source of a function parameter based on its type and declaration:

    1. Path Parameters: If the parameter name matches a placeholder in the URL path (e.g., /items/{item_id}).
    2. Query Parameters: If the parameter is a singular type (e.g., int, str, float, bool) and is not in the path.
    3. Request Body: If the parameter is an instance of a Schema (or Pydantic BaseModel).

    You can use all three types in a single path operation function.

    from ninja import NinjaAPI, Schema
    
    api = NinjaAPI()
    
    class Item(Schema):
        name: str
        price: float
    
    @api.put("/items/{item_id}")
    def update_item(request, item_id: int, item: Item, query_param: str):
        # item_id is from path
        # item is from request body
        # query_param is from query string
        return {"item_id": item_id, "item": item, "query_param": query_param}
  3. How reverse URL resolution works in Django Shinobi

    master

    Django Shinobi (via Django Ninja) automatically generates reverse URL names for each method decorated in a NinjaAPI instance or a Router.

    By default, URLs are contained within a namespace that follows the pattern api-<version>. The URL name itself matches the name of the decorated function.

    To resolve these URLs in your Django code, use reverse_lazy or reverse with the format namespace:url_name.

    api = NinjaAPI()
    
    @api.get("/")
    def index(request):
        ...
    
    # Resolving the URL
    index_url = reverse_lazy("api-1.0.0:index")
  4. Introspect related fields using `depth`

    master

    The depth argument allows create_schema to introspect Django model relationships such as ForeignKey, OneToOne, and ManyToManyField. Setting depth > 0 will cause the function to automatically generate schemas for the related models up to the specified depth.

    from django.contrib.auth.models import User
    from ninja.orm import create_schema
    
    # depth=1 will introspect the 'groups' ManyToMany field
    UserSchema = create_schema(User, depth=1, fields=['username', 'groups'])
    
    # Resulting schema structure:
    # class UserSchema(Schema):
    #    username: str
    #    groups: List[Group]
    #
    # class Group(Schema):
    #    id: int
    #    name: str
    #    permissions: List[int]
  5. Create a custom pagination class

    master

    To implement custom pagination logic, subclass ninja.pagination.PaginationBase (or AsyncPaginationBase for async support) and implement the following:

    1. Input(Schema): A nested class defining the query parameters the paginator accepts.
    2. Output(Schema): A nested class defining the structure of the paginated response.
    3. paginate_queryset(self, queryset, pagination, **params): The method that performs the slicing/logic. It receives the queryset, the validated pagination input, and **params (which includes the request object).

    Note on Output Attributes: By default, items are placed in an 'items' key in the response. To change this, set the items_attribute string attribute on your class.

    from ninja.pagination import paginate, PaginationBase
    from ninja import Schema
    from typing import List, Any
    
    class CustomPagination(PaginationBase):
        class Input(Schema):
            skip: int
            
        class Output(Schema):
            items: List[Any]
            total: int
            per_page: int
    
        # Change the key in the JSON response from 'items' to 'results'
        items_attribute: str = "items"
    
        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()
  6. How authentication works in Django Ninja

    master

    Authentication in Django Ninja is defined at the operation level by passing an authentication object to the auth argument of an API decorator (e.g., @api.get(...)).

    When an authentication object is used, the client must provide valid credentials (like a session cookie, API key, or Bearer token). If authentication fails, the API returns an HTTP 401 error. If successful, the return value of the authentication logic is assigned to the request.auth attribute, making it available within your endpoint function.

    @api.get("/pets", auth=django_auth)
    def pets(request):
        # request.auth contains the authenticated user/object
        return {"message": "success"}
  7. Define input types using Python type hints

    master

    Django Shinobi (via Django Ninja) uses standard Python type hints to determine and validate input types. If no type hint is provided, a str is assumed. Providing explicit type hints allows the API to automatically validate and cast inputs (e.g., converting a query string value to an int).

    @api.get("/math")
    def math(request, a: int, b: int):
        return {"add": a + b, "multiply": a * b}
  8. Anticipated breaking changes in Django Ninja v1.0.0

    master

    The project is planning a transition to version 1.0.0, which will introduce breaking changes to address architectural limitations. If you are currently using the library, be aware of the following planned changes that will require code updates:

    • Authentication (auth): The authentication interface will transition from a callable to a class-based interface. This change is intended to provide native support for async authenticators.
    • Responses (responses): The response handling will be expanded to support codes, headers, and content, bringing it closer to the behavior of a general Response class.
    • Router Paths: Currently, router paths are automatically joined with a /. In v1, this behavior may change to allow the router prefix to act as a true prefix rather than a subfolder (avoiding forced slash joining in certain cases).
  9. Why use Django Ninja instead of FastAPI

    master

    While FastAPI is inspired by the same principles, Django Ninja is preferred for Django-based projects because it solves several integration friction points:

    1. ORM Compatibility: FastAPI's ORM-agnostic nature creates difficulties with the Django ORM, particularly regarding async support and connection management (e.g., closed connection issues in sync mode).
    2. Dependency Injection Verbosity: FastAPI requires explicit dependency injection for common requirements like database sessions and authentication in every operation. Django Ninja uses the request instance attributes (similar to standard Django views), reducing boilerplate.
    3. Naming Conflicts: Django Ninja avoids the confusion caused by the term model being used for both Django ORM models and Pydantic models in FastAPI/FastAPI-like environments.
  10. Use multiple authenticators

    master

    The auth argument can accept a list or tuple of authenticators. Django Ninja will attempt to authenticate using each one in order. If the first one fails, it moves to the second, and so on. If all provided authenticators fail, an authentication error is returned.

    from ninja.security import APIKeyQuery, APIKeyHeader
    
    # Tries Query first, then Header
    @api.get("/multi", auth=[APIKeyQuery(), APIKeyHeader()])
    def multi_auth_endpoint(request):
        return {"auth": request.auth}
  11. Combine Form, Path, and Query parameters

    master

    Django Ninja can simultaneously extract parameters from different sources in a single endpoint. It uses the type annotations to distinguish between them:

    • Parameters matching path segments are taken from the path.
    • Parameters declared with Form[...] are taken from the request form fields.
    • Other parameters (without specific wrappers) are typically taken from query parameters.