Models, fields and relationships

class alchemiq.Model(**kwargs)[source]

Bases: DeclarativeBase

Annotation-first declarative base for all alchemiq models.

Subclass Model and declare fields as plain Python annotations - no mapped_column boilerplate required. The metaclass resolves each annotation to the appropriate FieldType, wires the SQLAlchemy column, and installs validators.

E.g.:

class User(Model):
    id: PK[int]
    name: str
    email: Email

class Post(Model):
    id: PK[int]
    author: User = ForeignKey(related_name="posts")
    title: str

Table name defaults to the snake_case class name (no pluralization). Override __tablename__ explicitly to use a different name.

Behaviour flags are set via an inner class Meta:

class Article(Model):
    id: PK[int]
    body: str

    class Meta:
        soft_delete = True   # adds deleted_at; enables restore / hard_delete
        versioned   = True   # adds _version; enables optimistic locking
        timestamps  = True   # adds created_at / updated_at
        outbox      = True   # captures mutations for the transactional outbox

See also

Repository - the async query/mutation API over a Model.

Parameters:

kwargs (Any)

registry: ClassVar[registry] = <sqlalchemy.orm.decl_api.registry object>

Refers to the _orm.registry in use where new _orm.Mapper objects will be associated.

metadata: ClassVar[MetaData] = MetaData()

Refers to the _schema.MetaData collection that will be used for new _schema.Table objects.

check_password(raw)[source]

Verify raw against the stored password hash (whatever scheme produced it).

This stub is replaced at class-definition time on any model that declares a Password field. Calling it on a model with no Password field raises ConfigError.

E.g.:

user = User(id=1, email="a@b.com", password="s3cr3t")
assert user.check_password("s3cr3t") is True
assert user.check_password("wrong") is False
Parameters:

raw (str) – the plaintext password to verify.

Returns:

True if raw matches the stored hash, False otherwise.

Raises:

ConfigError – if the model has no Password field.

Return type:

bool

to_dict(*, include=None, exclude=None, mode='python', relations=())[source]

Serialize own columns (and optionally loaded relations) to a plain dict.

Password fields are omitted unless explicitly listed in include. Maybe[T] columns are unwrapped: Some(v) -> v, Nothing -> None. On a soft-delete model the auto-injected deleted_at key IS present.

E.g.:

user = User(id=1, email="ada@x.io", password="s3cr3t")
d = user.to_dict()                         # password omitted
d = user.to_dict(mode="json")              # datetime -> ISO string
d = user.to_dict(include={"id", "email"})  # whitelist
d = user.to_dict(exclude={"created_at"})   # blacklist
Parameters:
  • include (Any) – field names to include (whitelist); None means all.

  • exclude (Any) – field names to omit (blacklist); None means none.

  • mode (Literal['python', 'json']) – "python" keeps native types (datetime, Decimal); "json" coerces them to JSON-safe scalars (ISO strings, str).

  • relations (Any) – names of eagerly-loaded relationship attributes to inline.

Returns:

a plain dict keyed by field name.

Raises:

RelationNotLoaded – if a name in relations was not joined in the query.

Return type:

dict[str, Any]

See also

Model.to_pydantic() - validated Pydantic DTO.

classmethod from_dict(data)[source]

Construct an instance from a plain dict, validating field names and types.

Unknown keys raise ValidationError immediately (fail-fast). Known keys are passed through the same set-event validators as normal assignment, so Email is normalised, Password is hashed, etc.

E.g.:

acc = Account.from_dict(
    {"id": 9, "email": "X@Y.com", "password": "pw"}
)
assert acc.email == "x@y.com"    # normalised
assert acc.check_password("pw")  # hashed on assignment
Parameters:

data (Mapping[str, Any]) – a mapping of field names to raw values.

Returns:

a new instance of this model class.

Raises:

ValidationError – if data contains keys that are not fields; an aggregated error is also raised if any per-field validator rejects a value.

Return type:

Self

classmethod to_schema(*, include=None, exclude=None)[source]

Return a Pydantic model class whose fields mirror this model’s columns.

The schema class is built once and cached per (include, exclude) pair. Password fields are excluded by default (same policy as Model.to_dict()).

E.g.:

AccountSchema = Account.to_schema()
AccountSchema = Account.to_schema(exclude={"created_at"})
instance = AccountSchema(id=1, email="a@b.com")
Parameters:
  • include (Any) – field names to include (whitelist).

  • exclude (Any) – field names to omit (blacklist).

Returns:

a pydantic.BaseModel subclass named <Model>Schema.

Return type:

Any

See also

Model.to_pydantic() - convert an instance to a schema DTO.

to_pydantic()[source]

Convert this instance to a validated Pydantic schema object.

Equivalent to Model.to_schema().model_validate(self.to_dict()). Password fields are excluded; Maybe[T] columns are unwrapped.

E.g.:

dto = user.to_pydantic()
assert dto.email == "ada@x.io"
Returns:

a validated instance of the Pydantic class returned by Model.to_schema().

Return type:

Any

See also

Model.to_schema() - the generated Pydantic class itself.

class alchemiq.Field(*, python_type=None, unique=False, index=False, primary_key=False, nullable=False, default=<MISSING>, server_default=None, max_length=None, onupdate=None)[source]

Bases: FieldType[Any]

Generic field for plain python-typed columns (str/int/…).

python_type is set by the pipeline.

Parameters:
  • python_type (type)

  • unique (bool)

  • index (bool)

  • primary_key (bool)

  • nullable (bool)

  • default (Any)

  • server_default (Any)

  • max_length (int | None)

  • onupdate (Any)

column_type()[source]

Return the SQLAlchemy column type derived from python_type.

Return type:

TypeEngine

class alchemiq.ForeignKey(on_delete=None, related_name=None)[source]

Bases: object

Optional override marker for a many-to-one FK inferred from the annotation.

When the annotation alone is enough (most cases) you can omit the marker. Use ForeignKey(...) only to customise on_delete behaviour or to resolve a related_name collision.

E.g.:

class Member(Model):
    id: PK[int]
    org: Org                             # required -> ON DELETE RESTRICT (default)
    sponsor: Org | None = ForeignKey(related_name="sponsored")  # optional + custom name
    owner: Org = ForeignKey(on_delete="CASCADE", related_name="docs")
Parameters:
  • on_delete (str | None) – PostgreSQL ON DELETE action ("RESTRICT", "CASCADE", "SET NULL"). Inferred from nullability when omitted.

  • related_name (str | None) – name of the reverse accessor on the target model. Defaults to <snake_cls>_set.

See also

OneToOne - unique FK with a scalar reverse accessor.

class alchemiq.ManyToMany(related_name=None, secondary=None)[source]

Bases: object

Optional override marker for a list[Model] many-to-many annotation.

Without the marker the join-table name is derived automatically as sorted(a_table, b_table) joined with _. Use ManyToMany(...) to supply a custom related_name or secondary table name.

E.g.:

class Tag(Model):
    id: PK[int]
    name: str

class Post(Model):
    id: PK[int]
    tags: list[Tag]                         # auto join-table
    featured: list[Tag] = ManyToMany(       # explicit names
        related_name="featured_post_set",
        secondary="post_featured_tag",
    )
Parameters:
  • related_name (str | None) – name of the reverse accessor on the target model. Defaults to <snake_cls>_set.

  • secondary (str | None) – explicit name for the auto-created association table. Required when declaring M2M from both sides to avoid a collision.

See also

ForeignKey - many-to-one relationship.

class alchemiq.OneToOne[source]

Bases: Generic

Marker for a one-to-one relationship: profile: OneToOne[Profile].

Wires a unique, non-nullable FK column <name>_id on the declaring model and a scalar back-reference <snake_cls> on the target.

E.g.:

class Profile(Model):
    id: PK[int]
    bio: str

class User(Model):
    id: PK[int]
    profile: OneToOne[Profile]

The user.profile_id FK column is added automatically; profile.user is the reverse scalar accessor (_snake(cls.__name__)).

Note

Under TYPE_CHECKING OneToOne[T] is an alias for T, so static analysers infer the correct type without seeing the runtime class.

See also

ForeignKey - many-to-one (nullable or required).

class alchemiq.types.FieldType(*, python_type=None, unique=False, index=False, primary_key=False, nullable=False, default=<MISSING>, server_default=None, max_length=None, onupdate=None)[source]

Bases: ABC, Generic

Base of every Alchemiq field type. Public, user-subclassable.

Parameters:
  • python_type (type)

  • unique (bool)

  • index (bool)

  • primary_key (bool)

  • nullable (bool)

  • default (Any)

  • server_default (Any)

  • max_length (int | None)

  • onupdate (Any)

python_type

alias of object

abstractmethod column_type()[source]

SQLAlchemy storage type (or TypeDecorator) for this field.

Return type:

TypeEngine

validate(value)[source]

Eager validation + normalization. Override to enforce rules. Identity by default.

Parameters:

value (Any)

Return type:

Any

descriptor(name)[source]

Optional custom descriptor (e.g. Password). None = use native instrumentation.

Parameters:

name (str)

Return type:

Any | None

build_column()[source]

Build and return the mapped_column for this field.

Return type:

Mapped[Any]