## File: README.md

[](https://github.com/vitalik/django-ninja/issues/383)



[^ Please read ^](https://github.com/vitalik/django-ninja/issues/383)










[](https://badge.fury.io/py/django-ninja)
[](https://pepy.tech/project/django-ninja)

# Django Ninja - Fast Django REST Framework

**Django Ninja** is a web framework for building APIs with **Django** and Python 3.6+ **type hints**.


 **Key features:**

  - **Easy**: Designed to be easy to use and intuitive.
  - **FAST execution**: Very high performance thanks to **[Pydantic](https://pydantic-docs.helpmanual.io)** and **[async support](/docs/docs/guides/async-support.md)**.
  - **Fast to code**: Type hints and automatic docs lets you focus only on business logic.
  - **Standards-based**: Based on the open standards for APIs: **OpenAPI** (previously known as Swagger) and **JSON Schema**.
  - **Django friendly**: (obviously) has good integration with the Django core and ORM.
  - **Production ready**: Used by multiple companies on live projects (If you use django-ninja and would like to publish your feedback, please email ppr.vitaly@gmail.com).





**Documentation**: https://django-ninja.dev

---

## Installation

```
pip install django-ninja
```



## Usage


In your django project next to urls.py create new `api.py` file:

```Python
from ninja import NinjaAPI

api = NinjaAPI()


@api.get("/add")
def add(request, a: int, b: int):
    return {"result": a + b}
```


Now go to `urls.py` and add the following:


```Python hl_lines="3 7"
...
from .api import api

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", api.urls),  # <---------- !
]
```

**That's it !**

Now you've just created an API that:

 - receives an HTTP GET request at `/api/add`
 - takes, validates and type-casts GET parameters `a` and `b`
 - decodes the result to JSON
 - generates an OpenAPI schema for defined operation

### Interactive API docs

Now go to [http://127.0.0.1:8000/api/docs](http://127.0.0.1:8000/api/docs)

You will see the automatic interactive API documentation (provided by [Swagger UI](https://github.com/swagger-api/swagger-ui) or [Redoc](https://github.com/Redocly/redoc)):




## Sponsors

[](https://www.sendcloud.com/)

[Become a sponsor](mailto:ppr.vitaly@gmail.com)



## What next?

 - Read the full documentation here - https://django-ninja.dev
 - To support this project, please give star it on Github. 
 - Share it [via Twitter](https://twitter.com/intent/tweet?text=Check%20out%20Django%20Ninja%20-%20Fast%20Django%20REST%20Framework%20-%20https%3A%2F%2Fdjango-ninja.dev)
 - If you already using django-ninja, please share your feedback to ppr.vitaly@gmail.com

---

## File: docs/docs/guides/input/body.md

# Request Body

Request bodies are typically used with “create” and “update” operations (POST, PUT, PATCH).
For example, when creating a resource using POST or PUT, the request body usually contains the representation of the resource to be created.

To declare a **request body**, you need to use **Django Ninja `Schema`**.

!!! info
    Under the hood **Django Ninja** uses [Pydantic](https://pydantic-docs.helpmanual.io/) models with all their power and benefits.
    The alias `Schema` was chosen to avoid confusion in code when using Django models, as Pydantic's model class is called Model by default, and conflicts with Django's Model class.

## Import Schema

First, you need to import `Schema` from `ninja`:

```python hl_lines="2"
{!./src/tutorial/body/code01.py!}
```

## Create your data model

Then you declare your data model as a class that inherits from `Schema`.

Use standard Python types for all the attributes:

```python hl_lines="5 6 7 8 9"
{!./src/tutorial/body/code01.py!}
```

Note: if you use **`None`** as the default value for an attribute, it will become optional in the request body.
For example, this model above declares a JSON "`object`" (or Python `dict`) like:

```JSON
{
    "name": "Katana",
    "description": "An optional description",
    "price": 299.00,
    "quantity": 10
}
```

...as `description` is optional (with a default value of `None`), this JSON "`object`" would also be valid:

```JSON
{
    "name": "Katana",
    "price": 299.00,
    "quantity": 10
}
```

## Declare it as a parameter

To add it to your *path operation*, declare it the same way you declared the path and query parameters:

```python hl_lines="13"
{!./src/tutorial/body/code01.py!}
```

... and declare its type as the model you created, `Item`.

## Results

With just that Python type declaration, **Django Ninja** will:

* Read the body of the request as JSON.
* Convert the corresponding types (if needed).
* Validate the data.
    * If the data is invalid, it will return a nice and meaningful error, indicating exactly where and what the incorrect data was.
* Give you the received data in the parameter `item`.
    * Because you declared it in the function to be of type `Item`, you will also have all the editor support
      (completion, etc.) for all the attributes and their types.
* Generate [JSON Schema](https://json-schema.org) definitions for
  your models, and you can also use them anywhere else you like if it makes sense for your project.
* Those schemas will be part of the generated OpenAPI schema, and used by the automatic documentation <abbr title="User Interfaces">UI's</abbr>.

## Automatic docs

The JSON Schemas of your models will be part of your OpenAPI generated schema, and will be shown in the interactive API docs:



... and they will be also used in the API docs inside each *path operation* that needs them:



## Editor support

In your editor, inside your function you will get type hints and completion everywhere (this wouldn't happen if you received a `dict` instead of a Schema object):




The previous screenshots were taken with [Visual Studio Code](https://code.visualstudio.com).

You would get the same editor support with [PyCharm](https://www.jetbrains.com/pycharm/) and most of the other Python editors.


## Request body + path parameters

You can declare path parameters **and** body requests 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 `Schema` should be **taken from the request body**.

```python hl_lines="11 12"
{!./src/tutorial/body/code02.py!}
```

## Request body + path + query parameters

You can also declare **body**, **path** and **query** parameters, all at the same time.

**Django Ninja** will recognize each of them and take the data from the correct place.

```python hl_lines="11 12"
{!./src/tutorial/body/code03.py!}
```

The function parameters will be recognized as follows:

* If the parameter is also declared in the **path**, it will be used as a path parameter.
* If the parameter is of a **singular type** (like `int`, `float`, `str`, `bool`, etc.), it will be interpreted as a **query** parameter.
* If the parameter is declared to be of the type of **Schema** (or Pydantic `BaseModel`), it will be interpreted as a request **body**.

---

## File: docs/docs/guides/input/file-params.md

# File uploads

Handling files are no different from other parameters.

```python hl_lines="1 2 5"
from ninja import NinjaAPI, File
from ninja.files import UploadedFile

@api.post("/upload")
def upload(request, file: File[UploadedFile]):
    data = file.read()
    return {'name': file.name, 'len': len(data)}
```


`UploadedFile` is an alias to [Django's UploadFile](https://docs.djangoproject.com/en/stable/ref/files/uploads/#django.core.files.uploadedfile.UploadedFile) and has all the methods and attributes to access the uploaded file:

 - read()
 - multiple_chunks(chunk_size=None)
 - chunks(chunk_size=None)
 - name
 - size
 - content_type
 - content_type_extra
 - charset
 - etc.

## Uploading array of files

To **upload several files** at the same time, just declare a `List` of `UploadedFile`:


```python hl_lines="1 6"
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]
```

## Uploading files with extra fields

Note: The HTTP protocol does not allow you to send files in `application/json` format by default (unless you encode it somehow to JSON on client side)

To send files along with some extra attributes, you need to send bodies with `multipart/form-data` encoding. You can do it by simply marking fields with `Form`:

```python hl_lines="14"
from ninja import NinjaAPI, Schema, UploadedFile, Form, File
from datetime import date

api = NinjaAPI()


class UserDetails(Schema):
    first_name: str
    last_name: str
    birthdate: date


@api.post('/users')
def create_user(request, details: Form[UserDetails], file: File[UploadedFile]):
    return [details.dict(), file.name]

```

Note: in this case all fields should be send as form fields

You can as well send payload in single field as JSON - just remove the Form mark from:

```python
@api.post('/users')
def create_user(request, details: UserDetails, file: File[UploadedFile]):
    return [details.dict(), file.name]

```

this will expect from the client side to send data as `multipart/form-data with 2 fields:
  
  - details: JSON as string
  - file: file


### List of files with extra info

```python
@api.post('/users')
def create_user(request, details: Form[UserDetails], files: File[list[UploadedFile]]):
    return [details.dict(), [f.name for f in files]]
```

### Optional file input

If you would like the file input to be optional, all that you have to do is to pass `None` to the `File` type, like so:

```python
@api.post('/users')
def create_user(request, details: Form[UserDetails], avatar: File[UploadedFile] = None):
    user = add_user_to_database(details)
    if avatar is not None:
        set_user_avatar(user)
```


## Handling request.FILES in PUT/PATCH Requests

**Problem**

```python
@api.put("/upload") # !!!!
def upload(request, file: File[UploadedFile]):
   ...
```

For some [historical reasons Django’s](https://groups.google.com/g/django-users/c/BeBKj_6qNsc) `request.FILES` is populated only for POST requests by default. When using HTTP PUT or PATCH methods with file uploads (e.g., multipart/form-data), request.FILES will not contain uploaded files. This is a known Django behavior, not specific to Django Ninja.

As a result, views expecting files in PUT or PATCH requests may not behave correctly, since request.FILES will be empty.

**Solution**

Django Ninja provides a built-in middleware to automatically fix this behavior:
`ninja.compatibility.files.fix_request_files_middleware`

This middleware will manually parse multipart/form-data for PUT and PATCH requests and populate request.FILES, making file uploads work as expected across all HTTP methods.

**Usage**

To enable the middleware, add the following to your Django settings:

```python
MIDDLEWARE = [
    # ... your existing middleware ...
    "ninja.compatibility.files.fix_request_files_middleware",
]
```

**Auto-detection**

When Django Ninja detects a PUT or PATCH  etc methods with multipart/form-data and expected FILES  - it will throw an error message suggesting you install the compatibility middleware:


Note: This middleware does not interfere with normal POST behavior or any other methods.

---

## File: docs/docs/guides/input/filtering.md

# Filtering

If you want to allow the user to filter your querysets by a number of different attributes, it makes sense
to encapsulate your filters into a `FilterSchema` class. `FilterSchema` is a regular `Schema`, so it's using all the
necessary features of Pydantic, but it also adds some bells and whistles that ease the translation of the user-facing filtering
parameters into database queries. 

Start off with defining a subclass of `FilterSchema`:

```python hl_lines="6 7 8 9"
from ninja import FilterSchema
from typing import Optional
from datetime import datetime


class BookFilterSchema(FilterSchema):
    name: Optional[str] = None
    author: Optional[str] = None
    created_after: Optional[datetime] = None
```


Next, use this schema in conjunction with `Query` in your API handler:
```python hl_lines="2"
@api.get("/books")
def list_books(request, filters: BookFilterSchema = Query(...)):
    books = Book.objects.all()
    books = filters.filter(books)
    return books
```

Just like described in [defining query params using schema](./query-params.md#using-schema), Django Ninja converts the fields
defined in `BookFilterSchema` into query parameters.

You can use a shorthand one-liner `.filter()` to apply those filters to your queryset:
```python hl_lines="4"
@api.get("/books")
def list_books(request, filters: Query[BookFilterSchema]):
    books = Book.objects.all()
    books = filters.filter(books)
    return books
```

Under the hood, `FilterSchema` converts its fields into [Q expressions](https://docs.djangoproject.com/en/3.1/topics/db/queries/#complex-lookups-with-q-objects) which it then combines and uses to filter your queryset.


Alternatively to using the `.filter` method, you can get the prepared `Q`-expression and perform the filtering yourself.
That can be useful, when you have some additional queryset filtering on top of what you expose to the user through the API:
```python hl_lines="5 8"
@api.get("/books")
def list_books(request, filters: Query[BookFilterSchema]):

    # Never serve books from inactive publishers and authors
    q = Q(author__is_active=True) | Q(publisher__is_active=True)
    
    # But allow filtering the rest of the books
    q &= filters.get_filter_expression()
    return Book.objects.filter(q)
```

By default, the filters will behave the following way:

* `None` values will be ignored and not filtered against;
* Every non-`None` field will be converted into a `Q`-expression based on the `Field` definition of each field;
* All `Q`-expressions will be merged into one using `AND` logical operator;
* The resulting `Q`-expression is used to filter the queryset and return you a queryset with a `.filter` clause applied.

## Customizing Fields
By default, `FilterSet` will use the field names to generate Q expressions:
```python
class BookFilterSchema(FilterSchema):
    name: Optional[str] = None
```
The `name` field will be converted into `Q(name=...)` expression.

When your database lookups are more complicated than that, you can annotate your fields with an instance of `FilterLookup` where you specify how you wish your field to be looked up for filtering:
```python hl_lines="5"
from ninja import FilterSchema, FilterLookup
from typing import Annotated

class BookFilterSchema(FilterSchema):
    name: Annotated[Optional[str], FilterLookup("name__icontains")] = None
```

You can even specify multiple lookups as a list:
```python hl_lines="3 4 5"
class BookFilterSchema(FilterSchema):
    search: Annotated[Optional[str], FilterLookup(
        ["name__icontains",
         "author__name__icontains",
         "publisher__name__icontains"]
    )]
```

By default, field-level expressions are combined using `"OR"` connector, so with the above setup, a query parameter `?search=foobar` will search for books that have "foobar" in either of their name, author or publisher.

And to make generic fields, you can make the field name implicit by skipping it:
```python hl_lines="1 4"
IContainsField = Annotated[Optional[str], FilterLookup('__icontains')]

class BookFilterSchema(FilterSchema):
    name: IContainsField = None
```

??? note "Deprecated syntax"

    In previous versions, database lookups were specified using `Field(q=...)` syntax:
    ```python
    from ninja import FilterSchema, Field
    
    class BookFilterSchema(FilterSchema):
        name: Optional[str] = Field(None, q="name__icontains")
    ```
    
    This approach is still supported, but it is considered **deprecated** and **not recommended** for new code because:
    
    - Poor IDE support (IDEs don't recognize custom `Field` arguments)
    - Uses deprecated Pydantic features (`**extra`)
    - Less type-safe and harder to maintain
    
    The new `FilterLookup` annotation provides better developer experience with full IDE support and type safety. Prefer using `FilterLookup` for new projects.


## Combining expressions
By default,

* Field-level expressions are joined together using `OR` operator.
* The fields themselves are joined together using `AND` operator.

So, with the following `FilterSchema`...
```python
class BookFilterSchema(FilterSchema):
    search: Annotated[
        Optional[str],
        FilterLookup(["name__icontains", "author__name__icontains"])] = None
    popular: Optional[bool] = None
```
...and the following query parameters from the user
```
http://localhost:8000/api/books?search=harry&popular=true
```
the `FilterSchema` instance will look for popular books that have `harry` in the book's _or_ author's name. 


You can customize this behavior using an `expression_connector` argument in field-level and class-level definition:
```python hl_lines="12"
from ninja import FilterConfigDict, FilterLookup, FilterSchema

class BookFilterSchema(FilterSchema):
    active: Annotated[
        Optional[bool],
        FilterLookup(
            ["is_active", "publisher__is_active"],
            expression_connector="AND"
        )] = None
    name: Annotated[Optional[str], FilterLookup("name__icontains")] = None
    
    model_config = FilterConfigDict(expression_connector="OR")
```

An expression connector can take the values of `"OR"`, `"AND"` and `"XOR"`, but the latter is only [supported](https://docs.djangoproject.com/en/4.1/ref/models/querysets/#xor) in Django starting with 4.1.

Now, a request with these query parameters 
```
http://localhost:8000/api/books?name=harry&active=true
```
...shall search for books that have `harry` in their name _or_ are active themselves _and_ are published by active publishers.


## Filtering by Nones
You can make the `FilterSchema` treat `None` as a valid value that should be filtered against.

This can be done on a field level with a `ignore_none` kwarg:
```python hl_lines="3"
class BookFilterSchema(FilterSchema):
    name: Annotated[Optional[str], FilterLookup("name__icontains")] = None
    tag: Annotated[Optional[str], FilterLookup("tag", ignore_none=False)] = None
```

This way when no other value for `"tag"` is provided by the user, the filtering will always include a condition `tag=None`.

You can also specify this setting for all fields at the same time in `model_config`:
```python hl_lines="5"
class BookFilterSchema(FilterSchema):
    name: Annotated[Optional[str], FilterLookup("name__icontains")] = None
    tag: Optional[str] = None
    
    model_config = FilterConfigDict(ignore_none=False)
```


## Custom expressions
Sometimes you might want to have complex filtering scenarios that cannot be handled by individual Field annotations.
For such cases you can implement your field filtering logic as a custom method. Simply define a method called `filter_<fieldname>` which takes a filter value and returns a Q expression:

```python hl_lines="5"
class BookFilterSchema(FilterSchema):
    tag: Optional[str] = None
    popular: Optional[bool] = None
    
    def filter_popular(self, value: bool) -> Q:
        return Q(view_count__gt=1000) | Q(download_count__gt=100) if value else Q()
```
Such field methods take precedence over what is specified in the `Field()` definition of the corresponding fields.

If that is not enough, you can implement your own custom filtering logic for the entire `FilterSet` class in a `custom_expression` method:

```python hl_lines="5"
class BookFilterSchema(FilterSchema):
    name: Optional[str] = None
    popular: Optional[bool] = None

    def custom_expression(self) -> Q:
        q = Q()
        if self.name:
            q &= Q(name__icontains=self.name)
        if self.popular:
            q &= (
                Q(view_count__gt=1000) |
                Q(downloads__gt=100) |
                Q(tag='popular')
            )
        return q
```
The `custom_expression` method takes precedence over any other definitions described earlier, including `filter_<fieldname>` methods.

---

## File: docs/docs/guides/input/form-params.md

# 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 

```python hl_lines="1 4"
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`
```python
from ninja import Form
```

2) Use `Form` as default value for your parameter:
```python
username: Form[str]
```

## Using a Schema

In a similar manner to [Body](body.md#declare-it-as-a-parameter), you can use
a Schema to organize your parameters.

```python hl_lines="12"
{!./src/tutorial/form/code01.py!}
```

## Request form + path + query parameters

In a similar manner to [Body](body.md#request-body-path-query-parameters), 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.

```python hl_lines="12"
{!./src/tutorial/form/code02.py!}
```
## 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](https://pydantic-docs.helpmanual.io/usage/types/#generic-classes-as-types).

```python hl_lines="15 16 23-25"
{!./src/tutorial/form/code03.py!}
```

---

## File: docs/docs/guides/input/operations.md

# HTTP Methods

## Defining operations

An `operation` can be one of the following [HTTP methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods):

- GET
- POST
- PUT
- DELETE
- PATCH

**Django Ninja** comes with a decorator for each operation:

```python hl_lines="1 5 9 13 17"
@api.get("/path")
def get_operation(request):
    ...

@api.post("/path")
def post_operation(request):
    ...

@api.put("/path")
def put_operation(request):
    ...

@api.delete("/path")
def delete_operation(request):
    ...

@api.patch("/path")
def patch_operation(request):
    ...
```

See the [operations parameters](../../reference/operations-parameters.md)
reference docs for information on what you can pass to any of these decorators.

## Handling multiple methods

If you need to handle multiple methods with a single function for a given path,
you can use the `api_operation` decorator:

```python hl_lines="1"
@api.api_operation(["POST", "PATCH"], "/path")
def mixed_operation(request):
    ...
```

This feature can also be used to implement other HTTP methods that don't have
corresponding **Django Ninja** methods, such as `HEAD` or `OPTIONS`.

```python hl_lines="1"
@api.api_operation(["HEAD", "OPTIONS"], "/path")
def mixed_operation(request):
    ...
```

---

## File: docs/docs/guides/input/path-params.md

# Path parameters
You can declare path "parameters" with the same syntax used by Python format-strings (which luckily also matches the [OpenAPI path parameters](https://swagger.io/docs/specification/describing-parameters/#path-parameters)):

```python hl_lines="1 2"
{!./src/tutorial/path/code01.py!}
```

The value of the path parameter `item_id` will be passed to your function as the argument `item_id`.

So, if you run this example and go to [http://localhost:8000/api/items/foo](http://localhost:8000/api/items/foo), you will see this response:

```JSON
{"item_id":"foo"}
```


### Path parameters with types
You can declare the type of path parameter in the function using standard Python type annotations:

```python hl_lines="2"
{!./src/tutorial/path/code02.py!}
```

In this case,`item_id` is declared to be an **`int`**. This will give you editor and linter support for error checks, completion, etc.

If you run this in your browser with [http://localhost:8000/api/items/3](http://localhost:8000/api/items/3), you will see this response:
```JSON
{"item_id":3}
```

!!! tip
    Notice that the value your function received (and returned) is **3**, as a Python `int` - not a string `"3"`.
    So, with just that type declaration, **Django Ninja** gives you automatic request "parsing" and validation.



### Data validation
On the other hand, if you go to the browser at [http://localhost:8000/api/items/foo](http://localhost:8000/api/items/foo) <small>*(`"foo"` is not int)*</small>, you will see an HTTP error like this:

```JSON hl_lines="8"
{
    "detail": [
        {
            "loc": [
                "path",
                "item_id"
            ],
            "msg": "value is not a valid integer",
            "type": "type_error.integer"
        }
    ]
}
```


### Django Path Converters

You can use [Django Path Converters](https://docs.djangoproject.com/en/stable/topics/http/urls/#path-converters)
to help parse the path:

```python hl_lines="1"
@api.get("/items/{int:item_id}")
def read_item(request, item_id):
    return {"item_id": item_id}
```

In this case,`item_id` will be parsed as an **`int`**. If `item_id` is not a valid `int`, the url will not
match.  (e.g. if no other path matches, a *404 Not Found* will be returned)

!!! tip
    Notice that, since **Django Ninja** uses a default type of `str` for unannotated parameters, the value the
    function above received (and returned) is `"3"`, as a Python `str` - not an integer **3**. To receive
    an `int`, simply declare `item_id` as an `int` type annotation in the function definition as normal:

    ```python hl_lines="2"
    @api.get("/items/{int:item_id}")
    def read_item(request, item_id:int):
        return {"item_id": item_id}
    ```
 
#### Path params with slashes

Django's `path` converter allows you to handle path-like parameters:

```python hl_lines="1"
@api.get('/dir/{path:value}')
def someview(request, value: str):
    return value
```
you can query this operation with `/dir/some/path/with-slashes` and your `value` will be equal to `some/path/with-slashes`

### Multiple parameters

You can pass as many variables as you want into `path`, just remember to have unique names and don't forget to use the same names in the function arguments.

```python
@api.get("/events/{year}/{month}/{day}")
def events(request, year: int, month: int, day: int):
    return {"date": [year, month, day]}
```


### Using Schema

You can also use Schema to encapsulate path parameters that depend on each other (and validate them as a group):


```python hl_lines="1 2  5 6 7 8 9 10 11 15"
{!./src/tutorial/path/code010.py!}
```

!!! note
    Notice that here we used a `Path` source hint to let **Django Ninja** know that this schema will be applied to path parameters.

### Documentation
Now, when you open your browser at [http://localhost:8000/api/docs](http://localhost:8000/api/docs), you will see the automatic, interactive, API documentation.

---

## File: docs/docs/guides/input/query-params.md

# Query parameters

When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.

```python hl_lines="5"
{!./src/tutorial/query/code01.py!}
```

To query this operation, you use a URL like:

```
http://localhost:8000/api/weapons?offset=0&limit=10
```
By default, all GET parameters are strings, and when you annotate your function arguments with types, they are converted to that type and validated against it.

The same benefits that apply to path parameters also apply to query parameters:

- Editor support (obviously)
- Data "parsing"
- Data validation
- Automatic documentation


!!! Note
    if you do not annotate your arguments, they will be treated as `str` types

```python hl_lines="2"
@api.get("/weapons")
def list_weapons(request, limit, offset):
    # type(limit) == str
    # type(offset) == str
```

### Defaults

As query parameters are not a fixed part of a path, they are optional and can have default values:

```python hl_lines="2"
@api.get("/weapons")
def list_weapons(request, limit: int = 10, offset: int = 0):
    return weapons[offset : offset + limit]
```

In the example above we set default values of `offset=0` and `limit=10`.

So, going to the URL:
```
http://localhost:8000/api/weapons
```
would be the same as going to:
```
http://localhost:8000/api/weapons?offset=0&limit=10
```
If you go to, for example:
```
http://localhost:8000/api/weapons?offset=20
```

the parameter values in your function will be:

 - `offset=20`  (because you set it in the URL)
 - `limit=10`  (because that was the default value)


### Required and optional parameters

You can declare required or optional GET parameters in the same way as declaring Python function arguments:

```python hl_lines="5"
{!./src/tutorial/query/code02.py!}
```

In this case, **Django Ninja** will always validate that you pass the `q` param in the GET, and the `offset` param is an optional integer.

### GET parameters type conversion

Let's declare multiple type arguments:
```python hl_lines="5"
{!./src/tutorial/query/code03.py!}
```
The `str` type is passed as is.

For the `bool` type, all the following:
```
http://localhost:8000/api/example?b=1
http://localhost:8000/api/example?b=True
http://localhost:8000/api/example?b=true
http://localhost:8000/api/example?b=on
http://localhost:8000/api/example?b=yes
```
or any other case variation (uppercase, first letter in uppercase, etc.), your function will see
the parameter `b` with a `bool` value of `True`, otherwise as `False`.

Date can be both date string and integer (unix timestamp):

```
http://localhost:8000/api/example?d=**1577836800**  # same as 2020-01-01
http://localhost:8000/api/example?d=**2020-01-01**
```


### Using Schema

You can also use Schema to encapsulate GET parameters:

```python hl_lines="1 2  5 6 7 8"
{!./src/tutorial/query/code010.py!}
```

For more complex filtering scenarios please refer to [filtering](./filtering.md).

---

## File: docs/docs/guides/input/request-parsers.md

# Request parsers

In most cases, the default content type for REST API's is JSON, but in case you need to work with
other content types (like YAML, XML, CSV) or use faster JSON parsers, **Django Ninja** provides a `parser` configuration.

```python
api = NinjaAPI(parser=MyYamlParser())
```

To create your own parser, you need to extend the `ninja.parser.Parser` class, and override the `parse_body` method.


## Example YAML Parser

Let's create our custom YAML parser:

```python hl_lines="4 8 9"
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()


```

If you now send YAML like this as the request body:

```YAML
ints:
 - 0
 - 1
string: hello
f: 3.14
```

it will be correctly parsed, and you should have JSON output like this:


```JSON
{
  "ints": [
    0,
    1
  ],
  "string": "hello",
  "f": 3.14
}
```


## Example ORJSON Parser

[orjson](https://github.com/ijl/orjson#orjson) is a fast, accurate JSON library for Python. It benchmarks as the fastest Python library for JSON and is more accurate than the standard `json` library or other third-party libraries.

```
pip install orjson
```

Parser code:

```python hl_lines="1 8 9"
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())
```

---

## File: docs/docs/guides/response/config-pydantic.md

# Overriding Pydantic Config

There are many customizations available for a **Django Ninja `Schema`**, via the schema's
[Pydantic `model_config`](https://docs.pydantic.dev/latest/api/config/). 

!!! info
    Under the hood **Django Ninja** uses [Pydantic Models](https://pydantic-docs.helpmanual.io/usage/models/)
    with all their power and benefits. The alias `Schema` was chosen to avoid confusion in code
    when using Django models, as Pydantic's model class is called Model by default, and conflicts with
    Django's Model class.

## Example Camel Case mode

One interesting config attribute is [`alias_generator`](https://docs.pydantic.dev/latest/api/config/?query=alias_generator#pydantic.config.ConfigDict.alias_generator).
Using Pydantic's example in **Django Ninja** can look something like:

```python hl_lines="10"
from pydantic import ConfigDict
from ninja import Schema


def to_camel(string: str) -> str:
    words = string.split('_')
    return words[0].lower() + ''.join(word.capitalize() for word in words[1:])

class CamelModelSchema(Schema):
    model_config = ConfigDict(alias_generator=to_camel)
    str_field_name: str
    float_field_name: float
```

Keep in mind that when you want modify output for field names (like camel case) - you need to set as well  `populate_by_name` and `by_alias`

```python hl_lines="6 14"
from pydantic import ConfigDict

class UserSchema(ModelSchema):
    model_config = ConfigDict(
        alias_generator = to_camel
        populate_by_name = True,  # !!!!!! <--------
    )
    class Meta:
        model = User
        fields = ["id", "email", "is_staff"]



@api.get("/users", response=list[UserSchema], by_alias=True) # !!!!!! <-------- by_alias
def get_users(request):
    return User.objects.all()

```

results:

```JSON
[
  {
    "id": 1,
    "email": "tim@apple.com",
    "isStaff": true
  },
  {
    "id": 2,
    "email": "sarah@smith.com",
    "isStaff": false
  }
  ...
]

```