17

The body of an HTTP PUT request is a JSON list - like this:

[item1, item2, item3, ...]

I can't change this. (If the root was a JSON object rather than a list there would be no problem.)

Using FastAPI, I seem to be unable to access this content in the normal way:

@router.put('/data')
def set_data(data: DataModel): # This doesn't work; how do I even declare DataModel?

I found the following workaround, which seems like a very ugly hack:

class DataModel(BaseModel):
    __root__: List[str]


from fastAPI import Request

@router.put('/data')
async def set_data(request: Request): # Get the request object directly
    data = DataModel(__root__=await request.json())

This surely can't be the 'approved' way to achieve this. I've scoured the documentation both of FastAPI and Pydantic. What am I missing?

1
  • Future readers might find this answer helpful as well. Commented Jun 19 at 15:43

2 Answers 2

36

Descending from the model perspective to primitives

In FastAPI, you derive from BaseModel to describe the data models you send and receive (i.e. FastAPI also parses for you from a body and translates to Python objects). Also, it relies on the modeling and processing from pydantic.

from typing import List
from pydantic import BaseModel

class Item(BaseModel):
    name: str

class ItemList(BaseModel):
    items: List[Item]

def process_item_list(items: ItemList):
    pass

This example would be able to parse JSON like

{"items": [{"name": "John"}, {"name": "Mary"}]}

In your case - depending on what shape your list entries have - you'd also go for proper type modeling, but you want to directly receive and process the list without the JSON dict wrapper around it. You could go for:

from typing import List
from pydantic import BaseModel

class Item(BaseModel):
    name: str

def process_item_list(items: List[Item]):
    pass

Which is now able to process JSON like:

[{"name": "John"}, {"name": "Mary"}]

This is probably what you're looking for and the last adaption to take is depending on the shape of your item* in the list you receive. If it's plain strings, you can also go for:

from typing import List

def process_item_list(items: List[str]):
    pass

Which could process JSON like

["John", "Mary"]

I outlined the path from models down to primitives in lists because I think it's worth knowing where this can go if one needs more complexity in the data models.

Sign up to request clarification or add additional context in comments.

7 Comments

I thought I'd tried the above but obviously made some mistake along the way and ruled it out. Many thanks.
Why don't I just post the last option? Yes, that would seem better, but the API spec says that the entire list is rewritten.
Nah, that last heading was a question to myself - maybe it's poorly phrased, but I wanted to justify why I went all that way down ;)
i was trying too return an array without a name, as below; class ListItem(baseModel) List[Item] somehow it was working on print but cannot output . class ListItem(List[Item]) pass saved my life, thanks man
results in incorrect type {items :{"items" :["name" ] } } which is incorrect. About to try what @bilen mentioned.
|
-1

Let's clarify. There are 3 types of arguments, parsed in FastAPI:

  1. URL argument http:\\<YOUR_PATH>?<key>=<value>

  2. URL Path argument http:\\<PATH>\<argument>\<PATH>

  3. payload argument (for POST/PATCH/PUT/DELETE)

Appropriate function arguments formats in FastAPI:

1.

@router.put('/data')
def set_data(key1: int)
@router.put('/{key2}/data')
def set_data(key2: int)
class Item(BaseModel):
    name: str

@router.put('/data')
def set_data(payload_data: Item)

And one can combine all of them:

class Item(BaseModel):
    name: str

@router.put('/{key1}/data')
def set_data(
    key1: int,  # <-- PATH argument
    key2: str,  # <-- URL argument (for key=value after '?')
    payload_data: Item # <-- payload {'name': 'Vasya'}
):
   ...

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.