FastAPI基础:Depends怎么用?

目录

 

前言

 

一、Depends的是干什么的?

二、Depends的两种用法

1、Depends(function)形式

2、Depends(class)形式

三、拓展


前言

FastAPI真的是个非常好用的东西。首先它是异步,但是我想说的是Fast API对于保证数据交互传递过程中的一致性,保持的非常好。

当然,能做到这一点也是因为它站在了巨人肩膀上,因为它的schema是依靠pydantic来做的。

大家用Fast API的过程中会越来越发现,Fast API实现的东西并不多,它大部分的功能都是基于其他工具的(如pydantic、startllete)。

今天想记录一下Fast API中实现的一个非常好用的工具——Depends,它能帮我们减少很多的代码。当然,我也是看官方文档然后结合自己的理解来写这篇文章,小伙伴们如果有精力还是建议去看官方文档,Fast API的官方文档写的很通俗易懂的。

 

一、Depends的是干什么的?

我们知道Fast API对于路径参数(path parameter)、请求参数(Query parameters)、请求体参数(body parameters)有很明确的区别。简单的说一下,

  • path parameters       也就是你在路径里怎么传参,如 www.baidu.com/image/baike?id=001  这种,你想把image作为参数传,那么你要写成 www.baidu.com/{type}/
  • Query parameters    可以当成是get请求中的查询参数,如 www.baidu.com/image/baike?id=001  ,其中id=001 就是查询参数
  • body parameters      默认是json形式传参,用于post请求中的请求体,使用继承BaseModel的类进行schema

而我们今天要说的Depends,其实就是对于Query parameters的schema

为什么要Depends呢?大家看完fastAPI 用于post请求的body parameters的schema参数,是不是觉得这种schema方式很好,只要提供一个类,就可以完成传参的校验,并且还可以在Fast API的docs可视化文档中形成参数提示。也增加了代码的可重复利用性,减少了代码冗余。

那么,类比过来,对于get请求的Query parameters,我们是不是也希望有这么一种方式,可以写一个类,然后就可以自动解析url中的参数,并且进行schema,还在docs文档中生成提示?

对了,Depends可以完美完成,并且Depends还不止这些作用,还可以用来提供数据库连接、外部api连接等等,我们后面接着看吧。

 

二、Depends的两种用法

1、Depends(function)形式

记住一点:我们管Depends这种方式,叫做 Dependency Injection。当然内部Fastapi做了很多工作。但是请记住Dependency Injection,对于我们理解很有用处。

引用官方文档一个例子,如下:

from typing import Optional

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


if __name__ == '__main__':
    uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")

 

我们看到了Depends的使用,它接收了一个参数,是一个function。这个function里定义了一些参数。那么我们运行起来,看看这个接口有什么不同?打开http://127.0.0.1:8080/docs连接,查看可视化文档。

我们找到了这个接口,看看它的chema有什么?

 

多了三个参数query参数 q、skip、limit,那么他们是从哪里来的呢? 

没错,就是从Depends(common_parameters)这里来的。并且,由于我们设置了默认值,所以是否对q、skip、limit传参是可选的。我们点击Execute,如下:

 

看Request URL,证明了是把 Depends(common_parameters)当成Query parameters来使用的。

那么参数被怎么使用了呢?这个你就不用担心了,因为FastAPI已经帮你做了参数解析,什么意思呢?还记得:commons: dict = Depends(common_parameters)这句话吧,

意思就是:当有人访问http://127.0.0.1:8080/items?skip=0&limit=100这种地址时,后台会自动将自动把skip=0&limit=100这段解析出来,并赋值给commons参数,由于function返回的是一个dict,所以commons也是一个dict,我们就可以通过common.get(limit)得方式去访问这些传入的参数了。当然function也可以返回tuple、list等形式,那么commons就是对应的类型。

那么,可以像这种形式访问吗? http://127.0.0.1:8080/items  答案是可以的。为什么,因为我们在function: common_parameters 中都给参数设置了默认参数,所以可以使用默认参数。下面我们来验证一下,使用浏览器访问http://127.0.0.1:8080/items,如下所示:

 

正确的返回。

 

那么,如果我在function中设置一个必传参数呢?如下代码。首先我们看一下docs可视化文档有什么变化。如下,多了一个required的参数desc,也就是说这个是必须输入的。

那我们使用浏览器访问http://127.0.0.1:8080/items ,我就是不传desc,你能把我咋滴。如下,报错了,说的是没有传递desc参数。

 

 

那我给desc传递一个str类型的参数呢?报错了,提示说要传bool值。

 

通过以上的分析,我们就很明确Depends的方式,诠释了Dependency Injection的含义。真的是很好用。今后有时间了要看看Depends的源码,学习一下别人的实现方式,再补充到这篇文章。

段官方文档说明下Depends为什么可以完成注入,主要是以下3个步骤:

  • 首先查看是否有dependence的使用,如果有,调用 depedency function,从URL中解析出要使用的参数传给 depedency function
  • 获取depedency function的result,也就是return的值
  • 将获取的dict值注入到 路径参数 中

以上三步,也说明了为什么我们写在Depends(function)里的参数可以进入到路径参数中。

所以后台得到的路径参数是,前端传递的url路径参数,经过Depends加工后传递给后台路径函数的。

 

另外,Depends(function) 的function要写成同步的还是异步的,其实都可以。

 

2、Depends(class)形式

其实使用Depends(function)这种形式有一些缺陷。这里再贴一下我们前面的代码,使用Depends(function)形式。从可读性角度,这种方式适合于参数比较容易被理解其含义的情景。

为什么?因为这种形式ID E并不能给你提供提示(hint)。

from typing import Optional

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons


if __name__ == '__main__':
    uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")

 

而class形式的传参其实是对function的一种升级,为什么这么说呢?,比如,我要实现一个参数相对复杂的Depends,比如Person,有name,age,height、sex等等。那么使用class的形式就比较好,会有hint提示,并且还可以使用 属性访问形式来访问元素。

但是,大家有没有想过,为什么Depends接收class也行呢?

官网上说:Depends可以接收任何callable的对象。 但是是不是落下一点,你这个callable对象得有返回值吧?就像我们上面的common_parameters函数一样。我们可以试试没有返回值,是什么样的。代码如下。

from typing import Optional

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
    pass


@app.get("/items/")
async def read_items(commons= Depends(common_parameters)):
    return commons


if __name__ == '__main__':
    uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")

 

查看docs,我们发现校验的参数不变,唯一变化的就是async def read_items(commons= Depends(common_parameters))  里面的commons是None。能想通,因为commons得到的是Depends(function)中function的返回值嘛。你返回None,可不commons就为None了。

所以Dpends接收的cllable对象有没有返回值都行。

 

但是这样commons这个参数就没意义了不是,所以我认为,从可用的角度上改成下面这句话:

” Depends接收任何有返回值的callable对象 “  比较合适。

 

ok,我们使用Depends(class)的形式实现一把,看看有啥发现,代码如下:

import uvicorn
from fastapi import Depends, FastAPI
from typing import Optional

from pydantic import BaseModel

app = FastAPI()



class CommonQueryParams:
    def __init__(self, q: Optional[str] = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get("/items")
async def read_items(common=Depends(CommonQueryParams)):
    return common


if __name__ == '__main__':
    uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")

 

我们来看看docs有啥变化不,如下。可以正常访问,并没有啥变化。

 

但是,从后台使用common时,common就时一个CommonQueryParams的类型了。并且可以使用common.limit的这种属性访问形式访问元素了。

 

三、拓展

那么,大家有没有认真想过这句话, “的Depends可以接收任何的callable对象”。

为什么function和class都可以作为Depends参数,因为他们可以调用,即他们可以写成A()的形式。那么理论上,一个拥有__call__方法的对象也是可以传入Depends的。我们来试试。代码如下

import uvicorn
from fastapi import Depends, FastAPI
from typing import Optional

from pydantic import BaseModel

app = FastAPI()


class CombineWord:
    def __init__(self, q: Optional[str] = None):
        self.q = q

    def __call__(self):
        return self.q + "world"


@app.get("/items")
async def read_items(word=Depends(CombineWord(q="hello"))):
    return word


if __name__ == '__main__':
    uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")

 

运行起来后,我们从docs来访问,如下,运行成功,并且返回了要拼写的字符串。

所以,大家可以根据自己的需要来给Depends传参,非常灵活。

 

四、实际项目中的应用-Depends(database_connection)

在实际项目中,后端开发时,用到的比较多的Depends比较多的是3个场景:

  • Depends(current_user)   在路由函数中,经常需要知道当前的用户是谁,用一个def get_current_user()方法实现获取,然后Depends(current_user)
  • Depends(database)    创建数据库session,特别常用,使用def connecton()获取数据库session,然后Depends(connection),看举例代码
  • Depends(common_params) 有一些公共参数,可以提取出来,然后使用Depends(common_params)

1、Depends(databse)来获取数据库seesion

# db.py用于初始化数据库
# 执行db.init(config)

import logging
from urllib.parse import quote_plus

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session

context = {
    "db_url": None,
    "engine": None,
    "smaker": None
}

scoped_context = {
    "db_url": None,
    "engine": None,
    "smaker": None
}

logs = logging.getLogger("sqlalchemy")


def init(config):
    db_url, engine, smaker = make_context(config)
    logs.debug("try to crate all database objects")
    Base.metadata.create_all(engine)  # 创建所有数据表

    context["db_url"] = db_url
    context["engine"] = engine
    context["smaker"] = smaker

    scoped_context["db_url"] = db_url
    scoped_context["engine"] = engine
    scoped_context["smaker"] = scoped_session(smaker)


def make_context(c):
    logs.debug("database config: %s", c)

    db_url = make_url(c)
    engine = create_engine(
        db_url,
        encoding=c.get("encoding" "utf8"),
        echo=c.get("echo", False),
        echo_pool=c.get("echo_pool", False),
        pool_recycle=c.get("pool_recycle", 3600),
        pool_size=c.get("(pool_size", 256)
    )

    smaker = sessionmaker(bind=engine, expire_on_commit=c.get("expire_on_commit", False))
    logs.debug("create session maker")

    return db_url, engine, smaker


def make_url(c):
    protocol = c["protocol"]
    username = c["username"]
    password = c["password"]
    password = quote_plus(password)
    host = c["host"]
    port = c["port"]
    database = c["database"]

    template = "{}://{}:{}@{}:{}/{}"
    db_url = template.format(protocol, username, password, host, port, database)
    url_tip = template.format(protocol, username, "XXXXXX", host, port, database)
    logs.debug(f"create db engine to {url_tip}")

    return db_url


def make_session(ctx=None, scoped=False):
    c = ctx if ctx is not None else (scoped_context if scoped else context)
    m = c["smaker"]
    s = m()
    return s


@contextlib.contextmanager
def session_context(ctx=None, scoped=False):
    session = make_session(ctx, scoped)
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.colse()


def connection():
    with session_context() as session:
        yield session
import uvicorn
from fastapi import Depends, FastAPI

app = FastAPI()


@app.get("/items")
async def read_items(database=Depends(connection)):
    # 这里的connection就是一个mysql的session连接了
    data = database.query("selext xxx from yyy")
    return data


@app.get("/users")
async def read_items(database=Depends(connection)):
    # 这里的connection就是一个mysql的session连接了
    data = database.query("selext xxx from user")
    return data


if __name__ == '__main__':
    uvicorn.run(app, host="0.0.0.0", port=8080, loop="asyncio")

每次需要使用数据库操作时,就很方便了。

 

2、Depends(current_user) 以后再举例子吧

 

 

 

<think>好的,我现在要处理用户关于在FastAPI中使用async和fastapi_pagination库的详细指南请求。首先,我需要回顾用户提供的引用内容,看看有哪些相关信息可以利用。 用户提到了引用[2]和[3],其中引用[2]是关于fastapi-pagination库的快速入门教程,里面有一个示例代码,展示了如何集成分页功能到FastAPI应用中,尤其是与SQLAlchemy结合使用。引用[3]则讨论了使用依赖注入处理分页和过滤逻辑,提到了common_parameters这样的依赖函数。 用户的需求是详细的使用指南,包括async和fastapi_pagination的结合使用。需要提供示例代码和分步说明。首先,我需要确认fastapi_pagination库是否支持异步操作,根据引用[2]中的示例,他们使用了AsyncSession,所以应该是支持的。 接下来,我需要组织回答的结构。可能需要包括安装步骤、基础配置、分页类型、自定义分页参数、与数据库的集成(比如SQLAlchemy异步)、依赖注入的应用,以及示例代码。同时,要确保所有代码示例正确使用async/await,并符合FastAPI的异步框架。 还需要注意用户提到的系统级指令,比如数学公式的格式,但在这个问题中可能不太涉及数学公式,不过还是要注意其他格式要求,如代码块使用正确的语法,引用标识的添加等。 可能需要引用用户提供的引用内容中的代码示例,比如引用[2]中的分页示例和引用[3]中的依赖注入方法,将它们结合起来展示如何在异步环境下使用fastapi_pagination。 另外,需要检查是否有其他可能的优化或注意事项,比如性能问题,或者常见的错误处理,比如分页参数验证,确保用户能够顺利实现功能。 最后,生成相关问题,可能需要围绕fastapi_pagination的高级用法、性能优化、与其他库的集成等展开。</think>以下是如何在 FastAPI 中结合 async 和 fastapi_pagination 库的详细指南,包含完整示例和关键实现步骤: --- ### 一、环境准备 1. 安装依赖 ```bash pip install fastapi fastapi-pagination[async] sqlalchemy[asyncio] uvicorn ``` 2. 基础配置 ```python from fastapi import FastAPI from fastapi_pagination import add_pagination, Page app = FastAPI() add_pagination(app) # 必须调用以启用分页功能 ``` --- ### 二、异步分页实现 #### 1. 基础分页类型 ```python from fastapi_pagination import Page, Params from fastapi_pagination.ext.async_sqlalchemy import paginate @app.get("/items/", response_model=Page[ItemSchema]) async def get_items(params: Params = Depends()): query = select(Item) # SQLAlchemy 异步查询 return await paginate(async_db_session, query, params) # 自动分页处理 ``` #### 2. 自定义分页参数 ```python from pydantic import BaseModel class CustomParams(BaseModel): category: str | None = None page: int = 1 size: int = 20 @app.get("/custom-items/") async def custom_pagination(params: CustomParams = Depends()): query = select(Item).where(Item.category == params.category) return await paginate( async_db_session, query, Params(page=params.page, size=params.size) ) ``` --- ### 三、与 SQLAlchemy 异步集成 #### 1. 数据库配置 ```python from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import sessionmaker DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname" engine = create_async_engine(DATABASE_URL) async_session = sessionmaker( engine, class_=AsyncSession, expire_on_commit=False ) async def get_db(): async with async_session() as session: yield session ``` #### 2. 完整路由示例 ```python from fastapi_pagination import Page from sqlalchemy import select @app.get("/products/", response_model=Page[ProductSchema]) async def list_products( db: AsyncSession = Depends(get_db), params: Params = Depends() ): query = select(Product).order_by(Product.created_at.desc()) return await paginate(db, query, params) ``` --- ### 四、高级功能实现 #### 1. 依赖注入分页参数 ```python from fastapi_pagination import Params from typing import Annotated PageParams = Annotated[Params, Depends()] @app.get("/users/") async def list_users(params: PageParams, db: AsyncSession = Depends(get_db)): return await paginate(db, select(User), params) ``` #### 2. 自定义响应格式 ```python from fastapi_pagination import create_page @app.get("/custom-response/") async def custom_response(params: Params = Depends()): items = await fetch_data(params) # 自定义异步数据获取 total = await count_data() return create_page(items, total, params) ``` --- ### 五、性能优化建议 1. 使用 `with_options` 预编译查询: ```python from fastapi_pagination import paginate optimized_paginate = paginate.with_options( transformer=lambda rows: [process_row(row) for row in rows] ) @app.get("/optimized/") async def optimized_route(params: Params = Depends()): return await optimized_paginate(db, complex_query, params) ``` 2. 启用分页缓存: ```python from fastapi_pagination import add_pagination app = FastAPI() add_pagination(app, enable_cache=True) # 缓存分页参数解析逻辑 ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值