Cadmus
v0.1.2

Guides

Models

Describe your data; every model gets a table, admin pages and API endpoints. Field types and ModelOptions.

A model describes one kind of record: its fields and their types. Cadmus uses SeaORM entities as models.

#Generating a model

cadmus generate model Post title:string body:text published:bool views:int rating:float?

This writes src/models/post.rs:

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "post")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub title: String,
    #[sea_orm(column_type = "Text")]
    pub body: String,
    pub published: bool,
    pub views: i32,
    pub rating: Option<f64>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

and registers it in src/models/mod.rs. Model names are PascalCase (BlogPost); the table is named in snake_case (blog_post). An id primary key is always added.

#Field types

TypeRust typeStored as
stringStringshort text (varchar)
textStringlong text
inti3232-bit whole number
biginti6464-bit whole number
floatf64number with a fraction
boolbooltrue or false
datetimeDateTimedate and time, without a time zone
dateDatea date
jsonJsona JSON document
fileOption<i32>an uploaded file (see File uploads)
referencesi32a link to another model (see Relations)

Add ? to make a field optional (Option<...> in Rust, NULL in the database): summary:text?. File fields are always optional.

#Changing a model

Edit the file by hand: add, remove, rename or change fields, then make and apply a migration:

cargo run -- makemigrations
cargo run -- migrate

Useful SeaORM attributes on a field:

AttributeEffect
#[sea_orm(unique)]no two rows may have the same value
#[sea_orm(indexed)]an index, for faster lookups and sorting
#[sea_orm(default_value = "draft")]the value new rows get; also fills existing rows when the field is added
#[sea_orm(column_type = "Text")]long text instead of varchar
#[sea_orm(renamed_from = "body")]tells makemigrations the field was renamed, so the data is kept

#Admin options

Each model is registered with ModelOptions, which decide how it appears in the admin (and partly in the API):

app.model::<post::Entity>(
    ModelOptions::new()
        .label("Posts")                                   // name in the menu
        .list_display(["id", "title", "published"])       // columns on the list page
        .search_fields(["title", "body"])                 // what the search box looks in
        .list_filter(["published", "created_at"])         // filter dropdowns above the list
        .readonly_fields(["views"])                       // shown, but not editable
        .hidden_fields(["internal_note"])                 // never shown or sent anywhere
        .display_field("title")                           // how other models name a post
        .per_page(50)                                     // rows per list page
        .set_action("publish", "Mark as published", "published", "true"),
)
OptionWhat it does
label("Posts")the name shown in the menu and headings
list_display([...])the columns on the list page (default: all visible fields)
search_fields([...])the text fields the search box looks in; also ?q= in the API
list_filter([...])a filter dropdown per field above the list (see The admin site)
readonly_fields([...])shown but not editable, in the admin and the API
hidden_fields([...])never shown or accepted, in the admin, the API or its documentation
display_field("email")the field that names a record where other models link to it (default: the first text field)
per_page(n)rows per list page for this model
file_fields([...])integer fields that hold an uploaded file (set by field:file)
set_action(name, label, field, value)a bulk action setting a field on the selected rows
action(name, label, |ids| async move { ... })a bulk action running your own code with the selected ids

#Models without the generator

Any SeaORM entity can be registered with .model::<your_entity::Entity>(...), including ones you wrote by hand. Relations declared with belongs_to are picked up automatically. Models must have a single-column primary key to be edited.

A resource that is not a database table at all can be served by implementing the Resource trait and registering it with App::resource; see the API reference.