FastAPI: могу ли я также использовать Depends() для параметров в POST?

Обзор

Я создал зависимость на основе классов, похожую на ту, что описана в замечательном туториале по FastAPI.

Проблема

Это работает, за исключением того, что параметры в зависимости (часть Depends()) передаются как параметры запроса, что означает, что они являются частью URI/URL. Я использую зависимость на основе классов как средство упрощения доступа к Azure Datalake, чтобы параметры в зависимостях были хотя бы несколько секретными. Поэтому я бы предпочел, чтобы они были в части POST.

Вопрос

Есть ли способ использовать Depends(), но передавать параметры инициализации класса через полезную нагрузку POST, а не через URL-адрес?

Подробности

В качестве примера:

Класс зависимости (просто инициализация, которая фиксирует параметры зависимости):

class DatalakeConnection(object):
    """Using FastAPI's `Depends` Dependency Injection, this class can have all
    elements needed to connect to a data lake."""

    def __init__(
        self,
        dir: str = my_typical_folder,
        container: str = storage_container.value,
    ):
        service_client = DataLakeServiceClient(
            account_url=storage_uri,
            credential=storage_credential,
        )
        self.file_system_client = service_client.get_file_system_client(
            file_system=container
        )
        self.directory_client = self.file_system_client.get_directory_client(dir)
        self.file_client = None

Функция пути FastAPI:

@app.post("/datalake")  # I have no response model yet, but will add one
def predictions_from_datalake(
    query: schemas.Query, conn: DatalakeConnection = Depends()
):
    core_df = conn.read_excel(query.file_of_interest) # I create a DataFrame from reading Excel files

Краткое содержание

Как я уже сказал, это работает, но dir и container, необходимые для инициализации класса, принудительно добавляются в параметры запроса URL, но я бы хотел, чтобы они были парами ключ-значение в теле запроса POST:

Вы можете получить доступ к объекту Request внутри функции и что-то там сделать, но я не знаю, возможно ли это с классами. fastapi.tiangolo.com/advanced/using-request-directly/…

lsabi 10.12.2020 21:51
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
6 865
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете объявить их так же, как параметры тела операции пути. Подробнее здесь Единственное число в теле:

class DatalakeConnection(object):
    """Using FastAPI's `Depends` Dependency Injection, this class can have all
    elements needed to connect to a data lake."""

    def __init__(
            self,
            dir: str = Body("dir_default"),
            container: str = Body("container_default"),
    ):
        pass

Пример тела запроса:

{
  "dir": "string",
  "container": "string"
}

Если вы хотите использовать Depends с существующим классом, не обновляя значения по умолчанию для этого класса, вы можете создать функцию с правильной сигнатурой и передать ее в Depends.

def _body_dependify(model_cls):
    """
    Hack around fastapi not supporting Body(...) parameters in dependencies unless
    you specify them in the function signature.
    """
    import functools
    import inspect
    from collections import OrderedDict

    signature = inspect.signature(model_cls)
    signature = signature.replace(return_annotation=model_cls)
    parameters = OrderedDict(signature.parameters)
    for parameter_name in list(parameters):
        parameter = parameters[parameter_name]
        if parameter.default is inspect.Parameter.empty:
            parameter = parameter.replace(default=Body())
        else:
            parameter = parameter.replace(default=Body(parameter.default))
        parameters[parameter_name] = parameter
    signature = signature.replace(parameters=parameters.values())

    @functools.wraps(model_cls)
    def build(*args, **kwargs):
        return model_cls(*args, **kwargs)

    build.__signature__ = signature
    return Depends(build)

Затем в вашей конечной точке вы можете сделать:

@app.post("/datalake")  # I have no response model yet, but will add one
def predictions_from_datalake(
    query: schemas.Query, conn: DatalakeConnection = _body_dependify(DatalakeConnection)
):
    core_df = conn.read_excel(query.file_of_interest) # I create a DataFrame from reading Excel files

На странице /docs схема выглядит так:

Это также работает с моделями Pydantic, поскольку они устанавливают атрибут __signature__.

См. также обходной путь для ошибок проверки pydantic в Depends здесь: github.com/tiangolo/fastapi/issues/1474#issuecomment-1160633‌​178

Lucas Wiman 08.09.2022 03:52

Другие вопросы по теме