rustlang rdbms orm

SeaQL is an ORM for rust with an async runtime compatible with tokio. SeaQL is implemented on top of rust sqlx which means that example code for sqlx should work. For example the axum-login examples.

Entity Code Generation

It’s best to manually set up the entity create as per the guide

An example minimal Cargo.toml file for the entity crate might look like this:

[package]
name = "entity"
version = "0.1.0"
edition = "2021"
publish = false
 
[lib]
name = "entity"
path = "src/lib.rs"
 
[dependencies]
sea-orm = { version = "1.1.0" }

then we can use the cli to generate the entities from the db

sea generate entity -l -o entity/src

The -l flag tells seaql to generate a lib.rs file rather than a mod.rs file.

By default the tool will read DATABASE_URL from the .env file and use that to generate entities.

Migrations

Applying Multiple Changes in the same migration

As seen in the example docs we can use results and await? style syntax to apply multiple operations in the same migration.

 
// Remember to import `sea_orm_migration::schema::*` schema helpers into scope
use sea_orm_migration::{prelude::*, schema::*};
 
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
 
    manager
        .create_table(
            sea_query::Table::create()
                .table(Post::Table)
                .if_not_exists()
                .col(pk_auto(Post::Id))
                .col(string(Post::Title))
                .col(string(Post::Text))
        )
        .await?;
    
    manager
        .create_index(
            Index::create()
                .if_not_exists()
                .name("idx-post_title")
                .table(Post::Table)
                .col(Post::Title)                        
        )
        .await?;
    
    Ok(()) // All good!
}
 

Migrations inside the application

We need to make the migrations sub-crate a dependency of the main application:

Cargo.toml

 
...
[dependencies]
...
migration = { path="migration" }

Then we apply Migrator::up() to the database connection as described here

use migration::{Migrator, MigratorTrait};
 
let db_uri = dotenvy::var("DATABASE_URL").unwrap_or_else(|_| "".to_string());
let db = Database::connect(db_uri).await?;
 
// run necessary migrations
Migrator::up(&db, None).await?;
	
 

Inline Foreign Key

You don’t need to run separate commands to create foreign keys and in fact in Sqlite you can’t do that as it’s not possible to add foreign keys to existing tables.

Foreign keys can be added as part of a table migration up command

        manager
            .create_table(Table::create()
                  .table(Task::Table)
                  .if_not_exists()
                  .col(pk_auto(Task::Id))
                  .col(string(Task::Title))
                  .col(string(Task::Description))
                  .col(date_time(Task::CreatedAt))
                  .col(date_time(Task::UpdatedAt))
                  .col(integer(Task::UserId))
                  // add the foreign key
                  .foreign_key(ForeignKey::create()
                    .name("fk_task_user_id")
                    .from(Task::Table, Task::UserId)
                    .to(User::Table, User::Id)
                  )
                  .to_owned(),
            )
            .await?;