Как определить параметры запроса, используя модель Pydantic в FastAPI?

Я пытаюсь иметь конечную точку, например /services?status=New

status будет либо New, либо Old

Вот мой код:

from fastapi import APIRouter, Depends
from pydantic import BaseModel
from enum import Enum

router = APIRouter()

class ServiceStatusEnum(str, Enum):
    new = "New"
    old = "Old"


class ServiceStatusQueryParam(BaseModel):
    status: ServiceStatusEnum


@router.get("/services")
def get_services(
  status: ServiceStatusQueryParam = Query(..., title = "Services", description = "my desc"),
):
    pass #my code for handling this route.....

В результате я получаю ошибку, которая, кажется, имеет отношение к этой проблеме здесь

Ошибка говорит AssertionError: Param: status can only be a request body, using Body()


Затем я нашел другое решение, объясненное здесь.

Итак, мой код будет таким:

from fastapi import APIRouter, Depends
from pydantic import BaseModel
from enum import Enum

router = APIRouter()

class ServiceStatusEnum(str, Enum):
    new = "New"
    old = "Old"


class ServicesQueryParam(BaseModel):
    status: ServiceStatusEnum


@router.get("/services")
def get_services(
  q: ServicesQueryParam = Depends(),
):
    pass #my code for handling this route.....

Работает (и не пойму почему) - но вопрос как и куда добавить описание и заголовок?

Я не понимаю цели ServiceStatusQueryParam. Почему бы просто не аннотировать параметр status вашего маршрута напрямую с помощью ServiceStatusEnum. Это будет работать. Вам действительно нужен весь объект JSON в параметре запроса URL? Мне кажется, это супер съеживается.

Daniil Fajnberg 12.04.2023 20:27

Я не совсем понимаю разницу, потому что я новичок в fastapi, но вы, похоже, правы. Я тестировал, и он работает

Amin Ba 12.04.2023 20:35

Как было в вашем первом фрагменте кода, параметр запроса URL теоретически должен быть ?status = {"status":"New"} или что-то в этом роде, потому что вы устанавливаете тип параметра запроса status как вашу модель ServiceStatusQueryParam, которая, в свою очередь, сериализуется в объект JSON. . Принимая во внимание, что вы просто хотите, чтобы ваш запрос был ?status=New, то есть, по сути, строкового типа, но ограничен элементами перечисления.

Daniil Fajnberg 12.04.2023 20:39

что, если я хочу выполнить некоторую проверку строки? как бы я это сделал?

Amin Ba 12.04.2023 20:44
Почему в 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
4
501
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Чтобы создать модель Pydantic и использовать ее для определения параметров запроса, вам нужно будет использовать Depends() в параметре вашей конечной точки. Чтобы добавить description, title и т. д. для параметров запроса, вы можете обернуть Query() в Field().

Я также хотел бы отметить, что вместо Literal можно использовать тип Enum, как описано здесь и здесь . Кроме того, если кто-то хочет определить поле List внутри модели Pydantic и использовать его в качестве параметра запроса, ему нужно будет либо реализовать это в отдельном классе зависимостей, как показано здесь и здесь, либо снова заверните Query() в Field(), как показано ниже.

Более того, чтобы выполнить проверку параметров запроса внутри модели Pydnatic, можно сделать это, как обычно, используя Pydantic @validator , как показано здесь , а также здесь и здесь . Обратите внимание, что в этом случае, когда BaseModel используется для параметров запроса, повышение ValueError вызовет Internal Server Error. Следовательно, вы должны вместо этого вызвать HTTPException , когда проверка не удалась, или использовать собственный обработчик исключений для обработки ValueError исключений, как показано в Варианте 2 этого ответа . Помимо @validator, можно также иметь дополнительные проверки для Query параметров, как описано в документации FastAPI (см. также Реализация класса запроса).

В качестве примечания, касающегося определения необязательных параметров, в приведенном ниже примере используется подсказка типа Необязательный (сопровождаемый None в качестве значения по умолчанию в Query) из модуля typing ; однако вы также можете взглянуть на этот ответ и этот ответ, в которых описаны все доступные способы, как это сделать.

Рабочий пример

from fastapi import FastAPI, Depends, Query, HTTPException
from pydantic import BaseModel, Field, validator
from typing import List, Optional, Literal
from enum import Enum

app = FastAPI()

class Status(str, Enum):
    new = 'New'
    old = 'Old'


class ServiceStatus(BaseModel):
    status: Optional[Status] = Field (Query(..., description='Select service status'))
    msg: Optional[str] = Field (Query(None, description='Type something'))
    choice: Literal['a', 'b', 'c', 'd'] = Field (Query(..., description='Choose something'))
    comments: List[str] = Field (Query(..., description='Add some comments'))
    
    @validator('choice')
    def check_choice(cls, v):
        if v == 'b': 
             raise HTTPException(status_code=422, detail='Wrong choice')
        return v

@app.get('/status')
def main(status: ServiceStatus = Depends()):
    return status

Вы можете прочитать комментарий к исходному вопросу?

Amin Ba 12.04.2023 20:47

и могу ли я также включить Path и выполнить сложную проверку между полями?

Amin Ba 12.04.2023 20:49

предположим, я хотел определенную комбинацию статуса и комментариев. как я могу сделать проверку?

Amin Ba 12.04.2023 20:55

Спасибо. ты прав. на самом деле я уже прошел два курса на udemy про fastapi но все же иногда я не знаю что должен делать разработчик и что не должен делать. Я могу создать конечную точку, но не могу понять, это то, что делают профессионалы или нет, и это то, что создатели fastapi представляют себе, что разработчик делает или нет.

Amin Ba 12.04.2023 21:04

например, теперь, когда вы предоставили этот пример, мне интересно, следует ли мне включать Path и Body в класс ServiceStatus или нет

Amin Ba 12.04.2023 21:05

если бы у нас было services/{service_id}, вы бы добавили service_id: str = Field (Path(..., description='')) под class ServiceStatus(BaseModel) или нет?

Amin Ba 12.04.2023 23:58

Пожалуйста, ознакомьтесь с этим ответом , а также этим ответом и этим ответом (вы также можете найти этот полезным). Пожалуйста, внимательно прочитайте связанные ответы выше, а также любые включенные ссылки.

Chris 13.04.2023 07:33

Что, если я хочу сделать выбор статуса нечувствительным к регистру?

Amin Ba 25.04.2023 19:51

Будущим читателям: чтобы сделать enum значения нечувствительными к регистру, взгляните на этот ответ

Chris 29.04.2023 06:10

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