DEV Community

Cover image for An async & dynamic ORM for Rust!
SeaQL
SeaQL

Posted on

An async & dynamic ORM for Rust!

SeaORM is a relational ORM to help you build web services in Rust with the familiarity of dynamic languages.

Getting Started

GitHub stars
If you like what we do, consider starring, commenting, sharing and contributing!

Discord
Join our Discord server to chat with others in the SeaQL community!

Features

  1. Async

    Relying on SQLx, SeaORM is a new library with async support from day 1.

  2. Dynamic

    Built upon SeaQuery, SeaORM allows you to build complex queries without 'fighting the ORM'.

  3. Testable

    Use mock connections to write unit tests for your logic.

  4. Service Oriented

    Quickly build services that join, filter, sort and paginate data in APIs.

A quick taste of SeaORM

Entity

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

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "cake")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub name: String,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(has_many = "super::fruit::Entity")]
    Fruit,
}

impl Related<super::fruit::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Fruit.def()
    }
}
Enter fullscreen mode Exit fullscreen mode

Select

// find all models
let cakes: Vec<cake::Model> = Cake::find().all(db).await?;

// find and filter
let chocolate: Vec<cake::Model> = Cake::find()
    .filter(cake::Column::Name.contains("chocolate"))
    .all(db)
    .await?;

// find one model
let cheese: Option<cake::Model> = Cake::find_by_id(1).one(db).await?;
let cheese: cake::Model = cheese.unwrap();

// find related models (lazy)
let fruits: Vec<fruit::Model> = cheese.find_related(Fruit).all(db).await?;

// find related models (eager)
let cake_with_fruits: Vec<(cake::Model, Vec<fruit::Model>)> =
    Cake::find().find_with_related(Fruit).all(db).await?;

Enter fullscreen mode Exit fullscreen mode

Insert

let apple = fruit::ActiveModel {
    name: Set("Apple".to_owned()),
    ..Default::default() // no need to set primary key
};

let pear = fruit::ActiveModel {
    name: Set("Pear".to_owned()),
    ..Default::default()
};

// insert one
let pear = pear.insert(db).await?;

// insert many
Fruit::insert_many(vec![apple, pear]).exec(db).await?;
Enter fullscreen mode Exit fullscreen mode

Update

use sea_orm::sea_query::{Expr, Value};

let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
let mut pear: fruit::ActiveModel = pear.unwrap().into();

pear.name = Set("Sweet pear".to_owned());

// update one
let pear: fruit::Model = pear.update(db).await?;

// update many: UPDATE "fruit" SET "cake_id" = NULL WHERE "fruit"."name" LIKE '%Apple%'
Fruit::update_many()
    .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
    .filter(fruit::Column::Name.contains("Apple"))
    .exec(db)
    .await?;

Enter fullscreen mode Exit fullscreen mode

Save

let banana = fruit::ActiveModel {
    id: NotSet,
    name: Set("Banana".to_owned()),
    ..Default::default()
};

// create, because primary key `id` is `NotSet`
let mut banana = banana.save(db).await?;

banana.name = Set("Banana Mongo".to_owned());

// update, because primary key `id` is `Set`
let banana = banana.save(db).await?;

Enter fullscreen mode Exit fullscreen mode

Delete

// delete one
let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
let orange: fruit::Model = orange.unwrap();
fruit::Entity::delete(orange.into_active_model())
    .exec(db)
    .await?;

// or simply
let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
let orange: fruit::Model = orange.unwrap();
orange.delete(db).await?;

// delete many: DELETE FROM "fruit" WHERE "fruit"."name" LIKE 'Orange'
fruit::Entity::delete_many()
    .filter(fruit::Column::Name.contains("Orange"))
    .exec(db)
    .await?;

Enter fullscreen mode Exit fullscreen mode

Learn More

  1. Design
  2. Architecture
  3. Release Model
  4. Change Log

Top comments (6)

Collapse
 
billy1624 profile image
Billy Chan • Edited

Hi there, I'm one of the core members in SeaQL :)

Collapse
 
batisteo profile image
Baptiste Darthenay

Hi! I’m wondering why the column name start with a capital letter (Column::Name), when it was define lowercase (name):

Cake::find().filter(cake::Column::Name.contains("chocolate"))
Enter fullscreen mode Exit fullscreen mode
Collapse
 
billy1624 profile image
Billy Chan

Good questions! Column is a Enum in Rust and its Enum variants (e.g. Column::Name) are conventionally defined in pascal case. Hence, Column::Name.

Thread Thread
 
batisteo profile image
Baptiste Darthenay

Thanks, makes sense! Where this is done? In SeaORM or SQLx, and I guess it’s done in a macro?

Thread Thread
 
billy1624 profile image
Billy Chan

It's done by SeaORM via prodedual macros. See the entity structure docs for details.

Collapse
 
seaql profile image
SeaQL

If you have any comments, suggestions and feedbacks, please leave it below. We'd loved to hear from the community!