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.gint.float.str.boolEtc.), 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 than
  • le: Less than or equal to
  • lt: 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.