0

the following FastAPI code is producing unexpected behaviour to me:

import uvicorn
from fastapi import FastAPI, Depends, Query
from typing import Optional
from pydantic.dataclasses import dataclass


app = FastAPI()

@dataclass
class Catz:
    qqq: Optional[str] = Query(None, alias="q")

@app.get("/productz/")
def search_products(query: Catz = Depends(Catz)  ):
    products = [{"name": "Computer"}, {"name": "HDD"}]
    if not query.qqq:
        query.qqq = ""
    return {"query": query, "results": [product for product in products if query.qqq in product["name"]]}


@dataclass
class Cats:
    qqq: Optional[str] = Query(None )

@app.get("/products/")
def search_products(query: Cats = Depends(Cats)  ):
    products = [{"name": "Computer"}, {"name": "HDD"}]
    if not query.qqq:
        query.qqq = ""
    return {"query": query, "results": [product for product in products if query.qqq in product["name"]]}




if __name__ == "__main__":
    uvicorn.run("main:app", port=11978, log_level="info", reload=True)

when I use the service via curl, I get the following outputs:

  1. the expected behaviour with the endpoint /products/ that has no aliases
>> curl -X 'GET'  'http://localhost:11978/products/?qqq=H'  -H 'accept: application/json' -H 'api-version: 1.0' ; echo
{"query":{"qqq":"H"},"results":[{"name":"HDD"}]}
  1. not the expected behaviour with the endpoint /productz/ that has no aliases (regardless of me using a query parameter with its own name or with the alias I have in the code)
>> curl -X 'GET'  'http://localhost:11978/productz/?qqq=H'  -H 'accept: application/json' -H 'api-version: 1.0' ; echo
{"query":{"qqq":""},"results":[{"name":"Computer"},{"name":"HDD"}]}

>> curl -X 'GET'  'http://localhost:11978/productz/?q=H'  -H 'accept: application/json' -H 'api-version: 1.0' ; echo
{"query":{"qqq":""},"results":[{"name":"Computer"},{"name":"HDD"}]}

any idea why that would be?

2 Answers 2

5

Do not import dataclass from pydantic.dataclasses - it should be imported from Python's own built-in dataclasses module:

from fastapi import FastAPI, Depends, Query
from fastapi.exceptions import RequestValidationError
from dataclasses import dataclass
from typing import Optional


app = FastAPI()


@dataclass
class Catz:
    qqq: Optional[str] = Query(None, alias="q")

    
@app.get("/productz/")
def search_products(query: Catz = Depends()):
    products = [{"name": "Computer"}, {"name": "HDD"}]
    if not query.qqq:
        query.qqq = ""
    return {"query": query, "results": [product for product in products if query]}

Outputs for /productz?q=123:

{"query":{"qqq":"123"},"results":[{"name":"Computer"},{"name":"HDD"}]}
Sign up to request clarification or add additional context in comments.

3 Comments

Can I ask what's the difference? --I had other behaviours breaking by not importing from pydantic, I can't reproduce the issue right now, but I could do an "import as" and have them both imported. \\At any rate, Thanks for the hint, I'll work on it tomorrow (late evening here) and mark this answered!
According with the link you posted (which was exactly my case): <<In some cases, you might still have to use Pydantic's version of dataclasses. For example, if you have errors with the automatically generated API documentation.>> . --the solution here was to use both imported separately, as in this example from pydantic's documentation: pydantic-docs.helpmanual.io/usage/dataclasses/…
Yeah, sounds like its a bug, since it almost works; might want to report it on the FastAPI bugtracker.
0

You can append to @dataclass-decorator the argument config={'extra': 'allow'}, so, your code will be looks like

@dataclass(config={'extra': 'allow'})
class Catz:
   qqq: Optional[str] = Query(None, alias="q")

But you should keep in mind what if you pass parameter qqq instead q in the query — it will return a validation error.

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.