#[derive(Model)]
{
// Attributes available to this derive:
#[model]
#[field]
#[serde]
}
Expand description
Derive siderite::orm::Model: static column metadata, one typed
Field<Self, T> constant per field (User::name), to_values /
from_row, and the relation accessors.
One struct can derive Model, Validate, Schema, Serialize, Deserialize
together: the ORM keys of #[field(...)] are ignored by the other derives,
and Model ignores their validation keys (except max_length,
max_digits and decimal_places, which become column metadata).
§#[model(...)] on the struct
table = "users": table name. Default: the snake_case of the type name (BlogPostbecomesblog_post); there is no app prefix or pluralisation.ordering = ["name", "-created_at"]: default ordering; a leading-is descending. Entries are field names (or column names).indexes(idx_name(columns = ["a", "b"], unique)): named indexes.unique_together(["a", "b"], ["c", "d"]): unique constraints, named{table}_{columns}_uniq.checks(age_positive = "age >= 0"): namedCHECKconstraints. The SQL is written by the model author, never built from user input.managed = false: migrations leave the table alone.many_to_many(tags(Tag, through_table = "post_tags", source_column = "..", target_column = "..", related_name = "posts")): many-to-many relations (not struct fields). Defaults:through_tableis{table}_{name},source_columnis{snake(Model)}_idandtarget_columnis{snake(Target)}_id(set both explicitly for a self-referential relation).through = Modeluses an explicit through model: its table is the join table (through_tablemust then be omitted).
Indexes, unique groups and ordering are checked at compile time: unknown fields are errors.
§#[field(...)] ORM keys
primary_key: exactly one field must have it. Without a declared key the derive suggests#[field(primary_key, auto)] id: i64.auto: database-generated integer primary key (i16,i32,i64). The model is unsaved while the key equalsDefault::default(); a primary key withoutautois never “unsaved”.unique,index: constraint and single-column index.column = "name": column name (default: the field name, or{field}_idfor relations).db_default = <int | bool | "text">: server-side default.auto_now_add/auto_now:DbDefault::Nowat insert / refresh on every save (the queryset layer fills the value; the derive only emits metadata).on_delete = "cascade" | "protect" | "set_null" | "set_default" | "do_nothing"andrelated_name = "..": relation fields only.set_nullrequiresOption<ForeignKey<..>>.skip: not a column; the field isDefault::default()when loaded.max_length,max_digits,decimal_places: column metadata.
§Relations
Fields typed ForeignKey<T>, OneToOne<T> or Option<..> of those are
relations; a OneToOne is unique. The field’s constant is a
Field<Self, ForeignKey<T>> over the {field}_id column, and an async
fetch_{field}(&self, &Db) loads the target (the name differs from the
field’s because an associated constant and a method cannot share a name).
With related_name, the target type gets a reverse accessor
(author.books(&db) returning a QuerySet, or an async Option lookup
for a OneToOne); the target model must be defined in the same crate.
Each many-to-many gets post.tags(&db) returning a
ManyToManyManager, and with related_name a reverse
tag.posts(&db) queryset.
Generated accessors can clash with methods you write yourself; the compiler reports the duplicate.
Generic types are not supported.