Schema API¶
Core schema definition classes and decorators.
Schema
¶
Base schema class for defining data models.
Define your schema by subclassing Schema and adding field definitions.
The metaclass automatically collects fields and validators.
Fields are defined using Pydantic-style type annotations with optional Field() for constraints.
Examples:
Basic schema definition:
>>> from flycatcher import Schema, Field
>>> from datetime import datetime
>>> class UserSchema(Schema):
... # Simple fields - just type annotations
... name: str
... created_at: datetime
...
... # Nullable fields
... bio: str | None = None
...
... # Fields with defaults
... is_active: bool = True
...
... # Fields with constraints
... age: int = Field(ge=0, le=120)
... email: str = Field(min_length=5, max_length=100)
...
... # Database options
... id: int = Field(primary_key=True, autoincrement=True)
With cross-field validation:
>>> from flycatcher import Schema, Field, col, model_validator
>>> class BookingSchema(Schema):
... check_in: datetime
... check_out: datetime
...
... @model_validator
... def check_dates():
... return (
... col('check_out') > col('check_in'),
... "Check-out must be after check-in"
... )
Generate outputs:
>>> from datetime import datetime
>>> # Generate Pydantic model
>>> UserModel = UserSchema.to_pydantic()
>>> user = UserModel(
... id=1,
... name="Alice",
... age=25,
... email="alice@example.com",
... created_at=datetime.now(),
... )
>>>
>>> # Generate Polars validator
>>> import polars as pl
>>> validator = UserSchema.to_polars_validator()
>>> df = pl.DataFrame({
... "id": [1],
... "name": ["Alice"],
... "age": [25],
... "email": ["alice@example.com"],
... "created_at": [datetime.now()],
... })
>>> validated_df = validator.validate(df, strict=True)
>>>
>>> # Generate SQLAlchemy table
>>> table = UserSchema.to_sqlalchemy(table_name="users")
to_pydantic()
classmethod
¶
Generate a Pydantic BaseModel from this schema.
Returns:
| Type | Description |
|---|---|
type
|
A dynamically created Pydantic BaseModel class. |
Examples:
>>> from flycatcher import Field, Schema
>>> class UserSchema(Schema):
... id: int = Field(primary_key=True)
... name: str
>>> UserModel = UserSchema.to_pydantic()
>>> user = UserModel(id=1, name="Alice")
>>> user.model_dump()
{'id': 1, 'name': 'Alice'}
Source code in src/flycatcher/base.py
to_polars_validator()
classmethod
¶
Generate a Polars validator from this schema.
Returns:
| Type | Description |
|---|---|
PolarsValidator
|
A validator instance for validating Polars DataFrames. |
Examples:
>>> from flycatcher import Field, Schema
>>> import polars as pl
>>> class UserSchema(Schema):
... id: int = Field(primary_key=True)
... name: str = Field(min_length=1)
>>> validator = UserSchema.to_polars_validator()
>>> df = pl.DataFrame({"id": [1, 2], "name": ["Alice", "Bob"]})
>>> validated_df = validator.validate(df, strict=True)
Source code in src/flycatcher/base.py
to_sqlalchemy(table_name=None, metadata=None)
classmethod
¶
Generate a SQLAlchemy Table from this schema.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
Name for the SQL table. If not provided, auto-generated from schema class name (removes "Schema" suffix, lowercases, adds "s"). |
None
|
metadata
|
MetaData
|
MetaData instance to attach the table to. If not provided, a new MetaData instance is created. |
None
|
Returns:
| Type | Description |
|---|---|
Table
|
A SQLAlchemy Table object. |
Examples:
>>> from flycatcher import Field, Schema
>>> from sqlalchemy import MetaData, create_engine
>>> class UserSchema(Schema):
... id: int = Field(primary_key=True)
... name: str
>>> metadata = MetaData()
>>> table = UserSchema.to_sqlalchemy(table_name="users", metadata=metadata)
>>> engine = create_engine("sqlite:///example.db")
>>> metadata.create_all(engine)
Source code in src/flycatcher/base.py
fields()
classmethod
¶
Return all fields defined in this schema.
Returns:
| Type | Description |
|---|---|
dict[str, FieldBase]
|
Dictionary mapping field names to Field instances. |
Examples:
>>> from flycatcher import Schema, Field
>>> class UserSchema(Schema):
... id: int = Field(primary_key=True)
... name: str
>>> fields = UserSchema.fields()
>>> list(fields.keys())
['id', 'name']
Source code in src/flycatcher/base.py
model_validators()
classmethod
¶
Return all model validators defined in this schema.
Returns:
| Type | Description |
|---|---|
list[Callable]
|
List of validator functions decorated with @model_validator. |
Examples:
>>> from flycatcher import Schema, col, model_validator
>>> class UserSchema(Schema):
... age: int
...
... @model_validator
... def check_age():
... return col('age') >= 18
>>> validators = UserSchema.model_validators()
>>> len(validators)
1
Source code in src/flycatcher/base.py
model_validator(func)
¶
Decorator for cross-field validation.
Use this decorator to add custom validation logic that involves multiple fields. The validator function can return either:
- A DSL expression (recommended) - compiles to both Polars and Pydantic
- A dict with 'polars' and/or 'pydantic' keys for explicit implementations
The function can optionally accept a cls parameter, but it's not required
for most use cases.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable
|
The validation function to decorate. |
required |
Returns:
| Type | Description |
|---|---|
Callable
|
The decorated function, marked as a model validator. |
Examples:
Simple DSL expression:
>>> from flycatcher import Schema, col, model_validator
>>> class BookingSchema(Schema):
... check_in: int
... check_out: int
...
... @model_validator
... def check_dates():
... return col('check_out') > col('check_in')
With error message:
>>> class BookingSchema(Schema):
... check_in: int
... check_out: int
...
... @model_validator
... def check_dates():
... return (
... col('check_out') > col('check_in'),
... "Check-out date must be after check-in date"
... )
Complex validation with multiple conditions:
>>> from flycatcher import Field, Schema, col, model_validator
>>> class ProductSchema(Schema):
... price: float
... discount_price: float | None = None
...
... @model_validator
... def check_discount():
... return (
... (col('discount_price').is_null()) |
... (col('discount_price') < col('price')),
... "Discount price must be less than regular price"
... )