Fastapi: [QUESTION] How to set a parameter from Body OR Query

Created on 21 May 2020  路  6Comments  路  Source: tiangolo/fastapi

First check

  • [x] I used the GitHub search to find a similar issue and didn't find it.
  • [x] I searched the FastAPI documentation, with the integrated search.
  • [x] I already searched in Google "How to X in FastAPI" and didn't find any information.

Description

I am trying to setup a dependency that will accept a paramter from a Body or Query (it will be used as a dependency for POST and GET requests.
I need to be able to look for the env parameter in either the Body or Query. Something like this maybe:

def testFactory(env: str = Body(...) or Query(...)):
    testDict = {
        'testenv1' = TestObject1(),
        'testenv2' = TestObject2()
    }
    return testDict.get(env)

I'm an amateur programmer, so please let me know if I'm just not doing it right!

question

All 6 comments

This is my current workaround, but it feels like it could be better. Basically looking for this functionality in a single dependency function or class instead of 3

def testDict():
    return {
        'testenv1' = TestObject1,
        'testenv2' = TestObject2
    }

def qTestFactory(env: str = Query(...), testObject = Depends(testDict)):
    return testObject.get(env)()

def bTestFactory(env: str = Body(...), testObject = Depends(testDict)):
    return testObject.get(env)()

@taoofshawn here's an example of doing what I think you want to do:

from typing import Optional

import uvicorn
from fastapi import FastAPI, Depends, Query, Body, HTTPException
from starlette.status import HTTP_400_BAD_REQUEST

app = FastAPI()


def get_env(
    query_env: Optional[str] = Query(None, alias="env"),
    body_env: Optional[str] = Body(None, alias="env"),
) -> str:
    if query_env is not None:
        return query_env
    if body_env is not None:
        return body_env
    raise HTTPException(
        HTTP_400_BAD_REQUEST, detail="You must provide 'env' in body or query"
    )


@app.get("/")
def test(env: str = Depends(get_env)):
    return env


@app.post("/")
def test(env: str = Depends(get_env)):
    return env


if __name__ == "__main__":
    uvicorn.run(app)

However, I strongly recommend that you do not take this approach. There are a few problems here:

  1. It's unclear the users _which_ env will be used if they provide both.
  2. It's not clear that env is required because we have the make the parameters optional in order to be able to choose a location.

I recommend that you either just put this option in query (which you can do for both POST and GET) or have two different dependencies and select one per endpoint. Something like this:

import uvicorn
from fastapi import FastAPI, Depends, Query, Body

app = FastAPI()


def query_env(env: str = Query(...)) -> str:
    return env


def body_env(env: str = Body(...)) -> str:
    return env


@app.get("/")
def test(env: str = Depends(query_env)):
    return env


@app.post("/")
def test(env: str = Depends(body_env)):
    return env


if __name__ == "__main__":
    uvicorn.run(app)

At that point it's less code to just include the params directly in your route rather than use a dependency. I am assuming you need a dependency so you can do something else with the env.

You're right, I think it would be better to keep "env" as a query parameter for all requests and just also accept a body for the other params for posts. I think it got (incorrectly) burned into my head somewhere to always use query for gets and form or body for posts.
thanks @dbanty !

Thanks for the help here @dbanty ! :bow:

Thanks for reporting back and closing the issue @taoofshawn :+1:

It would be convenient if fastapi did support this type of usage without having to create two separate duplicate path operators (just decorated differently). Something equivalent to "get_arguments" in tornado. Nevertheless, it's helpful to hear that the best approach currently is to define two separate path operators.

Maybe one could make a single pydantic class to represent the common input args and somehow reuse it for GET and POST's?

EDIT: okay maybe not a pydantic class, but maybe a dataclass which is a dependency?

@falkben if you are needing to put that in many places, yeah, you could just create a class dependency to get and store the alternative parameters.

Was this page helpful?
0 / 5 - 0 ratings