]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
:memo: Add request body tutorial
authorSebastián Ramírez <tiangolo@gmail.com>
Fri, 14 Dec 2018 11:30:43 +0000 (15:30 +0400)
committerSebastián Ramírez <tiangolo@gmail.com>
Fri, 14 Dec 2018 11:30:43 +0000 (15:30 +0400)
docs/img/tutorial/body/image01.png [new file with mode: 0644]
docs/img/tutorial/body/image02.png [new file with mode: 0644]
docs/img/tutorial/body/image03.png [new file with mode: 0644]
docs/img/tutorial/body/image04.png [new file with mode: 0644]
docs/img/tutorial/body/image05.png [new file with mode: 0644]
docs/tutorial/body.md [new file with mode: 0644]
docs/tutorial/query-params.md
docs/tutorial/src/all/tutorial022.py
docs/tutorial/src/query-params/tutorial005.py
fastapi/dependencies/utils.py
mkdocs.yml

diff --git a/docs/img/tutorial/body/image01.png b/docs/img/tutorial/body/image01.png
new file mode 100644 (file)
index 0000000..819bf9c
Binary files /dev/null and b/docs/img/tutorial/body/image01.png differ
diff --git a/docs/img/tutorial/body/image02.png b/docs/img/tutorial/body/image02.png
new file mode 100644 (file)
index 0000000..27a683f
Binary files /dev/null and b/docs/img/tutorial/body/image02.png differ
diff --git a/docs/img/tutorial/body/image03.png b/docs/img/tutorial/body/image03.png
new file mode 100644 (file)
index 0000000..1301aaf
Binary files /dev/null and b/docs/img/tutorial/body/image03.png differ
diff --git a/docs/img/tutorial/body/image04.png b/docs/img/tutorial/body/image04.png
new file mode 100644 (file)
index 0000000..f6c25c1
Binary files /dev/null and b/docs/img/tutorial/body/image04.png differ
diff --git a/docs/img/tutorial/body/image05.png b/docs/img/tutorial/body/image05.png
new file mode 100644 (file)
index 0000000..03a98a5
Binary files /dev/null and b/docs/img/tutorial/body/image05.png differ
diff --git a/docs/tutorial/body.md b/docs/tutorial/body.md
new file mode 100644 (file)
index 0000000..72a2879
--- /dev/null
@@ -0,0 +1,131 @@
+To declare a request body, you use <a href="https://pydantic-docs.helpmanual.io/" target="_blank">Pydantic</a> models with all their power and benefits.
+
+## Import Pydantic's `BaseModel`
+
+First, you need to import `BaseModel` from `pydantic`:
+
+```Python hl_lines="2"
+{!./tutorial/src/body/tutorial001.py!}
+```
+
+## Create your data model
+
+Then you declare your data model as a class that inherits from `BaseModel`.
+
+Use standard Python types for all the attributes:
+
+```Python hl_lines="5 6 7 8 9"
+{!./tutorial/src/body/tutorial001.py!}
+```
+
+The same as when declaring query parameters, when a model attribute has a default value, it is not required. Otherwise, it is required. Use `None` to make it just optional.
+
+For example, this model above declares a JSON "`object`" (or Python `dict`) like:
+
+```JSON
+{
+    "name": "Foo",
+    "description": "An optional description",
+    "price": 45.2,
+    "tax": 3.5
+}
+```
+
+...as `description` and `tax` are optional (with a default value of `None`), this JSON "`object`" would also be valid:
+
+```JSON
+{
+    "name": "Foo",
+    "price": 45.2
+}
+```
+
+## Declare it as a parameter
+
+To add it to your endpoint, declare it the same way you declared path and query parameters:
+
+```Python hl_lines="16"
+{!./tutorial/src/body/tutorial001.py!}
+```
+
+...and declare its type as the model you created, `Item`.
+
+## Results
+
+With just that Python type declaration, **FastAPI** 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 clear error, indicating exactly where and what was the incorrect data.
+* Give you the received data in the parameter `item`.
+    * As you declared it in the function to be of type `Item`, you will also have all the editor support (completion, etc) for all of the attributes and their types.
+* Generate <a href="http://json-schema.org" target="_blank">JSON Schema</a> definitions for your model, 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">UIs</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:
+
+<img src="/img/tutorial/body/image01.png">
+
+And will be also used in the API docs inside each endpoint that needs them:
+
+<img src="/img/tutorial/body/image02.png">
+
+## Editor support
+
+In your editor, inside your function you will get type hints and completion everywhere (this wouldn't happen if your received a `dict` instead of a Pydantic model):
+
+<img src="/img/tutorial/body/image03.png">
+
+You also get error checks for incorrect type operations:
+
+<img src="/img/tutorial/body/image04.png">
+
+This is not by chance, the whole framework was built around that desing.
+
+And it was thoroughly tested at the design phase, before any implementation, to ensure it would work with all the editors.
+
+There were even some changes to Pydantic itself to support this.
+
+The previous screenshots were taken with <a href="https://code.visualstudio.com" target="_blank">Visual Studio Code</a>.
+
+But you would get the same editor support with <a href="https://www.jetbrains.com/pycharm/" target="_blank">PyCharm</a> and most of the other Python editors:
+
+<img src="/img/tutorial/body/image05.png">
+
+
+## Use the model
+
+Inside of the function, you can access all the attributes of the model object directly:
+
+```Python hl_lines="19"
+{!./tutorial/src/body/tutorial002.py!}
+```
+
+## Request body + path parameters
+
+You can declare path parameters and body requests at the same time.
+
+**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**.
+
+```Python hl_lines="15 16"
+{!./tutorial/src/body/tutorial003.py!}
+```
+
+## Request body + path + query parameters
+
+You can also declare **body**, **path** and **query** parameters, all at the same time.
+
+**FastAPI** will recognize each of them and take the data from the correct place.
+
+```Python hl_lines="16"
+{!./tutorial/src/body/tutorial004.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 a **Pydantic model**, it will be interpreted as a request **body**.
\ No newline at end of file
index e58fe2749f2067dbf41f95ce67b582deffe23c44..6109cb74211ff6af0e50f88c256de10a4b4f55a6 100644 (file)
@@ -131,7 +131,7 @@ If you don't want to add a specific value but just make it optional, set the def
 
 But when you want to make a query parameter required, you can just do not declare any default value:
 
-```Python hl_lines="6 8"
+```Python hl_lines="6 7"
 {!./tutorial/src/query-params/tutorial005.py!}
 ```
 
index 3f4f53265dff6c2606e706224d9d8049285b3b51..1165fd7a05642f8d59353787bc0afb2e0539f07e 100644 (file)
@@ -1,5 +1,5 @@
 from fastapi import Body, FastAPI
-from pydantic import BaseModel, Schema
+from pydantic import BaseModel
 
 app = FastAPI()
 
index 62da63fc75d61888ddc4eb5f0163cf1c6d121327..0979ad390dd0e7a57ca8ddb399be3c65825f61d1 100644 (file)
@@ -4,8 +4,6 @@ app = FastAPI()
 
 
 @app.get("/users/{user_id}/items/{item_id}")
-async def read_user_item(
-    user_id: int, item_id: str, needy: str
-):
+async def read_user_item(user_id: int, item_id: str, needy: str):
     item = {"item_id": item_id, "owner_id": user_id, "needy": needy}
     return item
index d757f6b1feefe903e9235d4ed7f405d96ba2640f..ba1e5efee553251b706153a81fe57e512d19501a 100644 (file)
@@ -254,7 +254,11 @@ def request_params_to_args(
         if value is None:
             if field.required:
                 errors.append(
-                    ErrorWrapper(MissingError(), loc=(schema.in_.value, field.alias), config=BaseConfig)
+                    ErrorWrapper(
+                        MissingError(),
+                        loc=(schema.in_.value, field.alias),
+                        config=BaseConfig,
+                    )
                 )
             else:
                 values[field.name] = deepcopy(field.default)
index 1e4b9000a27566c83d43f030e2eccca8b55d42e7..089fe949257481409794c6f5a2fc3a3164faaeb2 100644 (file)
@@ -18,9 +18,10 @@ nav:
     - Features: 'features.md'
     - Tutorial:
         - Tutorial Intro: 'tutorial/intro.md'
-        - First steps: 'tutorial/first-steps.md'
+        - First Steps: 'tutorial/first-steps.md'
         - Path Parameters: 'tutorial/path-params.md'
         - Query Parameters: 'tutorial/query-params.md'
+        - Request Body: 'tutorial/body.md'
     - Concurrency and async / await: 'async.md'
     - Deployment: 'deployment.md'