pub struct FieldSpec {Show 28 fields
pub name: &'static str,
pub ty: SqlType,
pub primary_key: bool,
pub nullable: bool,
pub supported_backends: &'static [&'static str],
pub fk_target: Option<&'static str>,
pub noform: bool,
pub db_constraint: bool,
pub noedit: bool,
pub is_string_repr: bool,
pub max_length: u32,
pub choices: &'static [&'static str],
pub choice_labels: &'static [&'static str],
pub default: &'static str,
pub is_multichoice: bool,
pub unique: bool,
pub on_delete: FkAction,
pub on_update: FkAction,
pub index: bool,
pub auto_now_add: bool,
pub auto_now: bool,
pub help: &'static str,
pub widget: Option<&'static str>,
pub example: &'static str,
pub min: Option<i64>,
pub max: Option<i64>,
pub text_format: Option<&'static str>,
pub slug_from: Option<&'static str>,
}Expand description
Static metadata for one column on a model.
Constructed once per field as a const, lives in Model::FIELDS.
Carries enough information for the QuerySet, the system check, and
the migration engine to do their jobs without the model needing any
runtime introspection.
Fields§
§name: &'static strThe SQL column name — always the Rust field name. Only the table name
is overridable (via #[umbral(table = "...")]); there is no field-level
column-rename attribute, so the column is whatever the field is called.
ty: SqlTypeThe SQL type kind. M2 ships the minimum set needed for the
hardcoded Post model (BigInt, Text, Timestamptz);
additional variants land as the M3 derive’s field-type
catalogue grows.
primary_key: boolWhether the column is part of the primary key.
nullable: boolWhether the column accepts SQL NULL. Maps from Option<T> in
the struct definition; the only path to NULL is Option<T>,
per the 04-orm-model-and-fields.md invariant.
supported_backends: &'static [&'static str]Which backends this field type works on. Empty slice means “all backends.” Non-empty restricts the field to those listed; the M4 boot system check rejects models that use a field on an unsupported backend.
fk_target: Option<&'static str>For SqlType::ForeignKey fields: the SQL table name of the
referenced model (i.e. T::TABLE). The migration engine reads
this at DDL-emit time to produce REFERENCES "<target>"("id").
None for all non-FK fields.
noform: boolWhen true, this field is never rendered on any form (create or
edit) AND the REST plugin drops it from POST/PUT/PATCH request
bodies before write. This is the framework’s “server-managed,
never accepts client input” flag — password_hash,
internal_token, audit timestamps the database owns.
Set via #[umbral(noform)].
OpenAPI emits readOnly: true for noform columns so Swagger
UI / generated clients honour the contract too. If you only
want the admin to render the field disabled — without affecting
the REST API or the spec — use noedit below.
If noform is true, noedit is moot (noform takes precedence).
db_constraint: boolFor SqlType::ForeignKey fields: whether the migration engine
emits a physical FOREIGN KEY ... REFERENCES constraint.
Toggles the physical FK constraint. Set via
#[umbral(db_constraint = false)]; defaults to true (today’s
behaviour — emit the constraint).
When false, the FK stays a logical relation: the column +
fk_target are unchanged, so joins, select_related, and the
app-level check_fk_row_exists pre-validation all keep working —
but no REFERENCES clause is rendered. This is the only way to
model an FK whose target lives on a different database (a real
DB constraint can’t span databases). The boot-time guard in
App::build rejects a cross-database FK that has NOT opted out
via this flag (BuildError::CrossDatabaseForeignKey). Closes
gaps2 #22. Ignored for non-FK fields.
noedit: boolWhen true, the admin shows this field disabled on the edit
form. Pure UX hint — no effect on the REST API or the OpenAPI
spec; clients can still POST/PUT/PATCH the column normally.
Set via #[umbral(noedit)].
Use case: a value the user supplies once at signup (email,
username) but isn’t supposed to change later through the
admin. The REST API may still accept updates — gate that
separately via ResourceConfig::hide(...) or a permission
class if you want hard enforcement. To block writes entirely,
use noform instead.
Has no effect when noform is also set.
is_string_repr: boolWhen true, this field is the display string for the
model — the admin uses it as the default label in
list_display when the developer hasn’t specified one
explicitly. Set via #[umbral(string)] /
#[umbral(string = true)]. Only meaningful on String-typed
columns; on non-string columns the admin falls back to the PK.
max_length: u32Soft length cap for display. The admin truncates the value at
this many characters when rendering it in list_display so a
long body doesn’t blow out a column. 0 means no truncation.
Set via #[umbral(max_length = N)].
choices: &'static [&'static str]Closed-set values for a choices column, in declaration order.
Populated by the #[derive(Model)] macro for fields tagged
#[umbral(choices)] by reading <T as ChoiceField>::VALUES at
derive time. Empty slice means “not a choices field” — every
non-choices column uses the empty default.
The migration engine emits a Postgres CHECK (col IN (...))
constraint when this slice is non-empty; the admin renders a
<select> widget with these as the <option> values.
choice_labels: &'static [&'static str]Human-readable labels matching choices position-for-position.
Used by the admin to render the <select> widget’s option text.
Empty when choices is empty.
default: &'static strSQL DEFAULT clause for this column. Set via
#[umbral(default = "...")] — accepts a string literal that
the DDL pass passes verbatim into DEFAULT '<value>'. Empty
string means no default. Carried through to the migration
engine, which emits the DEFAULT on both CREATE TABLE and
ALTER TABLE ADD COLUMN.
is_multichoice: boolWhen true, this column is a MultiChoice<E> field: TEXT
storage holding a CSV of the variants of E. The choices and
choice_labels slices carry the same metadata as a single-valued
choices field — the admin uses is_multichoice to pick the
checkbox-chip widget over the <select> widget.
unique: boolWhen true, the migration engine emits a UNIQUE constraint
on this column at CREATE TABLE time. Set via
#[umbral(unique)]. Closes gap #65.
Scope at v1: applies to new tables only. Toggling unique
on an existing column does not generate an automatic
ALTER TABLE ADD CONSTRAINT — SQLite cannot add a unique
constraint without rebuilding the table, and the M8 diff
engine only watches ty and nullable. Add or remove
uniqueness on a live table via a hand-written migration
until the diff engine grows constraint-level ops.
Primary-key columns are already implicitly unique, so this
flag is a no-op on a PK field. Set it on every other column
that needs database-enforced uniqueness (username,
email, opaque tokens, slugs, etc.) so handler-level
pre-checks become unnecessary.
on_delete: FkActionReferential action emitted on DELETE of the FK target row.
Only meaningful when ty == ForeignKey; ignored for every
other column. Set via #[umbral(on_delete = "...")]. Closes
gap #68. Defaults to NoAction so existing migrations
don’t change shape.
on_update: FkActionReferential action emitted on UPDATE of the FK target row’s
primary key. Same FK-only semantics as on_delete; almost
nobody touches this in practice (PKs rarely move) but the
symmetry matches REFERENCES ... ON UPDATE ... and the
on_delete / on_update pair. Set via
#[umbral(on_update = "...")].
index: boolWhen true, the migration engine emits a single-column
CREATE INDEX statement alongside the CREATE TABLE. Set
via #[umbral(index)]. Closes BUG-4 in
bugs/tests/testBugs.md.
Index name convention: idx_<table>_<column>. Apps that
need a custom name, a multi-column index, or a partial
index write the CREATE INDEX by hand in a follow-up
migration.
auto_now_add: boolWhen true, the column gets populated with Utc::now() at
row-creation time only. Set via #[umbral(auto_now_add)].
Closes BUG-5 in bugs/tests/testBugs.md.
Where this fires: the dynamic write path
(DynQuerySet::insert_json, used by umbral-rest /
umbral-admin). The typed Manager::create(instance) path
is user-controlled — the caller passes whatever value they
chose at the struct-init site. v1 scope: the framework
auto-populates only when the body / form omits the field.
auto_now: boolWhen true, the column gets populated with Utc::now() on
every write (create AND update). Set via #[umbral(auto_now)]. Closes
BUG-5 in bugs/tests/testBugs.md.
Where this fires: the dynamic write path
(DynQuerySet::insert_json and update_json, used by
umbral-rest / umbral-admin). The typed paths stay
user-controlled at v1. Body-supplied values are kept —
users can override auto_now columns on the dynamic
path, matching the lenient “fill if missing” shape of
auto_now_add. An “always override” shape lands as
a future v2 toggle if a real consumer asks.
help: &'static strHuman-readable column description (help text).
Set via #[umbral(help = "...")]. Flows
through to:
- OpenAPI
descriptionon the property schema (closes playground-openapi-gaps item 5). - Admin form field hint (the small line below the input).
- Doc-comment-style introspection for any future code generator.
Empty string means “no description” — the OpenAPI emitter and admin form skip the surrounding markup when this is unset.
widget: Option<&'static str>Presentation hint for form-rendering surfaces. Set via
#[umbral(widget = "markdown" | "rte" | "textarea" | ...)];
None (the default) means “let the renderer pick by
SqlType”. features.md #4.
It is metadata only — the column’s SqlType, DDL, and
stored value are unchanged. A widget = "markdown" field is
still TEXT; the widget only tells the admin (or any plugin
form) to render a markdown editor instead of a bare
<textarea>, and pairs with the {{ value | markdown }}
filter on the display side. Excluded from the migration diff
for the same reason help / example are: no DB effect.
Renderers fall back to the SqlType-derived input for any
widget name they don’t recognise, so an unknown widget is a
soft no-op rather than an error — third-party plugins can ship
new widget names without the core knowing them.
example: &'static strSample value rendered as OpenAPI example on the property
schema. Set via #[umbral(example = "...")]. Closes
playground-openapi-gaps item 6.
Empty string means no example. Emitted as a JSON string in
the spec — clients that want typed examples can coerce on
their end. Pairs naturally with help to make a column’s
purpose clear in Swagger UI.
min: Option<i64>Optional numeric lower bound. Set via #[umbral(min = N)].
Closes IMP-3 from bugs/tests/testBugs.md. Flows to:
- OpenAPI
minimumon the property schema. - REST plugin’s dynamic write path pre-validation (400 response with a structured message).
- Future: HTML5
minattribute on admin form inputs.
i64::MIN sentinel means “no minimum”; the DDL +
OpenAPI emitters skip the constraint when this is the
sentinel value. Macro accepts integer literals only at
v1 (a Decimal-aware shape can land when there’s a real
consumer for decimal-typed validators).
max: Option<i64>Optional numeric upper bound. Set via #[umbral(max = N)].
Mirror of min; same plumbing on the OpenAPI / REST /
admin sides.
text_format: Option<&'static str>Constrained-text marker. None is a plain String /
SqlType::Text column; Some("slug" | "email" | "url") is
one of the validator wrapper types from
crate::orm::validators. Closes BUG-11/12/13. Flows to:
- OpenAPI
format: email/format: uri/patternon the property schema (the standard 3.0 markers). - REST plugin’s dynamic write path:
validate_text_formatpre-checks the body value and returns a structured 400 on a bad input. - Admin form: HTML5
type="email"/type="url"widget (when those land).
The marker is set by the macro classifier from the field type
— Slug → Some("slug"), Email → Some("email"),
Url → Some("url"). The wrapper type + marker stay in sync
because they’re produced from the same single match arm in
umbral-macros::classify_field_type.
slug_from: Option<&'static str>Source column for an auto-derived slug. Set via
#[umbral(slug_from = "title")] on a Slug / String field;
names a sibling column on the same model whose value seeds
this column at write time. Gap 109.
Where this fires: the dynamic write path
(crate::orm::DynQuerySet::insert_json +
crate::orm::DynQuerySet::update_json). On insert, an empty
or absent slug column is replaced by slugify(source_value)
derived from the source column in the same body. On update,
the slug is regenerated only when the source column is also
in the update payload, so callers who edit nothing but the
slug itself keep their hand-tuned value.
None is the default — no auto-derive. The string is a
column name (snake_case), not a Rust field name, so it must
match exactly what ends up in FieldSpec::name.
Trait Implementations§
impl Copy for FieldSpec
impl Eq for FieldSpec
impl StructuralPartialEq for FieldSpec
Auto Trait Implementations§
impl Freeze for FieldSpec
impl RefUnwindSafe for FieldSpec
impl Send for FieldSpec
impl Sync for FieldSpec
impl Unpin for FieldSpec
impl UnsafeUnpin for FieldSpec
impl UnwindSafe for FieldSpec
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);