Skip to main content

8 posts tagged with "news"

View All Tags

ยท 4 min read
SeaQL Team

๐ŸŽ‰ 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.

ยท 4 min read
SeaQL Team

๐ŸŽ‰ 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.

ยท 4 min read
SeaQL Team

๐ŸŽ‰ 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.

ยท 2 min read
SeaQL Team

๐ŸŽ‰ 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.

ยท 5 min read
Chris Tsang

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
Jonas Rinner
Marco Napetti
Markus Merklinger
Muhannad
nitnelave
Raphaรซl Duchaรฎne
Rรฉmi Kalbe
Sam Samai

ยท One min read
Chris Tsang

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.

ยท One min read
Chris Tsang

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.

ยท One min read
Chris Tsang

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.