Python Type hints
Specify the parameter type and return type
>>> def demo1(first_name: str, last_name: str) - >str:
return first_name.title() + "" + last_name.title()
>>> demo1("wang"."haha")
'Wang Haha'
>>>
Copy the code
A type with subtypes
>>> from typing import List.Tuple.Dict
>>> def demo2(items: List[str]) :
for item in items:
print(item)
>>> demo2(["ryan"."king".24])
ryan
king
24
Copy the code
>>> def demo3(items: Tuple[int.str.int]) :
for item in items:
print(item, type(item))
>>> demo3((3."hello".89))
3 <class 'int'>
hello <class 'str'> < 89class 'int'>
Copy the code
>>> def demo4(info: Dict[str.str]) :
for k, v in enumerate(info):
print(k, v)
>>> demo4({"name": "ryan"."age": 24})
0 name
1 age
Copy the code
Class as a parameter type
>>> class Student:
def __init__(self, name) :
self.name = name
def getName(self) :
return self.name
>>> def printName(stu: Student) :
print(stu.getName())
>>> stu = Student("ryan")
>>> printName(stu)
ryan
Copy the code
Pydantic model
>>> from pydantic import BaseModel
>>> from typing import List
>>> from pydantic.error_wrappers import ValidationError
>>> class User(BaseModel) :
id: int
name = "ryan"
hobby: List[str]
>>> data = {
"id": [123]."name": "ryan"."hobby": ["Basketball"."Ball"."Milk"]}>>> try:
user = User(**data)
print(user)
print(user.id, user.name, user.hobby)
except ValidationError as e:
print(e)
1 validation error for User
id
value is not a valid integer (type=type_error.integer)
Copy the code
The installation
pip install fastapi
Copy the code
ASGI server
pip install uvicorn
Copy the code
HelloWorld
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root() :
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None) :
return {"item_id": item_id, "q": q}
Copy the code
Start the
uvicorn main:app --reload
Copy the code
Receive PUT request data
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel) :
name: str
price: float
is_offer: bool = None
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item) :
return {"item_name": item.name, "item_id": item_id}
Copy the code
Interactive API documentation
http://127.0.0.1:8000/docs
http://127.0.0.1:8000/redoc
Request method
@app.post()
@app.put()
@app.delete()
@app.options()
@app.head()
@app.patch()
@app.trace()
Copy the code
usually
POST: creates data. GET: reads data. PUT: updates dataCopy the code
The path parameter
from fastapi import FastAPI
app = FastAPI()
@app.get("/index/{id}")
async def index(id) :
return {"id": id}
Copy the code
http://127.0.0.1:8000/index/20
Path parameter with type
from fastapi import FastAPI
app = FastAPI()
@app.get("/index/{id}")
async def index(id: int) :
return {"id": id}
Copy the code
http://127.0.0.1:8000/index/25
Execution order
Because path operations are calculated sequentially, you need to make sure that the path to /users/me is declared before /users/{user_id}, otherwise the route will always match to /users/{user_id}
from fastapi import FastAPI
app = FastAPI()
@app.get('/users/me')
async def read_user_me() :
return {'user_id': '10010'}
@app.get('/users/{user_id}')
async def read_user(user_id: str) :
return {'user_id': user_id}
Copy the code
Enumeration parameter
Import Enum and create a subclass that inherits STR and Enum.
from fastapi import FastAPI
from enum import Enum
app = FastAPI()
# By inheriting from STR, the API document will know that the value must be of type string and render correctly.
class ModelName(str, Enum) :
a = 'ryan'
b = 'king'
c = 'ryan'
@app.get('/model/{model_name}')
async def get_model(model_name: ModelName) :
print('model_name', model_name)
print('key=', model_name.name)
print('val=', model_name.value)
return {model_name.name: model_name.value}
Copy the code
The path parameter contains the path
from fastapi import FastAPI
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str) :
return {"file_path": file_path}
Copy the code
http://127.0.0.1:8000/files/a/b/c/d/e/f/g
Query parameters
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10) :
print(skip)
print(limit)
Copy the code
http://127.0.0.1:8000/items/?skip=1&limit=3
Optional parameters
You can declare optional query parameters by setting their default value to None
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str = None) :
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
Copy the code
http://127.0.0.1:8000/items/5
Multiple paths and query parameters
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
user_id: int, item_id: str, q: str = None, short: bool = False
) :
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"})return item
Copy the code
Optional
from typing import Optional
limit: Optional[int] = None
Copy the code
from typing import Optional
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_user_item(item_id: str, limit: Optional[int] = None) :
item = {"item_id": item_id, "limit": limit}
return item
Copy the code
Request body
When you need to send data to your API from a client (simply a browser), what you send is called the request body
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel) :
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item) :
return item
Copy the code
Using the model
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel) :
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item) :
print(item)
print("json->", item.json())
print("dict->", item.dict())
print("Config->", item.Config)
print(item.dict().get("name"))
return item
Copy the code
Request body + path parameter
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel) :
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item) :
return {"item_id": item_id, **item.dict()}
Copy the code
Request body + path parameter + query parameter
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel) :
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: str = None) :
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
Copy the code
Functional parameters will be identified as follows:
- If this parameter is also declared in the path, it will be used as the path parameter.
- If the argument is of singular type (e.g
int
.float
.str
.bool
Etc.), which will be interpreted as a query parameter. - If the parameter is declared to be a Pydantic model, it will be interpreted as the request body.
Validation of query parameters and strings
Additional validation
Limit the length of query parameters
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/test")
async def test(item: str = Query(None, max_length=20, min_length=5)) :
return item
Copy the code
When we replace Query(None) with the default value None, the first argument to Query serves the same purpose as defining the default value.
q: str = Query(None)
Copy the code
Is equivalent to
q: str = None
Copy the code
Matches regular expression parameters
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/test")
async def test(item: str = Query(None, max_length=20, min_length=5, regex='^w.*a$')) :
return item
Copy the code
http://127.0.0.1:8000/test?item=wanghaha
The default value
Having a default value also makes this parameter optional.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/test")
async def test(item: str = Query('wanghaha', max_length=20, min_length=5, regex='^w.*a$')) :
return item
Copy the code
http://127.0.0.1:8000/test
When we declare a value using Query, you can use… As the first parameter, it lets FastAPI know that this parameter is required.
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(. , min_length=3)) :
print(q)
return q
Copy the code
Query parameter list/multiple values
from fastapi import FastAPI, Query
from typing import List
app = FastAPI()
@app.get("/items/")
async def read_items(q: List[str] = Query(None)) :
query_items = {"q": q}
return query_items
Copy the code
http://127.0.0.1:8000/items/?q=abc&q=defaazv&q=23
Query parameter list/Multiple values Default values
from fastapi import FastAPI, Query
from typing import List
app = FastAPI()
@app.get("/items/")
async def read_items(q: List[str] = Query(["foo"."bar"])) :
query_items = {"q": q}
return query_items
Copy the code
http://127.0.0.1:8000/items/
Do not specify a list subelement type
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: list = Query(None)) :
query_items = {"q": q}
return query_items
Copy the code
Declare more original information
Add the title, description
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items1/")
async def read_items(q: str = Query(None, title="Query string", min_length=3)) :
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}}]if q:
results.update({"q": q})
return results
@app.get("/items2/")
async def read_items(
q: str = Query(
None,
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3.)
) :
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}}]if q:
results.update({"q": q})
return results
Copy the code
Parameters of the alias
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(None, alias="item-query")) :
print(q)
return q
Copy the code
http://127.0.0.1:8000/items/?item-query=hello
Deprecated parameters
Now you don’t want to see this parameter anymore.
You must keep it for a while because many customers use it, but you want the documentation to show it clearly as deprecated.
The deprecated = True parameter is then passed to Query
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/items/")
async def read_items(
q: str = Query(
None,
deprecated=True.)
) :
print(q)
return q
Copy the code
http://127.0.0.1:8000/docs#/default/read_items_items__get
Path parameters and numerical validation
Parameters in ascending order
Python won’t do anything with *, but it will know that all of the following arguments should be called keyword arguments (key/value pairs), also known as kwargs. Even if they don’t have default values.
from fastapi import FastAPI, Query, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*, item_id: int = Path(. , title="The ID of the item to get"), q: str
) :
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
Copy the code
Numerical verification: Greater than or equal to
from fastapi import FastAPI, Query, Path
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
*, item_id: int = Path(. , title="The ID of the item to get", ge=1), q: str
) :
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
Copy the code
http://127.0.0.1:8000/items/3?q=hello
gt
: more thanle
: Less than or equal tolt
: less than
The Body more parameters
from fastapi import FastAPI, Path
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel) :
name: str
description: str = None
price: float
tax: float = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int = Path(. , title="The ID of the item to get", ge=0, le=1000),
q: str = None,
item: Item = None.) :
results = {"item_id": item_id}
if q:
results.update({"q": q})
if item:
results.update({"item": item})
return results
Copy the code
Multiple request parameters
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel) :
name: str
description: str = None
price: float
tax: float = None
class User(BaseModel) :
username: str
full_name: str = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User) :
results = {"item_id": item_id, "item": item, "user": user}
return results
Copy the code
Request body
{
"item": {
"name": "Foo"."description": "The pretender"."price": 42.0."tax": 3.2
},
"user": {
"username": "dave"."full_name": "Dave Grohl"}}Copy the code
The Body instructs FastAPI to treat it as another principal key
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel) :
name: str
description: str = None
price: float
tax: float = None
class User(BaseModel) :
username: str
full_name: str = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: int = Body(.)
) :
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results
Copy the code
Expected request body
{
"item": {
"name": "Foo"."description": "The pretender"."price": 42.0."tax": 3.2
},
"user": {
"username": "dave"."full_name": "Dave Grohl"
},
"importance": 5
}
Copy the code
The Body also has the same additional validation and metadata parameters as Query, Path, and all the others you’ll see later.
Embed a single principal parameter
Suppose you have a single body parameter in the Pydantic model Item.
By default, FastAPI will directly expect its body.
However, if you want it to expect a JSON with a key item and include the contents of the model in it, as when declaring additional Body parameters, you can use the special Body embed parameter:
item: Item = Body(... , embed=True)
Copy the code
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel) :
name: str
description: str = None
price: float
tax: float = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(. , embed=True)) :
results = {"item_id": item_id, "item": item}
return results
Copy the code
Expected request body
{
"item": {
"name": "Foo"."description": "The pretender"."price": 42.0."tax": 3.2}}Copy the code
Rather than
{
"name": "Foo"."description": "The pretender"."price": 42.0."tax": 3.2
}
Copy the code
The Body field
Notional model attribute
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel) :
name: str
description: str = Field(None, title="The description of the item", max_length=300)
price: float= Field(... , gt=0, description="The price must be greater than zero")
tax: float = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(. , embed=True)) :
results = {"item_id": item_id, "item": item}
return results
Copy the code
Field works the same as Query, Path, and Body; it has all the same parameters, and so on.
In fact, next you’ll see Query, Path, and other objects that create objects that are subclasses of the common Param class, which itself is a subclass of Pydantic’s FieldInfo class. And Pydantic’s Field also returns an instance of FieldInfo. The Body also returns the object of the FieldInfo subclass directly. Some of the others you’ll see later are subclasses of the Body class. Remember that when you import Query, Path, and so on from FastAPI, these are actually functions that return special classes.
Note how the properties of each model with type, default values, and Field have the same structure as the parameters of the Path manipulation function, using fields instead of Path, Query, and Body.
You can use Pydantic’s Field to declare additional validation and metadata for model properties.
You can also use the extra keyword argument to pass additional JSON Schema metadata.