Skip to content

Commit 4804fe7

Browse files
committed
Added documentation for all input-parsing parameter
1 parent 08208dc commit 4804fe7

File tree

11 files changed

+688
-260
lines changed

11 files changed

+688
-260
lines changed

docs/overview/parsing-inputs/body.md

Lines changed: 229 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,65 +3,108 @@
33
Request bodies are typically used with “create” and “update” operations (POST, PUT, PATCH).
44
For example, when creating a resource using POST or PUT, the request body usually contains the representation of the resource to be created.
55

6-
To declare a **request body**, you need to use **Django Ninja `Schema`**.
6+
To declare a **request body**, you need to use **Ellar `Serializer`**.
77

88
!!! info
9-
Under the hood **Django Ninja** uses <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> models with all their power and benefits.
10-
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.
9+
Under the hood **Ellar** uses <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> models with all their power and benefits.
1110

12-
## Import Schema
11+
## Import Serializer
1312

14-
First, you need to import `Schema` from `ninja`:
13+
First, you need to import `Serializer` from `ella.serializer`:
14+
15+
```python
16+
# project_name/apps/items/controllers.py
17+
from ellar.serializer import Serializer
18+
19+
# class Item(Serializer):
20+
# name: str
21+
# description: str = None
22+
# price: float
23+
# quantity: int
1524

16-
```Python hl_lines="1"
17-
{!./src/tutorial/body/code01.py!}
1825
```
1926

2027
## Create your data model
2128

22-
Then you declare your data model as a class that inherits from `Schema`.
29+
Then you declare your data model as a class that inherits from `Serializer`.
2330

2431
Use standard Python types for all the attributes:
2532

26-
```Python hl_lines="4 5 6 7 8"
27-
{!./src/tutorial/body/code01.py!}
33+
```python
34+
# project_name/apps/items/controllers.py
35+
36+
from ellar.serializer import Serializer
37+
from ellar.common import Controller, post
38+
from ellar.core import ControllerBase
39+
40+
41+
class Item(Serializer):
42+
name: str
43+
description: str = None
44+
price: float
45+
quantity: int
46+
47+
48+
@Controller
49+
class ItemsController(ControllerBase):
50+
@post("/")
51+
def create(self, item: Item):
52+
return item
2853
```
2954

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

3358
```JSON
3459
{
35-
"name": "Katana",
60+
"name": "Alexis",
3661
"description": "An optional description",
37-
"price": 299.00,
38-
"quantity": 10
62+
"price": 400.00,
63+
"quantity": 6
3964
}
4065
```
4166

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

4469
```JSON
4570
{
46-
"name": "Katana",
47-
"price": 299.00,
48-
"quantity": 10
71+
"name": "Alexis",
72+
"price": 200.00,
73+
"quantity": 12
4974
}
5075
```
5176

5277
## Declare it as a parameter
5378

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

56-
```Python hl_lines="12"
57-
{!./src/tutorial/body/code01.py!}
81+
```python
82+
# project_name/apps/items/controllers.py
83+
84+
from ellar.serializer import Serializer
85+
from ellar.common import Controller, post
86+
from ellar.core import ControllerBase
87+
88+
89+
class Item(Serializer):
90+
name: str
91+
description: str = None
92+
price: float
93+
quantity: int
94+
95+
96+
@Controller
97+
class ItemsController(ControllerBase):
98+
@post("/")
99+
def create(self, item: Item):
100+
return item
58101
```
59102

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

62105
## Results
63106

64-
With just that Python type declaration, **Django Ninja** will:
107+
With just that Python type declaration, **Ellar** will:
65108

66109
* Read the body of the request as JSON.
67110
* Convert the corresponding types (if needed).
@@ -74,7 +117,7 @@ With just that Python type declaration, **Django Ninja** will:
74117
your models, and you can also use them anywhere else you like if it makes sense for your project.
75118
* Those schemas will be part of the generated OpenAPI schema, and used by the automatic documentation <abbr title="User Interfaces">UI's</abbr>.
76119

77-
## Automatic docs
120+
## Automatic Docs
78121

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

@@ -84,40 +127,189 @@ The JSON Schemas of your models will be part of your OpenAPI generated schema, a
84127

85128
![Openapi schema](../../img/body-schema-doc2.png)
86129

87-
## Editor support
88-
89-
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):
90-
91-
![Type hints](../../img/body-editor.gif)
92130

131+
## Request Body + Path parameters
93132

94-
The previous screenshots were taken with <a href="https://code.visualstudio.com" class="external-link" target="_blank">Visual Studio Code</a>.
133+
You can declare path parameters **and** body requests at the same time.
95134

96-
You would get the same editor support with <a href="https://www.jetbrains.com/pycharm/" class="external-link" target="_blank">PyCharm</a> and most of the other Python editors.
135+
**Ellar** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared with `Serializer` should be **taken from the request body**.
97136

137+
```python
138+
# project_name/apps/items/controllers.py
98139

99-
## Request body + path parameters
140+
from ellar.serializer import Serializer
141+
from ellar.common import Controller, post, put
142+
from ellar.core import ControllerBase
100143

101-
You can declare path parameters **and** body requests at the same time.
102144

103-
**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**.
145+
class Item(Serializer):
146+
name: str
147+
description: str = None
148+
price: float
149+
quantity: int
104150

105-
```Python hl_lines="11 12"
106-
{!./src/tutorial/body/code02.py!}
151+
@Controller
152+
class ItemsController(ControllerBase):
153+
@post("/")
154+
def create(self, item: Item):
155+
return item
156+
157+
@put("/items/{item_id}")
158+
def update(self, item_id: int, item: Item):
159+
return {"item_id": item_id, "item": item.dict()}
107160
```
108161

109-
## Request body + path + query parameters
162+
## Request Body + Path + Query parameters
110163

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

113-
**Django Ninja** will recognize each of them and take the data from the correct place.
166+
**Ellar** will recognize each of them and take the data from the correct place.
167+
168+
```python
169+
# project_name/apps/items/controllers.py
170+
171+
from ellar.serializer import Serializer
172+
from ellar.common import Controller, post, put
173+
from ellar.core import ControllerBase
114174

115-
```Python hl_lines="11 12"
116-
{!./src/tutorial/body/code03.py!}
175+
176+
class Item(Serializer):
177+
name: str
178+
description: str = None
179+
price: float
180+
quantity: int
181+
182+
@Controller
183+
class ItemsController(ControllerBase):
184+
@post("/")
185+
def create(self, item: Item):
186+
return item
187+
188+
@put("/{item_id}")
189+
def update(self, item_id: int, item: Item, q: str):
190+
return {"item_id": item_id, "item": item.dict(), "q": q}
117191
```
118192

119193
The function parameters will be recognized as follows:
120194

121195
* If the parameter is also declared in the **path**, it will be used as a path parameter.
122196
* If the parameter is of a **singular type** (like `int`, `float`, `str`, `bool`, etc.), it will be interpreted as a **query** parameter.
123-
* If the parameter is declared to be of the type of **Schema** (or Pydantic `BaseModel`), it will be interpreted as a request **body**.
197+
* If the parameter is declared to be of the type of **Serializer** (or Pydantic `BaseModel`), it will be interpreted as a request **body**.
198+
199+
!!! info
200+
In here, we have combined both `Serializers` and `Controllers` in one file. This is for the convenience of writing this documentation.
201+
It's advised to have all your serializers in `schemas.py` and then import them over to `controllers.py` if needed.
202+
203+
## Singular values in body
204+
The same way there is a `Query` and `Path` to define extra data for query and path parameters,
205+
**Ellar** provides an equivalent `Body`.
206+
207+
For example, extending the previous model, you could decide that you want to have another key `importance` in the same body, besides the `item` and `user`.
208+
209+
If you declare it as is, because it is a singular value, **Ellar** will assume that it is a query parameter.
210+
211+
But you can instruct **Ellar** to treat it as another body key using Body:
212+
213+
```python
214+
# project_name/apps/items/controllers.py
215+
216+
from ellar.serializer import Serializer
217+
from ellar.common import Controller, Body, post, put
218+
from ellar.core import ControllerBase
219+
from pydantic import BaseModel
220+
221+
222+
class Item(Serializer):
223+
name: str
224+
description: str = None
225+
price: float
226+
quantity: int
227+
228+
229+
class User(BaseModel):
230+
username: str
231+
full_name: str
232+
233+
234+
@Controller
235+
class ItemsController(ControllerBase):
236+
@post("/")
237+
def create(self, item: Item):
238+
return item
239+
240+
@put("/{item_id}")
241+
def update(self, item_id: int, item: Item, q: str):
242+
return {"item_id": item_id, "item": item.dict(), "q": q}
243+
244+
@put("/{item_id}/another")
245+
async def update_another(self, item_id: int, item: Item, user: User, importance: int = Body()):
246+
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
247+
return results
248+
```
249+
In this case, **Ellar** will expect a body like:
250+
251+
```json
252+
{
253+
"item": {
254+
"name": "Foo",
255+
"description": "The pretender",
256+
"price": 42.0,
257+
"tax": 3.2
258+
},
259+
"user": {
260+
"username": "dave",
261+
"full_name": "Dave Grohl"
262+
},
263+
"importance": 5
264+
}
265+
```
266+
267+
## Multiple body params and query
268+
Of course, you can also declare additional `query` parameters whenever you need, additional to anybody parameters.
269+
270+
As, by default, singular values are interpreted as query parameters, you don't have to explicitly add a `Query`, you can just do:
271+
272+
For example:
273+
274+
```python
275+
# project_name/apps/items/controllers.py
276+
277+
from ellar.serializer import Serializer
278+
from ellar.common import Controller, Body, post, put
279+
from ellar.core import ControllerBase
280+
from pydantic import BaseModel
281+
282+
283+
class Item(Serializer):
284+
name: str
285+
description: str = None
286+
price: float
287+
quantity: int
288+
289+
290+
class User(BaseModel):
291+
username: str
292+
full_name: str
293+
294+
295+
@Controller
296+
class ItemsController(ControllerBase):
297+
@post("/")
298+
def create(self, item: Item):
299+
return item
300+
301+
@put("/{item_id}")
302+
def update(self, item_id: int, item: Item, q: str):
303+
return {"item_id": item_id, "item": item.dict(), "q": q}
304+
305+
@put("/{item_id}/another")
306+
async def update_another(self, item_id: int, item: Item, user: User, importance: int = Body(), q: str = None):
307+
results = {"item_id": item_id, "item": item, "user": user, "importance": importance, "q": q}
308+
return results
309+
```
310+
And you will have this:
311+
312+
![Openapi schema](../../img/body-schema-doc3.png)
313+
314+
!!! info
315+
`Body` also has all the same extra validation and metadata parameters as `Query`,`Path` and others you will see later.

0 commit comments

Comments
 (0)