Table Definition

We define tables in Iceaxe using the TableBase class. You define the column type by attaching a Python typehint to the class attributes. You can further configure these columns by customizing a Field.

from iceaxe import TableBase, Field, PostgresDateTime, DBConnection

class Employee(TableBase):
    id: int | None = Field(primary_key=True, default=None)
    name: str
    age: int
    payload: datetime = Field(postgres_config=PostgresDateTime(timezone=True))

Right now this table just exists in Python. To create it in the database, we need to issue the instructions to create the table. You can manually write this SQL or rely on our harness to generate it for you.

from iceaxe.schemas.cli import create_all

conn = DBConnection(
    await asyncpg.connect(
        host="localhost",
        port=5432,
        user="db_user",
        password="yoursecretpassword",
        database="your_db",
    )
)
await create_all(conn)

By default, create_all will create all tables that are subclasses of TableBase that are currently imported into your Python environment. Make sure all the models that you use are imported into thet namespace before you call create_all. One convenient convention to follow is to define your schemas in a models folder and then re-export them from the models/__init__.py file.

myproject/
├── __init__.py
└── models/
    ├── __init__.py
    ├── employee.py
    └── office.py
# models/__init__.py
from .employee import Employee as Employee
from .office import Office as Office

This allows you to import the one models module and have all of your models available with that one import:

from myproject import models

print(models.Employee)
print(models.Office)

await create_all(conn)

You can also limit the models that are created by passing a list of explicit models to the models argument:

await create_all(conn, models=[DemoCustomModel])

Fields

This TableBase is a subclass of Pydantic's BaseModel, which means that you can use Pydantic's validation and serialization features on your tables in addition to our database specific flags. You can read more about the Pydantic features in the Pydantic documentation. We define our full database options in the Iceaxe API docs.

To use one example, let's say that you want to validate that the age is at least 16. Add a new field validator for that particular field:

from iceaxe import TableBase, Field
from pydantic import field_validator

class Employee(TableBase):
    id: int | None = Field(primary_key=True, default=None)
    name: str
    age: int

    @field_validator("age")
    def validate_age(cls, v):
        if v < 16:
            raise ValueError("You must be at least 16 to receive a tax return")
        return v

# Successful validation
person_1 = Person(name="John Doe", age=16)
print(person_1)

# Raises a pydantic_core._pydantic_core.ValidationError
person_2 = Person(name="John Doe", age=15)
print(person_2)