Dynamic Schemas
Building models at runtime with create_model and using model_construct for skipping validation.
All the models we've built are defined at class level. But what if I don't know the field names until runtime? Like building a model from a database schema or a config file?
Pydantic has create_model for exactly this. It builds a model class dynamically:
from pydantic import create_model
# Create a model at runtime
UserModel = create_model(
"UserModel",
name=(str, ...), # (type, default) — ... means required
age=(int, 0), # int with default 0
email=(str, ...),
)
user = UserModel(name="Alice", email="alice@test.com")
print(user.name) # "Alice"
print(user.age) # 0
The ... (Ellipsis) means the field is required. A value like 0 becomes the default. You get a fully functional Pydantic model — with validation, serialization, and JSON schema support.
Can I add field constraints to dynamic models? Like Field(gt=0) for a price field?
Yes. Pass a FieldInfo object instead of a simple tuple:
from pydantic import create_model, Field
ProductModel = create_model(
"ProductModel",
name=(str, Field(min_length=1, description="Product name")),
price=(float, Field(gt=0, description="Price in USD")),
quantity=(int, Field(ge=0, default=0)),
)
p = ProductModel(name="Widget", price=9.99)
print(p.quantity) # 0
When would I actually use dynamic models in practice?
Common scenarios: building validators from database column definitions, creating form models from configuration files, or generating API models from OpenAPI specs. Here's a practical example:
def build_model_from_spec(name: str, fields: dict):
"""Build a Pydantic model from a {field_name: type_string} spec."""
type_map = {"str": str, "int": int, "float": float, "bool": bool}
field_definitions = {}
for field_name, type_str in fields.items():
python_type = type_map.get(type_str, str)
field_definitions[field_name] = (python_type, ...)
return create_model(name, **field_definitions)
spec = {"username": "str", "score": "int", "active": "bool"}
DynamicUser = build_model_from_spec("DynamicUser", spec)
user = DynamicUser(username="alice", score=95, active=True)
What about model_construct? I've seen it mentioned for creating models without validation.
model_construct creates a model instance by skipping validation entirely. It's fast but dangerous — use it only when you know the data is already valid:
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
# Normal creation — validates
user1 = User(name="Alice", age=30) # validated
# Construct — no validation
user2 = User.model_construct(name="Alice", age=30) # NOT validated
user3 = User.model_construct(name="Alice", age="banana") # no error!
Use model_construct for performance-critical paths where data comes from a trusted source — like reading from your own database where you know the types are correct. Never use it for external input.
So create_model is for building model classes dynamically, and model_construct is for creating instances without validation?
Exactly. create_model = dynamic class definition. model_construct = bypassing validation on a known class. They solve different problems. Most of the time, you'll define models normally and validate everything. But when the schema itself is dynamic, create_model is your tool.
Practice your skills
Sign up to write and run code in this lesson.
Dynamic Schemas
Building models at runtime with create_model and using model_construct for skipping validation.
All the models we've built are defined at class level. But what if I don't know the field names until runtime? Like building a model from a database schema or a config file?
Pydantic has create_model for exactly this. It builds a model class dynamically:
from pydantic import create_model
# Create a model at runtime
UserModel = create_model(
"UserModel",
name=(str, ...), # (type, default) — ... means required
age=(int, 0), # int with default 0
email=(str, ...),
)
user = UserModel(name="Alice", email="alice@test.com")
print(user.name) # "Alice"
print(user.age) # 0
The ... (Ellipsis) means the field is required. A value like 0 becomes the default. You get a fully functional Pydantic model — with validation, serialization, and JSON schema support.
Can I add field constraints to dynamic models? Like Field(gt=0) for a price field?
Yes. Pass a FieldInfo object instead of a simple tuple:
from pydantic import create_model, Field
ProductModel = create_model(
"ProductModel",
name=(str, Field(min_length=1, description="Product name")),
price=(float, Field(gt=0, description="Price in USD")),
quantity=(int, Field(ge=0, default=0)),
)
p = ProductModel(name="Widget", price=9.99)
print(p.quantity) # 0
When would I actually use dynamic models in practice?
Common scenarios: building validators from database column definitions, creating form models from configuration files, or generating API models from OpenAPI specs. Here's a practical example:
def build_model_from_spec(name: str, fields: dict):
"""Build a Pydantic model from a {field_name: type_string} spec."""
type_map = {"str": str, "int": int, "float": float, "bool": bool}
field_definitions = {}
for field_name, type_str in fields.items():
python_type = type_map.get(type_str, str)
field_definitions[field_name] = (python_type, ...)
return create_model(name, **field_definitions)
spec = {"username": "str", "score": "int", "active": "bool"}
DynamicUser = build_model_from_spec("DynamicUser", spec)
user = DynamicUser(username="alice", score=95, active=True)
What about model_construct? I've seen it mentioned for creating models without validation.
model_construct creates a model instance by skipping validation entirely. It's fast but dangerous — use it only when you know the data is already valid:
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
# Normal creation — validates
user1 = User(name="Alice", age=30) # validated
# Construct — no validation
user2 = User.model_construct(name="Alice", age=30) # NOT validated
user3 = User.model_construct(name="Alice", age="banana") # no error!
Use model_construct for performance-critical paths where data comes from a trusted source — like reading from your own database where you know the types are correct. Never use it for external input.
So create_model is for building model classes dynamically, and model_construct is for creating instances without validation?
Exactly. create_model = dynamic class definition. model_construct = bypassing validation on a known class. They solve different problems. Most of the time, you'll define models normally and validate everything. But when the schema itself is dynamic, create_model is your tool.
Practice your skills
Sign up to write and run code in this lesson.