Models, fields and relationships¶
- class alchemiq.Model(**kwargs)[source]¶
Bases:
DeclarativeBaseAnnotation-first declarative base for all alchemiq models.
Subclass
Modeland declare fields as plain Python annotations - nomapped_columnboilerplate required. The metaclass resolves each annotation to the appropriateFieldType, 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.registryin use where new_orm.Mapperobjects will be associated.
- metadata: ClassVar[MetaData] = MetaData()¶
Refers to the
_schema.MetaDatacollection that will be used for new_schema.Tableobjects.See also
- 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
Passwordfield. Calling it on a model with noPasswordfield raisesConfigError.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:
Trueif raw matches the stored hash,Falseotherwise.- Raises:
ConfigError – if the model has no
Passwordfield.- Return type:
- to_dict(*, include=None, exclude=None, mode='python', relations=())[source]¶
Serialize own columns (and optionally loaded relations) to a plain dict.
Passwordfields are omitted unless explicitly listed ininclude.Maybe[T]columns are unwrapped:Some(v)->v,Nothing->None. On a soft-delete model the auto-injecteddeleted_atkey 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);
Nonemeans all.exclude (Any) – field names to omit (blacklist);
Nonemeans 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
dictkeyed by field name.- Raises:
RelationNotLoaded – if a name in relations was not joined in the query.
- Return type:
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
ValidationErrorimmediately (fail-fast). Known keys are passed through the same set-event validators as normal assignment, soEmailis normalised,Passwordis 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:
- 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.Passwordfields are excluded by default (same policy asModel.to_dict()).E.g.:
AccountSchema = Account.to_schema() AccountSchema = Account.to_schema(exclude={"created_at"}) instance = AccountSchema(id=1, email="a@b.com")
- Parameters:
- Returns:
a
pydantic.BaseModelsubclass named<Model>Schema.- Return type:
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()).Passwordfields 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:
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]¶
-
Generic field for plain python-typed columns (str/int/…).
python_type is set by the pipeline.
- Parameters:
- class alchemiq.ForeignKey(on_delete=None, related_name=None)[source]¶
Bases:
objectOptional 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 customiseon_deletebehaviour or to resolve arelated_namecollision.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:
See also
OneToOne- unique FK with a scalar reverse accessor.
- class alchemiq.ManyToMany(related_name=None, secondary=None)[source]¶
Bases:
objectOptional 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_. UseManyToMany(...)to supply a customrelated_nameorsecondarytable 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:
See also
ForeignKey- many-to-one relationship.
- class alchemiq.OneToOne[source]¶
Bases:
GenericMarker for a one-to-one relationship:
profile: OneToOne[Profile].Wires a unique, non-nullable FK column
<name>_idon 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_idFK column is added automatically;profile.useris the reverse scalar accessor (_snake(cls.__name__)).Note
Under
TYPE_CHECKINGOneToOne[T]is an alias forT, 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]¶
-
Base of every Alchemiq field type. Public, user-subclassable.
- Parameters:
- abstractmethod column_type()[source]¶
SQLAlchemy storage type (or TypeDecorator) for this field.
- Return type:
- validate(value)[source]¶
Eager validation + normalization. Override to enforce rules. Identity by default.