Nymous
Nymous

Reputation: 1251

How to parse list of models with Pydantic

I use Pydantic to model the requests and responses to an API.

I defined a User class:

from pydantic import BaseModel

class User(BaseModel):
  name: str
  age: int

My API returns a list of users which I retrieve with requests and convert into a dict:

users = [{"name": "user1", "age": 15}, {"name": "user2", "age": 28}]

How can I convert this dict to a list of User instances?

My solution for now is

user_list = []
for user in users:
  user_list.append(User(**user))

Upvotes: 115

Views: 161592

Answers (10)

here is the pydantic model model.py

from pydantic import BaseModel

class Post_Response(BaseModel):
    name: str

class Post_Mother(BaseModel):
    status: int
    users: List[Post_Response] = []

let import it into our app.py

from . import Post_Response, Post_Mother

response = Post_Mother(
        status=200,
        users=[Post_Response(name='user_name')],
 )

Upvotes: 0

Maxim Suslov
Maxim Suslov

Reputation: 5465

Pydantic V2

For adapter approach see david-asaf's answers.

Another solution is to use RootModel:

from pydantic import BaseModel, RootModel

class User(BaseModel):
  name: str
  age: int

class PersonList(RootModel):
    root: list[User]

users = [{"name": "user1", "age": 15}, {"name": "user2", "age": 28}]

Upvotes: 9

David Asaf
David Asaf

Reputation: 2166

Pydantic V1:

This is now possible using parse_obj_as.

from pydantic import parse_obj_as

users = [
    {"name": "user1", "age": 15}, 
    {"name": "user2", "age": 28}
]

m = parse_obj_as(List[User], users)

Pydantic V2:

Use Type Adapter.

from pydantic import TypeAdapter

users = [
    {"name": "user1", "age": 15}, 
    {"name": "user2", "age": 28}
]

ta = TypeAdapter(List[User])
m = ta.validate_python(users)

Upvotes: 215

Aymen Azoui
Aymen Azoui

Reputation: 392

this worked for me using list comprehension:

user_list = [User(**user) for user in users]

Upvotes: 1

reza_khalafi
reza_khalafi

Reputation: 6534

I just set in my models.py list of dict like this:

from django.db import models
from pydantic import BaseModel

class CustomList(BaseModel):
    data: list[dict]

Upvotes: 1

alex
alex

Reputation: 11

I have another idea to simple this code if pydantic version below 1.2 that doesn't support parse_obj_as method.

user_list = []
for user in users:
  user_list.append(User(**user))

simple way

user_list = [User(**user) for user in users]

Upvotes: 0

Jiri
Jiri

Reputation: 771

To confirm and expand the previous answer, here is an "official" answer at pydantic-github - All credits to "dmontagu":

The "right" way to do this in pydantic is to make use of "Custom Root Types". You still need to make use of a container model:

class UserList(BaseModel):
    __root__: List[User]

but then the following will work:

UserList.parse_obj([
    {'id': '123', 'signup_ts': '2017-06-01 12:22', 'friends': [1, '2', b'3']},
    {'id': '456', 'signup_ts': '2017-06-02 12:22', 'friends': ['you']},
])

(and will put the values inside the root property).

Unfortunately, I think there is not good serialization support for this yet, so I think when you go to return the results, if you want to return just a list you'll still need to return UserList.root.

I don't think there is currently a unified interface that gets you a serialized/unstructured version of the model that respects the root_model, but if this is what you are looking for, it could be worth building.

Upvotes: 66

oHo
oHo

Reputation: 54541

You can use the __root__ Pydantic keyword:

from typing import List
from pydantic import BaseModel

class User(BaseModel):
  name: str
  age: int

class UserList(BaseModel):
  __root__: List[User]     # ⯇-- __root__

To build the JSON response:

user1 = {"name": "user1", "age": 15}
user2 = {"name": "user2", "age": 28}

user_list = UserList(__root__=[])
user_list.__root__.append(User(**user1))
user_list.__root__.append(User(**user2))

Your API web framework can jsonify user_list to be returned as a JSON array (within the response body).

Upvotes: 10

svrist
svrist

Reputation: 7110

You could consider using a list comprehension along with dict unpacking to the User constructor

user_list = [
  User(**user) for user in users
]

Upvotes: 23

ozcanyarimdunya
ozcanyarimdunya

Reputation: 2534

You can try this

from typing import List
from pydantic import BaseModel

class User(BaseModel):
  name: str
  age: int

class Users(BaseModel):
    users: List[User]

users = [{"name": "user1", "age": 15}, {"name": "user2", "age": 28}]
m = Users(users=users)
print(m.dict())

Upvotes: 23

Related Questions