Reputation: 131
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.
Upvotes: 13
Views: 11329
Reputation: 34055
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.
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
Upvotes: 8
Reputation: 6297
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
Upvotes: 1
Reputation: 119
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.....
Upvotes: 0
Reputation: 20598
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.
Upvotes: -1