Form data
Django Ninja also allows you to parse and validate request.POST
data
(aka application/x-www-form-urlencoded
or multipart/form-data
).
Form Data as params
from ninja import NinjaAPI, Form
@api.post("/login")
def login(request, username: Form[str], password: Form[str]):
return {'username': username, 'password': '*****'}
Note the following:
1) You need to import the Form
class from ninja
from ninja import Form
2) Use Form
as default value for your parameter:
username: Form[str]
Using a Schema
In a similar manner to Body, you can use a Schema to organize your parameters.
from ninja import Form, Schema
class Item(Schema):
name: str
description: str = None
price: float
quantity: int
@api.post("/items")
def create(request, item: Form[Item]):
return item
Request form + path + query parameters
In a similar manner to Body, you can use Form data in combination with other parameter sources.
You can declare query and path and form field, and etc... parameters at the same time.
Django Ninja will recognize that the function parameters that match path
parameters should be taken from the path, and that function parameters that
are declared with Form(...)
should be taken from the request form fields, etc.
from ninja import Form, Schema
class Item(Schema):
name: str
description: str = None
price: float
quantity: int
@api.post("/items/{item_id}")
def update(request, item_id: int, q: str, item: Form[Item]):
return {"item_id": item_id, "item": item.dict(), "q": q}
Mapping Empty Form Field to Default
Form fields that are optional, are often sent with an empty value. This value is
interpreted as an empty string, and thus may fail validation for fields such as int
or bool
.
This can be fixed, as described in the Pydantic docs, by using Generic Classes as Types.
from ninja import Form, Schema
from typing import Annotated, TypeVar
from pydantic import WrapValidator
from pydantic_core import PydanticUseDefault
def _empty_str_to_default(v, handler, info):
if isinstance(v, str) and v == '':
raise PydanticUseDefault
return handler(v)
T = TypeVar('T')
EmptyStrToDefault = Annotated[T, WrapValidator(_empty_str_to_default)]
class Item(Schema):
name: str
description: str = None
price: EmptyStrToDefault[float] = 0.0
quantity: EmptyStrToDefault[int] = 0
in_stock: EmptyStrToDefault[bool] = True
@api.post("/items-blank-default")
def update(request, item: Form[Item]):
return item.dict()