15

I'm new to FastAPI (migrating from Flask) and I'm trying to create a Pydantic model for my GET route:

from fastapi import APIRouter,Depends
from pydantic import BaseModel
from typing import Optional,List

router = APIRouter()

class SortModel(BaseModel):
    field:    Optional[str]
    directions: List[str]

@router.get("/pydanticmodel")
def get_sort(criteria: SortModel = Depends(SortModel)):
    pass #my code for handling this route.....

When I'm running :

curl -X GET http://localhost:XXXX/pydanticmodel?directions=up&directions=asc&field=id

I'm getting:

422 Unprocessable Entity: {"detail":[{"loc":["body"],"msg":"field required","type":"value_error.missing"}]}

But if I change directions:List[str] to directions: str I'm getting 200 OK with directions="asc".

What is the reason that str works for query param and List[str] does not? What am I doing wrong?

Thanks.

1
  • Would be useful to see an example of what sorting in more than one direction looks like Commented Sep 14, 2024 at 2:15

4 Answers 4

13

Update

You could now wrap the Query() in a Field(), which would allow you to define a Pydantic List field that will be interpreted as query parameter:

from fastapi import FastAPI, Query, Depends
from pydantic import BaseModel, Field
from typing import List, Optional

app = FastAPI()


class SortModel(BaseModel):
    field: Optional[str]
    directions: List[str] = Field(Query(...))


@app.get("/")
async def get_data(criteria: SortModel = Depends()):
    return criteria

See this answer for more details and another working example.

Original answer

It is not, as yet, possible to use a GET request with Pydantic List field as query parameter. When you declare a List field in the Pydantic model, it is interpreted as a request body parameter, instead of a query one (regardless of using Depends()—you can check that through Swagger UI docs at http://127.0.0.1:8000/docs, for instance). Additionally, as you are using a GET request, even if you added the List of directions in the body and attempted sending the request, it wouldn't work, as a POST request would be required for that operation.

The way to do this is to either define the List of directions explicitly with Query as a separate parameter in your endpoint, or implement your query parameter-parsing in a separate dependency class, as described here. Remember again to define the List field explicitly with Query, so that directions can be interpreted as a query parameter and appear multiple times in the URL (in others words, to receive multiple values). Example:

from typing import List, Optional
from fastapi import FastAPI, Depends, Query

app = FastAPI()


class SortModel:
    def __init__(
        self,
        field: Optional[str],
        directions: List[str] = Query(...)
    ):
        self.field = field
        self.directions = directions


@app.get("/")
async def get_data(criteria: SortModel = Depends()):
    return criteria

The above can be re-written using the @dataclass decorator, as shown below:

from typing import List, Optional
from fastapi import FastAPI, Depends, Query
from dataclasses import dataclass

app = FastAPI()


@dataclass
class SortModel:
    field: Optional[str]
    directions: List[str] = Query(...)


@app.get("/")
async def get_data(criteria: SortModel = Depends()):
    return criteria
Sign up to request clarification or add additional context in comments.

Comments

2

With FastAPI 0.115.0, you can now use list fields in a BaseModel within a query, and it works with SwaggerUI:

from fastapi import FastAPI, Query
from pydantic import BaseModel
from typing import Annotated


class FilterParams(BaseModel):
    tags: list[str] = []


app = FastAPI()


@app.get("/hello")
def world(params: Annotated[FilterParams, Query()]):
    return params

Comments

0

I'm running into the same issue. The following solution will work, but it isn't really what I want however maybe it's good enough for you:

from fastapi import APIRouter,Depends, Query
from pydantic import BaseModel
from typing import Optional,List

router = APIRouter()

class SortModel(BaseModel):
    field:    Optional[str]

@router.get("/pydanticmodel")
def get_sort(criteria: SortModel = Depends(SortModel), directions: List[str] = Query(...)):
    pass #my code for handling this route.....

Comments

-1

It's not a Pydantic or FastAPI problem.

If you want to send an array with curl you should use -d flag.

In: curl -X GET "http://127.0.0.1:8000/pydanticmodel?field=123"  -d "[\"string\"]"
Out: {"field":"123","directions":["string"]}

Now your code should work perfectly.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.