'Pydantic - Dynamically create a model with multiple base classes?

From the pydantic docs I understand this:

import pydantic

class User(pydantic.BaseModel):
    id: int
    name: str

class Student(pydantic.BaseModel):
    semester: int

# this works as expected
class Student_User(User, Student):
    building: str

print(Student_User.__fields__.keys())
#> dict_keys(['semester', 'id', 'name', 'building'])

However, when I want to create a similar object dynamically (following the section dynamic-model-creation):

# this results in a TypeError
pydantic.create_model("Student_User2", __base__=(User, Student))

I get:

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Question: How to dynamically create a class like Student_User



Solution 1:[1]

Your problem is not with pydantic but with how python handles multiple inheritances. I am assuming in the above code, you created a class which has both the fields of User as well as Student, so a better way to do that is

class User(pydantic.BaseModel):
    id: int
    name: str

class Student(User):
    semester: int

class Student_User(Student):
    building: str

This gets your job done. So, now if you want to create these models dynamically, you would do

pydantic.create_model("Student_User2", building=(str, ...), __base__=Student)

Obviously, building is the new model's field, so you can change that as you want

So, the final complete code would look something like this

import pydantic

class User(pydantic.BaseModel):
    id: int
    name: str

class Student(User):
    semester: int

class Student_User(Student):
    building: str

print(Student_User.__fields__.keys())

model = pydantic.create_model("Student_User2", building=(str, ...), __base__=Student)

Solution 2:[2]

Its not the answer to the original question, but if you are like me and all you care about is having a model which holds fields of other models, this should be a solutions.

Student_User = pydantic.create_model("Student_User", **{
    **{key: (value.type_, value.default) for key, value in User.__fields__.items()},
    **{key: (value.type_, value.default) for key, value in Student.__fields__.items()},
    **{"building": (str, '')},
})

Essentially, we are dynamically creating a new pydantic model and we are setting its fields to be the fields of our other models plus an additional custom field.

Note: OP included these lines in his question:

print(Student_User.__fields__.keys())
#> dict_keys(['semester', 'id', 'name', 'building'])

So, my guess is that his end goal was copying the fields from the other models and having a model created from multiple bases was just a method of achieving it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dharman
Solution 2 OriFl