# Unit 12: Request Body - List Fields and Nested Models

## Introduction

In FastAPI, you can define request bodies with complex structures using Pydantic models. These can include fields and nested models. In the following sections, this unit will show how to implement list fields and nested models.

## List fields

Let's take a look at the following program:

{% code lineNumbers="true" %}

```python
# Note: This program is implemented in Python 3.12
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None
    tags: list[str] = []


@app.post("/update-item/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item, "message": "Item updated successfully"}
    return results
```

{% endcode %}

{% hint style="info" %}
You should note that the above program is implemented in Python 3.12. If you are using Python before version 3.9, the `List` declaration is as follows:

`from typing import List`

`my_list: List[str]`
{% endhint %}

To evaluate the program, you can compose a request in Postman as shown in Fig. 1. The request body can be as follows:

```json
{
    "name": "Green Apple",
    "description": "A type of fruit",
    "price": 50000,
    "tax": 2500,
    "tags": ["fruit", "apple"]
}
```

<figure><img src="https://4188609209-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fhp4eambztJcKKLa7ysKG%2Fuploads%2FbXEwbaP0DhfTnliKLvfB%2FScreenshot%202024-03-09%20at%2011.05.02%20AM.png?alt=media&#x26;token=f9483461-75cb-477e-a78b-a9d81c490b7d" alt=""><figcaption><p>Fig. 1. Example of a quest in Postman</p></figcaption></figure>

{% hint style="info" %}
In Python, a `list` allows duplicate elements and maintains their order, while a `set` ensures element uniqueness without any guaranteed order.
{% endhint %}

## Nested models

In the following program, we define two classes, i.e., `Image` and `Item`, and they have a HAS-A relationship. In `Image` class, the `metadata` is declared as a list of dictionary (key-value pairs).

{% code lineNumbers="true" %}

```python
# Note: This program is implemented in Python 3.12
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Image(BaseModel):
    url: str
    metadata: list[dict[str, str]]

class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None
    tags: list[str] = []
    image: Image = None


@app.post("/update-item/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item, "message": "Item updated successfully"}
    return results
```

{% endcode %}

The request body to evaluate the above program is as follows. You should note how we compose the `metadata` values. Fig. 2 shows the results when evaluating the program in Postman.

```json
{
    "name": "Green Apple",
    "description": "A type of fruit",
    "price": 50000,
    "tax": 2500,
    "tags": [
        "fruit",
        "apple"
    ],
    "image": {
        "url": "example.com",
        "metadata": [
            {
                "File type": "JPEG",
                "MIME type": "image/jpeg"
            }
        ]
    }
}
```

<figure><img src="https://4188609209-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fhp4eambztJcKKLa7ysKG%2Fuploads%2FkTLhGB1L29X4cJpplHik%2FScreenshot%202024-03-09%20at%2011.40.06%20AM.png?alt=media&#x26;token=c9709f91-613a-4e16-ad09-42297209c6c4" alt=""><figcaption><p>Fig. 2. Example of a quest in Postman</p></figcaption></figure>

## Example program

In this example:

* We define a Pydantic model `Item` with fields `name`, `description`, `price`, and `tax`.
* We then define another Pydantic model `Order` that contains a list of `Item` objects along with `customer_name` and `shipping_address`.
* In the route `/order/`, we accept POST requests with a request body of type `Order`.
* When a request is made to `/order/`, FastAPI automatically validates the request body against the `Order` model and gives you an instance of `Order` in the route function `create_order`.

{% code lineNumbers="true" %}

```python
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None

class Order(BaseModel):
    items: list[Item]
    customer_name: str
    shipping_address: str

@app.post("/order/")
async def create_order(order: Order):
    return {"order_details": order}
```

{% endcode %}

***

## Summary

In this unit, we have introduced how to enhance request body in FastAPI to allow the server program to capture more complex inputs using list fields and nested models in combination with Pydantic model.
