Skip to main content

69 posts tagged with "news"

View All Tags

What's new in SeaORM 0.6.0

ยท 7 min read
SeaQL Team
Chris Tsang

๐ŸŽ‰ We are pleased to release SeaORM 0.6.0 today! Here are some feature highlights ๐ŸŒŸ:

Migrationโ€‹

[#335] Version control you database schema with migrations written in SeaQuery or in raw SQL. View migration docs to learn more.

  1. Setup the migration directory by executing sea-orm-cli migrate init.
    migration
    โ”œโ”€โ”€ Cargo.toml
    โ”œโ”€โ”€ README.md
    โ””โ”€โ”€ src
    โ”œโ”€โ”€ lib.rs
    โ”œโ”€โ”€ m20220101_000001_create_table.rs
    โ””โ”€โ”€ main.rs
  2. Defines the migration in SeaQuery.
    use sea_schema::migration::prelude::*;

    pub struct Migration;

    impl MigrationName for Migration {
    fn name(&self) -> &str {
    "m20220101_000001_create_table"
    }
    }

    #[async_trait::async_trait]
    impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
    manager
    .create_table( ... )
    .await
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
    manager
    .drop_table( ... )
    .await
    }
    }
  3. Apply the migration by executing sea-orm-cli migrate.
    $ sea-orm-cli migrate
    Applying all pending migrations
    Applying migration 'm20220101_000001_create_table'
    Migration 'm20220101_000001_create_table' has been applied

Designed by:



Chris Tsang

Contributed by:



Billy Chan

Support DateTimeUtc & DateTimeLocal in Modelโ€‹

[#489] Represents database's timestamp column in Model with attribute of type DateTimeLocal (chrono::DateTime<Local>) or DateTimeUtc (chrono::DateTime<Utc>).

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "satellite")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub satellite_name: String,
pub launch_date: DateTimeUtc,
pub deployment_date: DateTimeLocal,
}

Proposed by:



lz1998


Chris Tsang

Contributed by:



CharlesยทChege


Billy Chan

Mock Join Resultsโ€‹

[#455] Constructs mock results of related model with tuple of model.

let db = MockDatabase::new(DbBackend::Postgres)
// Mocking result of cake with its related fruit
.append_query_results(vec![vec![(
cake::Model {
id: 1,
name: "Apple Cake".to_owned(),
},
fruit::Model {
id: 2,
name: "Apple".to_owned(),
cake_id: Some(1),
},
)]])
.into_connection();

assert_eq!(
cake::Entity::find()
.find_also_related(fruit::Entity)
.all(&db)
.await?,
vec![(
cake::Model {
id: 1,
name: "Apple Cake".to_owned(),
},
Some(fruit::Model {
id: 2,
name: "Apple".to_owned(),
cake_id: Some(1),
})
)]
);

Proposed by:



Bastian

Contributed by:



Bastian


Billy Chan

Support Max Connection Lifetime Optionโ€‹

[#493] You can set the maximum lifetime of individual connection with the max_lifetime method.

let mut opt = ConnectOptions::new("protocol://username:password@host/database".to_owned());
opt.max_lifetime(Duration::from_secs(8))
.max_connections(100)
.min_connections(5)
.connect_timeout(Duration::from_secs(8))
.idle_timeout(Duration::from_secs(8))
.sqlx_logging(true);

let db = Database::connect(opt).await?;

Proposed by:



ร‰mile Fugulin

Contributed by:



Billy Chan

SeaORM CLI & Codegen Updatesโ€‹

  • [#433] Generates the column_name macro attribute for column which is not named in snake case
  • [#335] Introduces migration subcommands sea-orm-cli migrate

Proposed by:



Gabriel Paulucci

Contributed by:



Billy Chan

Our GitHub Sponsor profile is up! If you feel generous, a small donation will be greatly appreciated.

A big shout out to our sponsors ๐Ÿ˜‡:

ร‰mile Fugulin

Zachary Vander Velden

Shane Sveller

Sakti Dwi Cahyono

Unnamed Sponsor

Communityโ€‹

SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.

Here is the roadmap for SeaORM 0.7.x.

What's new in SeaORM 0.5.0

ยท 6 min read
SeaQL Team
Chris Tsang

๐ŸŽ‰ We are pleased to release SeaORM 0.5.0 today! Here are some feature highlights ๐ŸŒŸ:

Insert and Update Return Modelโ€‹

[#339] As asked and requested by many of our community members. You can now get the refreshed Model after insert or update operations. If you want to mutate the model and save it back to the database you can convert it into ActiveModel with the method into_active_model.

Breaking Changes:

  • ActiveModel::insert and ActiveModel::update return Model instead of ActiveModel
  • Method ActiveModelBehavior::after_save takes Model as input instead of ActiveModel
// Construct a `ActiveModel`
let active_model = ActiveModel {
name: Set("Classic Vanilla Cake".to_owned()),
..Default::default()
};
// Do insert
let cake: Model = active_model.insert(db).await?;
assert_eq!(
cake,
Model {
id: 1,
name: "Classic Vanilla Cake".to_owned(),
}
);

// Covert `Model` into `ActiveModel`
let mut active_model = cake.into_active_model();
// Change the name of cake
active_model.name = Set("Chocolate Cake".to_owned());
// Do update
let cake: Model = active_model.update(db).await?;
assert_eq!(
cake,
Model {
id: 1,
name: "Chocolate Cake".to_owned(),
}
);

// Do delete
cake.delete(db).await?;

Proposed by:



Julien Nicoulaud


Edgar

Contributed by:



Billy Chan

ActiveValue Revampedโ€‹

[#340] The ActiveValue is now defined as an enum instead of a struct. The public API of it remains unchanged, except Unset was deprecated and ActiveValue::NotSet should be used instead.

Breaking Changes:

  • Rename method sea_orm::unchanged_active_value_not_intended_for_public_use to sea_orm::Unchanged
  • Rename method ActiveValue::unset to ActiveValue::not_set
  • Rename method ActiveValue::is_unset to ActiveValue::is_not_set
  • PartialEq of ActiveValue will also check the equality of state instead of just checking the equality of value
/// Defines a stateful value used in ActiveModel.
pub enum ActiveValue<V>
where
V: Into<Value>,
{
/// A defined [Value] actively being set
Set(V),
/// A defined [Value] remain unchanged
Unchanged(V),
/// An undefined [Value]
NotSet,
}

Designed by:



Chris Tsang

Contributed by:



Billy Chan

SeaORM CLI & Codegen Updatesโ€‹

Install latest version of sea-orm-cli:

cargo install sea-orm-cli

Updates related to entity files generation (cargo generate entity):

  • [#348] Discovers and defines PostgreSQL enums
  • [#386] Supports SQLite database, you can generate entity files from all supported databases including MySQL, PostgreSQL and SQLite

Proposed by:



Zachary Vander Velden

Contributed by:



CharlesยทChege


Billy Chan

Tracingโ€‹

[#373] You can trace the query executed by SeaORM with debug-print feature enabled and tracing-subscriber up and running.

pub async fn main() {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.with_test_writer()
.init();

// ...
}

Contributed by:

Marco Napetti

Our GitHub Sponsor profile is up! If you feel generous, a small donation will be greatly appreciated.

A big shout out to our sponsors ๐Ÿ˜‡:

Sakti Dwi Cahyono

Shane Sveller

Zachary Vander Velden

Praveen Perera

Unnamed Sponsor

Communityโ€‹

SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.

Here is the roadmap for SeaORM 0.6.x.

What's new in SeaORM 0.4.0

ยท 5 min read
SeaQL Team
Chris Tsang

๐ŸŽ‰ We are pleased to release SeaORM 0.4.0 today! Here are some feature highlights ๐ŸŒŸ:

Rust Edition 2021โ€‹

[#273] Upgrading SeaORM to Rust Edition 2021 ๐Ÿฆ€โค๐Ÿš!

Contributed by:

Carter Snook

Enumerationโ€‹

[#252] You can now use Rust enums in model where the values are mapped to a database string, integer or native enum. Learn more here.

#[derive(Debug, Clone, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "active_enum")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
// Use our custom enum in a model
pub category: Option<Category>,
pub color: Option<Color>,
pub tea: Option<Tea>,
}

#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "String(Some(1))")]
// An enum serialized into database as a string value
pub enum Category {
#[sea_orm(string_value = "B")]
Big,
#[sea_orm(string_value = "S")]
Small,
}

#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "i32", db_type = "Integer")]
// An enum serialized into database as an integer value
pub enum Color {
#[sea_orm(num_value = 0)]
Black,
#[sea_orm(num_value = 1)]
White,
}

#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")]
// An enum serialized into database as a database native enum
pub enum Tea {
#[sea_orm(string_value = "EverydayTea")]
EverydayTea,
#[sea_orm(string_value = "BreakfastTea")]
BreakfastTea,
}

Designed by:



Chris Tsang

Contributed by:



Billy Chan

Supports RETURNING Clause on PostgreSQLโ€‹

[#183] When performing insert or update operation on ActiveModel against PostgreSQL, RETURNING clause will be used to perform select in a single SQL statement.

// For PostgreSQL
cake::ActiveModel {
name: Set("Apple Pie".to_owned()),
..Default::default()
}
.insert(&postgres_db)
.await?;

assert_eq!(
postgres_db.into_transaction_log(),
vec![Transaction::from_sql_and_values(
DbBackend::Postgres,
r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id", "name""#,
vec!["Apple Pie".into()]
)]);
// For MySQL & SQLite
cake::ActiveModel {
name: Set("Apple Pie".to_owned()),
..Default::default()
}
.insert(&other_db)
.await?;

assert_eq!(
other_db.into_transaction_log(),
vec![
Transaction::from_sql_and_values(
DbBackend::MySql,
r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
vec!["Apple Pie".into()]
),
Transaction::from_sql_and_values(
DbBackend::MySql,
r#"SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = ? LIMIT ?"#,
vec![15.into(), 1u64.into()]
)]);

Proposed by:



Marlon Brandรฃo de Sousa

Contributed by:



Billy Chan

Axum Integration Exampleโ€‹

[#297] Added Axum integration example. More examples wanted!

Contributed by:

Yoshiera

Our GitHub Sponsor profile is up! If you feel generous, a small donation will be greatly appreciated.

A big shout out to our first sponsors ๐Ÿ˜‡:

Shane Sveller

Zachary Vander Velden

Unnamed Sponsor

Communityโ€‹

SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.

Here is the roadmap for SeaORM 0.5.x.

What's new in SeaORM 0.3.0

ยท 5 min read
SeaQL Team
Chris Tsang

๐ŸŽ‰ We are pleased to release SeaORM 0.3.0 today! Here are some feature highlights ๐ŸŒŸ:

Transactionโ€‹

[#222] Use database transaction to perform atomic operations

Two transaction APIs are provided:

  • closure style. Will be committed on Ok and rollback on Err.

    // <Fn, A, B> -> Result<A, B>
    db.transaction::<_, _, DbErr>(|txn| {
    Box::pin(async move {
    bakery::ActiveModel {
    name: Set("SeaSide Bakery".to_owned()),
    ..Default::default()
    }
    .save(txn)
    .await?;

    bakery::ActiveModel {
    name: Set("Top Bakery".to_owned()),
    ..Default::default()
    }
    .save(txn)
    .await?;

    Ok(())
    })
    })
    .await;
  • RAII style. begin the transaction followed by commit or rollback. If txn goes out of scope, it'd automatically rollback.

    let txn = db.begin().await?;

    // do something with txn

    txn.commit().await?;

Contributed by:

Marco Napetti

Chris Tsang

Streamingโ€‹

[#222] Use async stream on any Select for memory efficiency.

let mut stream = Fruit::find().stream(&db).await?;

while let Some(item) = stream.try_next().await? {
let item: fruit::ActiveModel = item.into();
// do something with item
}

Contributed by:

Marco Napetti

API for custom logic on save & deleteโ€‹

[#210] We redefined the trait methods of ActiveModelBehavior. You can now perform custom validation before and after insert, update, save, delete actions. You can abort an action even after it is done, if you are inside a transaction.

impl ActiveModelBehavior for ActiveModel {
// Override default values
fn new() -> Self {
Self {
serial: Set(Uuid::new_v4()),
..ActiveModelTrait::default()
}
}

// Triggered before insert / update
fn before_save(self, insert: bool) -> Result<Self, DbErr> {
if self.price.as_ref() <= &0.0 {
Err(DbErr::Custom(format!(
"[before_save] Invalid Price, insert: {}",
insert
)))
} else {
Ok(self)
}
}

// Triggered after insert / update
fn after_save(self, insert: bool) -> Result<Self, DbErr> {
Ok(self)
}

// Triggered before delete
fn before_delete(self) -> Result<Self, DbErr> {
Ok(self)
}

// Triggered after delete
fn after_delete(self) -> Result<Self, DbErr> {
Ok(self)
}
}

Contributed by:

Muhannad

Billy Chan

Generate Entity Models That Derive Serialize / Deserializeโ€‹

[#237] You can use sea-orm-cli to generate entity models that also derive serde Serialize / Deserialize traits.

//! SeaORM Entity. Generated by sea-orm-codegen 0.3.0

use sea_orm::entity::prelude:: * ;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "cake")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(column_type = "Text", nullable)]
pub name: Option<String> ,
}

// ...

Contributed by:

Tim Eggert

Introduce DeriveIntoActiveModel macro & IntoActiveValue Traitโ€‹

[#240] introduced a new derive macro DeriveIntoActiveModel for implementing IntoActiveModel on structs. This is useful when creating your own struct with only partial fields of a model, for example as a form submission in a REST API.

IntoActiveValue trait allows converting Option<T> into ActiveValue<T> with the method into_active_value.

// Define regular model as usual
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
#[sea_orm(table_name = "users")]
pub struct Model {
pub id: Uuid,
pub created_at: DateTimeWithTimeZone,
pub updated_at: DateTimeWithTimeZone,
pub email: String,
pub password: String,
pub full_name: Option<String>,
pub phone: Option<String>,
}

// Create a new struct with some fields omitted
#[derive(DeriveIntoActiveModel)]
pub struct NewUser {
// id, created_at and updated_at are omitted from this struct,
// and will always be `ActiveValue::unset`
pub email: String,
pub password: String,
// Full name is usually optional, but it can be required here
pub full_name: String,
// Option implements `IntoActiveValue`, and when `None` will be `unset`
pub phone: Option<String>,
}

#[derive(DeriveIntoActiveModel)]
pub struct UpdateUser {
// Option<Option<T>> allows for Some(None) to update the column to be NULL
pub phone: Option<Option<String>>,
}

Contributed by:

Ari Seyhun

Communityโ€‹

SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.

Here is the roadmap for SeaORM 0.4.x.

What's new in SeaORM 0.2.4

ยท 3 min read
SeaQL Team
Chris Tsang

๐ŸŽ‰ We are pleased to release SeaORM 0.2.4 today! Some feature highlights:

Better ergonomic when working with custom select listโ€‹

[#208] Use Select::into_values to quickly select a custom column list and destruct as tuple.

use sea_orm::{entity::*, query::*, tests_cfg::cake, DeriveColumn, EnumIter};

#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
enum QueryAs {
CakeName,
NumOfCakes,
}

let res: Vec<(String, i64)> = cake::Entity::find()
.select_only()
.column_as(cake::Column::Name, QueryAs::CakeName)
.column_as(cake::Column::Id.count(), QueryAs::NumOfCakes)
.group_by(cake::Column::Name)
.into_values::<_, QueryAs>()
.all(&db)
.await?;

assert_eq!(
res,
vec![("Chocolate Forest".to_owned(), 2i64)]
);

Contributed by:

Muhannad

Rename column name & column enum variantโ€‹

[#209] Rename the column name and enum variant of a model attribute, especially helpful when the column name is a Rust keyword.

mod my_entity {
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "my_entity")]
pub struct Model {
#[sea_orm(primary_key, enum_name = "IdentityColumn", column_name = "id")]
pub id: i32,
#[sea_orm(column_name = "type")]
pub type_: String,
}

//...
}

assert_eq!(my_entity::Column::IdentityColumn.to_string().as_str(), "id");
assert_eq!(my_entity::Column::Type.to_string().as_str(), "type");

Contributed by:

Billy Chan

not on a condition treeโ€‹

[#145] Build a complex condition tree with Condition.

use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::Expr, DbBackend};

assert_eq!(
cake::Entity::find()
.filter(
Condition::all()
.add(
Condition::all()
.not()
.add(Expr::val(1).eq(1))
.add(Expr::val(2).eq(2))
)
.add(
Condition::any()
.add(Expr::val(3).eq(3))
.add(Expr::val(4).eq(4))
)
)
.build(DbBackend::Postgres)
.to_string(),
r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE (NOT (1 = 1 AND 2 = 2)) AND (3 = 3 OR 4 = 4)"#
);

Contributed by:

nitnelave

6xzo

Communityโ€‹

SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.

Here is the roadmap for SeaORM 0.3.x.

Introducing SeaORM ๐Ÿš

ยท 6 min read
Chris Tsang
SeaQL Team

We are pleased to introduce SeaORM 0.2.2 to the Rust community today. It's our pleasure to have received feedback and contributions from awesome people to SeaQuery and SeaORM since 0.1.0.

Rust is a wonderful language that can be used to build anything. One of the FAQs is "Are We Web Yet?", and if Rocket (or your favourite web framework) is Rust's Rail, then SeaORM is precisely Rust's ActiveRecord.

SeaORM is an async ORM built from the ground up, designed to play well with the async ecosystem, whether it's actix, async-std, tokio or any web framework built on top.

Let's have a quick tour of SeaORM.

Asyncโ€‹

Here is how you'd execute multiple queries in parallel:

// execute multiple queries in parallel
let cakes_and_fruits: (Vec<cake::Model>, Vec<fruit::Model>) =
futures::try_join!(Cake::find().all(&db), Fruit::find().all(&db))?;

Dynamicโ€‹

You can use SeaQuery to build complex queries without 'fighting the ORM':

// build subquery with ease
let cakes_with_filling: Vec<cake::Model> = cake::Entity::find()
.filter(
Condition::any().add(
cake::Column::Id.in_subquery(
Query::select()
.column(cake_filling::Column::CakeId)
.from(cake_filling::Entity)
.to_owned(),
),
),
)
.all(&db)
.await?;

More on SeaQuery

Testableโ€‹

To write unit tests, you can use our mock interface:

// Setup mock connection
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results(vec![
vec![
cake::Model {
id: 1,
name: "New York Cheese".to_owned(),
},
],
])
.into_connection();

// Perform your application logic
assert_eq!(
cake::Entity::find().one(&db).await?,
Some(cake::Model {
id: 1,
name: "New York Cheese".to_owned(),
})
);

// Compare it against the expected transaction log
assert_eq!(
db.into_transaction_log(),
vec![
Transaction::from_sql_and_values(
DbBackend::Postgres,
r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
vec![1u64.into()]
),
]
);

More on testing

Service Orientedโ€‹

Here is an example Rocket handler with pagination:

#[get("/?<page>&<posts_per_page>")]
async fn list(
conn: Connection<Db>,
page: Option<usize>,
per_page: Option<usize>,
) -> Template {
// Set page number and items per page
let page = page.unwrap_or(1);
let per_page = per_page.unwrap_or(10);

// Setup paginator
let paginator = Post::find()
.order_by_asc(post::Column::Id)
.paginate(&conn, per_page);
let num_pages = paginator.num_pages().await.unwrap();

// Fetch paginated posts
let posts = paginator
.fetch_page(page - 1)
.await
.expect("could not retrieve posts");

Template::render(
"index",
context! {
page: page,
per_page: per_page,
posts: posts,
num_pages: num_pages,
},
)
}

Full Rocket example

We are building more examples for other web frameworks too.

Peopleโ€‹

SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.

Core Membersโ€‹

Chris Tsang

Billy Chan

Contributorsโ€‹

As a courtesy, here is the list of SeaQL's early contributors (in alphabetic order):

Ari Seyhun

Ayomide Bamidele

Ben Armstead

Bobby Ng

Daniel Lyne

Hirtol

Sylvie Rinner

Marco Napetti

Markus Merklinger

Muhannad

nitnelave

Raphaรซl Duchaรฎne

Rรฉmi Kalbe

Sam Samai

Release Model

ยท One min read
Chris Tsang
SeaQL Team

Today we will outline our release plan in the near future.

One of Rust's slogan is Stability Without Stagnation, and SeaQL's take on it, is 'progression without stagnation'.

Before reaching 1.0, we will be releasing every week, incorporating the latest changes and merged pull requests. There will be at most one incompatible release per month, so you will be expecting 0.2 in Sep 2021 and 0.9 in Apr 2022. We will decide by then whether the next release is an incremental 0.10 or a stable 1.0.

After that, a major release will be rolled out every year. So you will probably be expecting a 2.0 in 2023.

All of these is only made possible with a solid infrastructure. While we have a test suite, its coverage will likely never be enough. We urge you to submit test cases to SeaORM if a particular feature is of importance to you.

We hope that a rolling release model will provide momentum to the community and propell us forward in the near future.

Hello World

ยท One min read
Chris Tsang
SeaQL Team

After 8 months of secrecy, SeaORM is now public!

The Rust async ecosystem is definitely thriving, with Tokio announcing Axum a week before.

We are now busy doing the brush ups to head towards our announcement in Sep.

If you stumbled upon us just now, well, hello! We sincerely invite you to be our alpha tester.

Welcome to SeaQL

ยท One min read
Chris Tsang
SeaQL Team

One year ago, when we were writing data processing algorithms in Rust, we needed an async library to interface with a database. Back then, there weren't many choices. So we have to write our own.

December last year, we released SeaQuery, and received welcoming responses from the community. We decided to push the project further and develop a full blown async ORM.

It has been a bumpy ride, as designing an async ORM requires working within and sometimes around Rust's unique type system. After several iterations of experimentation, I think we've attained a balance between static & dynamic and compile-time & run-time that it offers benefits of the Rust language while still be familiar and easy-to-work-with for those who come from other languages.

SeaORM is tentative to be released in Sep 2021 and stabilize in May 2022. We hope that SeaORM will become a go-to choice for working with databases in Rust and that the Rust language will be adopted by more organizations in building applications.

If you are intrigued like I do, please stay in touch and join the community.

Share your thoughts here.