Add a model
Goal: add a new database-backed model to a Loco app — a migration, a Sea-ORM entity, and your own model file to extend it — using the model generator.
This assumes a working Loco app with the with-db feature enabled (the default). For the full field-type mini-language and every generator kind, see Generators & field types. For the migration DSL used under the hood, see Schema & ColType DSL.
1. Generate the model
Run the model generator with a name and a list of field:type pairs:
$ cargo loco generate model posts title:string! content:text user:referencesThis does three things in one step:
- Writes a migration under
migration/src/that creates apoststable. - Applies the migration against your development database.
- Regenerates Sea-ORM entities into
src/models/_entities/, and scaffoldssrc/models/posts.rsfor your own model code.
You end up with:
src/ models/ _entities/ posts.rs <-- generated entity (Entity, Model, ActiveModel, Column, Relation) posts.rs <-- your extension pointmigration/ src/ m20240101_000002_posts.rsSet the SKIP_MIGRATION environment variable if you want the generator to only write the migration file, without applying it or regenerating entities — useful when scripting several generate model calls back to back before running db migrate once at the end.
2. Read the field syntax
Each field:type pair follows a small suffix convention:
- no suffix → nullable column (
Option<T>) !→ required column (NOT NULL)^→ unique column (impliesNOT NULL)
So title:string! is a required String, and content:text is a nullable Option<String>.
user:references is special: it doesn’t name a column type, it declares a belongs-to foreign key. It adds a required user_id column referencing the users table (user:references? makes it nullable; user:references:author_id picks a custom column name). See Generators & field types § References for the full syntax.
int/int!/int^ field type now maps to i64 / BIGINT (big_integer), not i32 as in earlier Loco versions — matching the framework's i64 auto-increment primary keys. Use small_int if you specifically need a 16-bit column.
3. Add more fields to an existing model
To add columns to a table you already created, generate a plain migration instead of a new model — name it Add<Columns>To<Table> so Loco infers an “add columns” migration:
$ cargo loco generate migration AddViewsToPosts views:intApply it and regenerate entities:
$ cargo loco db migrate$ cargo loco db entitiesRemoving columns follows the mirror-image naming convention, Remove<Columns>From<Table>:
$ cargo loco generate migration RemoveViewsFromPosts views:int4. Generate without timestamps (optional)
By default every table generated through the DSL gets created_at/updated_at columns. To opt out, pass --without-tz to model, migration, or scaffold:
$ cargo loco generate model posts title:string! content:text --without-tz--without-tz, not --without-timestamps — an older spelling that no longer works.
5. Verify
Confirm the migration applied and entities exist:
$ cargo loco db status$ ls src/models/_entities/Then write against the model directly — e.g. in a cargo loco playground script or a test:
use migration::Migrator;use loco_rs::testing::prelude::*;use myapp::models::_entities::posts;
let boot = boot_test::<App, Migrator>().await?;let post = posts::ActiveModel { title: sea_orm::ActiveValue::set("hello".to_string()), user_id: sea_orm::ActiveValue::set(1), ..Default::default()}.insert(&boot.app_context.db).await?;
assert_eq!(post.title, "hello");Result: a posts table exists in your database, posts::Entity/Model/ActiveModel compile, and src/models/posts.rs is where you add custom methods (e.g. Model::find_by_title) the same way examples/demo/src/models/users.rs extends the generated users entity.
Next
- Query data with the
ConditionBuilderDSL. - Foreign-key relationships and request/model validation beyond a single table.