Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SeaORM 0.11.x Docs - 1 #87

Merged
merged 26 commits into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
cf1a4d7
Update 02-writing-migration.md
tyt2y3 Jan 24, 2023
0098ce6
Update SeaORM/docs/03-migration/02-writing-migration.md
tyt2y3 Jan 29, 2023
4d95e3c
Merge branch 'master' into 0.11.x/docs-1
billy1624 Feb 2, 2023
5f05e84
Support various UUID formats that are available in `uuid::fmt` module…
billy1624 Feb 2, 2023
8c05ab7
Casting columns as a different data type on select, insert and update…
billy1624 Feb 2, 2023
ca2a9b6
Methods of `ActiveModelBehavior` receive db connection as a parameter…
billy1624 Feb 2, 2023
f9026a1
Added `execute_unprepared` method to `DatabaseConnection` and `Databa…
billy1624 Feb 2, 2023
55db732
Added `Select::into_tuple` to select rows as tuples (instead of defin…
billy1624 Feb 2, 2023
8ae5996
Generate `#[serde(skip)]` for hidden columns (SeaQL/sea-orm#1171, Sea…
billy1624 Feb 3, 2023
0f0b961
Generate entity with extra derives and attributes for model struct (S…
billy1624 Feb 3, 2023
ab3f516
Generate entity with extra derives and attributes for model struct (S…
billy1624 Feb 3, 2023
77c8cdf
async_trait
billy1624 Feb 3, 2023
e321105
Migrations are now performed inside a transaction for Postgres (SeaQL…
billy1624 Feb 3, 2023
707e687
`MockDatabase::append_exec_results()`, `MockDatabase::append_query_re…
billy1624 Feb 3, 2023
5375c34
Cleanup the use of `vec!` macros
billy1624 Feb 3, 2023
4370d44
Added `DatabaseConnection::close` (SeaQL/sea-orm#1236)
billy1624 Feb 3, 2023
4604cef
Added `ActiveValue::reset` to convert `Unchanged` into `Set` (SeaQL/s…
billy1624 Feb 3, 2023
5664549
Added `QueryTrait::apply_if` to optionally apply a filter (SeaQL/sea-…
billy1624 Feb 3, 2023
5227b62
Added the `sea-orm-internal` feature flag to expose some SQLx types (…
billy1624 Feb 3, 2023
528a8cc
Add `QuerySelect::columns` method - select multiple columns (SeaQL/se…
billy1624 Feb 3, 2023
d3a6c00
Merge remote-tracking branch 'origin/master' into 0.11.x/docs-1
billy1624 Feb 3, 2023
5a2ea69
Edit
tyt2y3 Feb 3, 2023
3b7b1cb
Update SeaORM/docs/02-install-and-config/02-connection.md
billy1624 Feb 3, 2023
71c369d
Update SeaORM/docs/05-basic-crud/03-insert.md
billy1624 Feb 3, 2023
eafc745
fmt
billy1624 Feb 3, 2023
c3d160a
Edit
tyt2y3 Feb 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions SeaORM/docs/02-install-and-config/01-database-and-async-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ Basically, they are in the form of `runtime-ASYNC_RUNTIME-TLS_LIB`:

## Extra features

`debug-print` - print every SQL statement to logger

`mock` - mock interface for unit testing
+ `debug-print` - print every SQL statement to logger
+ `mock` - mock interface for unit testing
+ `macros` - procedural macros for your convenient
+ `with-chrono` - support [`chrono`](https://crates.io/crates/chrono) types
+ `with-time` - support [`time`](https://crates.io/crates/time) types
+ `with-json` - support [`serde-json`](https://crates.io/crates/serde-json) types
+ `with-rust_decimal` - support [`rust_decimal`](https://crates.io/crates/rust_decimal) types
+ `with-bigdecimal` - support [`bigdecimal`](https://crates.io/crates/bigdecimal) types
+ `with-uuid` - support [`uuid`](https://crates.io/crates/uuid) types
+ `postgres-array` - support array types in Postgres
+ `sea-orm-internal` - opt-in unstable internal APIs
+ `DatabaseConnection::get_*_connection_pool()` for accessing the inner SQLx connection pool
+ Re-exporting SQLx errors
11 changes: 11 additions & 0 deletions SeaORM/docs/02-install-and-config/02-connection.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,14 @@ opt.max_connections(100)

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

## Closing Connection

To close the connection explicitly, call the `close` method.
billy1624 marked this conversation as resolved.
Show resolved Hide resolved

```rust
let db = Database::connect(url).await?;

// Closing connection here
db.close().await?;
```
2 changes: 1 addition & 1 deletion SeaORM/docs/03-migration/01-setting-up-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Replace the sample below with your own migration scripts
Expand Down
48 changes: 35 additions & 13 deletions SeaORM/docs/03-migration/02-writing-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
Expand All @@ -46,7 +46,7 @@ mod m20220101_000001_create_table;

pub struct Migrator;

#[async_trait::async_trait]
#[async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Expand Down Expand Up @@ -212,26 +212,48 @@ use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let sql = r#"
CREATE TABLE `cake` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255) NOT NULL
)"#;
let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned());
manager.get_connection().execute(stmt).await.map(|_| ())
let db = manager.get_connection();

// Use `execute_unprepared` if the SQL statement doesn't have value bindings
db.execute_unprepared(
"CREATE TABLE `cake` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255) NOT NULL
)"
)
.await?;

// Construct a `Statement` if the SQL contains value bindings
let stmt = Statement::from_sql_and_values(
manager.get_database_backend(),
r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
["Cheese Cake".into()]
);
db.execute(stmt).await?;

Ok(())
}

async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let sql = "DROP TABLE `cake`";
let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned());
manager.get_connection().execute(stmt).await.map(|_| ())
manager
.get_connection()
.execute_unprepared("DROP TABLE `cake`")
.await?;

Ok(())
}
}
```

## Atomic Migration

Migration will be executed in Postgres atomically that means migration scripts will be executed inside a transaction. Changes done to the database will be rolled back if the migration failed. However, atomic migration is not supported in MySQL and SQLite.

You can start a transaction inside each migration to perform operations like [seeding sample data](03-migration/04-seeding-data.md#seeding-data-transactionally) for a newly created table.

## Schema first or Entity first?

In the grand scheme of things, we recommend a schema first approach: you write migrations first and then generate entities from a live database.
Expand Down
36 changes: 34 additions & 2 deletions SeaORM/docs/03-migration/04-seeding-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use sea_orm_migration::sea_orm::{entity::*, query::*};

// ...

#[async_trait::async_trait]
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
Expand All @@ -31,7 +31,7 @@ use sea_orm_migration::sea_orm::{entity::*, query::*};

// ...

#[async_trait::async_trait]
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let insert = Query::insert()
Expand All @@ -54,3 +54,35 @@ pub enum Cake {
Name,
}
```

## Seeding Data Transactionally

Starts a transaction and execute SQL inside migration up and down.

```rust
use sea_orm_migration::sea_orm::{entity::*, query::*};

// ...

#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Get the connection and start a transaction
let db = manager.get_connection();
let transaction = db.begin().await?;

// Insert with the transaction connection
cake::ActiveModel {
name: Set("Cheesecake".to_owned()),
..Default::default()
}
.insert(&transaction)
.await?;

// Commit it
transaction.commit().await?;

Ok(())
}
}
```
4 changes: 4 additions & 0 deletions SeaORM/docs/04-generate-entity/01-sea-orm-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ Command line options:
- `--compact-format`: generate entity file of [compact format](04-generate-entity/02-entity-structure.md) (default: true)
- `--expanded-format`: generate entity file of [expanded format](04-generate-entity/03-expanded-entity-structure.md)
- `--with-serde`: automatically derive serde Serialize / Deserialize traits for the entity (`none`, `serialize`, `deserialize`, `both`) (default: `none`)
- `--serde-skip-deserializing-primary-key`: generate entity model with primary key field labeled as `#[serde(skip_deserializing)]`
- `--serde-skip-hidden-column`: generate entity model with hidden column (column name starts with `_`) field labeled as `#[serde(skip)]`
- `--date-time-crate`: the datetime crate to use for generating entities (`chrono`, `time`) (default: `chrono`)
- `--max-connections`: maximum number of database connections to be initialized in the connection pool (default: `1`)
- `--model-extra-derives`: append extra derive macros to the generated model struct
- `--model-extra-attributes`: append extra attributes to generated model struct

```shell
# Generate entity files of database `bakery` to `entity/src`
Expand Down
14 changes: 13 additions & 1 deletion SeaORM/docs/04-generate-entity/02-entity-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ For the mappings of Rust non-primitive data types. You can check [`entity/prelud
| `DateTime`: chrono::NaiveDateTime <br/>`TimeDateTime`: time::PrimitiveDateTime | DateTime | text | datetime | timestamp |
| `DateTimeLocal`: chrono::DateTime&lt;Local&gt; <br/>`DateTimeUtc`: chrono::DateTime&lt;Utc&gt; | Timestamp | text | timestamp | N/A |
| `DateTimeWithTimeZone`: chrono::DateTime&lt;FixedOffset&gt; <br/>`TimeDateTimeWithTimeZone`: time::OffsetDateTime | TimestampWithTimeZone | text | timestamp | timestamp with time zone |
| `Uuid`: uuid::Uuid | Uuid | text | binary(16) | uuid |
| `Uuid`: uuid::Uuid, uuid::fmt::Braced, uuid::fmt::Hyphenated, uuid::fmt::Simple, uuid::fmt::Urn | Uuid | text | binary(16) | uuid |
| `Json`: serde_json::Value | Json | text | json | json |
| `Decimal`: rust_decimal::Decimal | Decimal | real | decimal | decimal |

Expand Down Expand Up @@ -162,6 +162,18 @@ If you want to ignore a particular model attribute such that it maps to no datab
pub ignore_me: String
```

### Cast Column Type on Select and Save

If you need to select a column as one type but save it into the database as another, you can specify the `select_as` and the `save_as` attributes to perform the casting. A typical use case is selecting a column of type `citext` (case-insensitive text) as `String` in Rust and saving it into the database as `citext`. One should define the model field as below:

```rust
#[sea_orm(
billy1624 marked this conversation as resolved.
Show resolved Hide resolved
select_as = "text",
save_as = "citext",
)]
pub case_insensitive_text: String
```

## Primary Key

Use the `primary_key` attribute to mark a column as the primary key.
Expand Down
45 changes: 41 additions & 4 deletions SeaORM/docs/04-generate-entity/03-expanded-entity-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ To specify the datatype of each column, the [`ColumnType`](https://docs.rs/sea-o
ColumnType::String(None).def().default_value("Sam").unique().indexed().nullable()
```

### Cast Column Type on Select and Save

If you need to select a column as one type but save it into the database as another, you can override the `select_as` and the `save_as` methods to perform the casting. A typical use case is selecting a column of type `citext` (case-insensitive text) as `String` in Rust and saving it into the database as `citext`. One should override the `ColumnTrait`'s methods as below:

```rust
use sea_orm::sea_query::{Expr, SimpleExpr, Alias}

impl ColumnTrait for Column {
// Snipped...

/// Cast column expression used in select statement.
fn select_as(&self, expr: Expr) -> SimpleExpr {
Column::CaseInsensitiveText => expr.cast_as(Alias::new("text")),
_ => self.select_enum_as(expr),
}

/// Cast value of a column into the correct type for database storage.
fn save_as(&self, val: Expr) -> SimpleExpr {
Column::CaseInsensitiveText => val.cast_as(Alias::new("citext")),
_ => self.save_enum_as(val),
}
}
```

## Primary Key

An enum representing the primary key of this table. A composite key is represented by an enum with multiple variants.
Expand Down Expand Up @@ -153,6 +177,7 @@ pub struct ActiveModel {
Handlers for different triggered actions on an `ActiveModel`. For example, you can perform custom validation logic, preventing a model from saving into database. You can abort an action even after it is done, if you are inside a transaction.

```rust
#[async_trait]
impl ActiveModelBehavior for ActiveModel {
/// Create a new ActiveModel with default values. Also used by `Default::default()`.
fn new() -> Self {
Expand All @@ -163,7 +188,10 @@ impl ActiveModelBehavior for ActiveModel {
}

/// Will be triggered before insert / update
fn before_save(self, insert: bool) -> Result<Self, DbErr> {
async fn before_save<C>(self, db: &C, insert: bool) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
if self.price.as_ref() <= &0.0 {
Err(DbErr::Custom(format!(
"[before_save] Invalid Price, insert: {}",
Expand All @@ -175,17 +203,26 @@ impl ActiveModelBehavior for ActiveModel {
}

/// Will be triggered after insert / update
fn after_save(model: Model, insert: bool) -> Result<Model, DbErr> {
async fn after_save<C>(model: Model, db: &C, insert: bool) -> Result<Model, DbErr>
where
C: ConnectionTrait,
{
Ok(model)
}

/// Will be triggered before delete
fn before_delete(self) -> Result<Self, DbErr> {
async fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
Ok(self)
}

/// Will be triggered after delete
fn after_delete(self) -> Result<Self, DbErr> {
async fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
Ok(self)
}
}
Expand Down
16 changes: 15 additions & 1 deletion SeaORM/docs/05-basic-crud/03-insert.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ let _: ActiveValue<i32> = Set(10);
let _: ActiveValue<i32> = NotSet;
```

### Reset ActiveValue

Calling `reset` method will convert `Unchanged` active value as `Set`.
billy1624 marked this conversation as resolved.
Show resolved Hide resolved

```rust
use sea_orm::ActiveValue;

// An `Unchanged` value
let active_value: ActiveValue<i32> = ActiveValue::Unchanged(10);

// Convert `Unchanged` active value as `Set`
assert!(active_value.reset(), ActiveValue::Set(10));
```

## Model & ActiveModel

An `ActiveModel` has all the attributes of `Model` wrapped in `ActiveValue`.
Expand Down Expand Up @@ -177,7 +191,7 @@ let orange = fruit::ActiveModel {
..Default::default()
};

let res: InsertResult = Fruit::insert_many(vec![apple, orange]).exec(db).await?;
let res: InsertResult = Fruit::insert_many([apple, orange]).exec(db).await?;
assert_eq!(res.last_insert_id, 30)
```

Expand Down
24 changes: 24 additions & 0 deletions SeaORM/docs/05-basic-crud/04-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,30 @@ let mut pear: fruit::ActiveModel = pear.unwrap().into();
pear.name = Set("Sweet pear".to_owned());

// Update corresponding row in database using primary key value
// SQL: `UPDATE "fruit" SET "name" = 'Sweet pear' WHERE "id" = 28`
let pear: fruit::Model = pear.update(db).await?;
```

### Update All Fields

You can make `Unchanged` fields "dirty".

```rust
let pear: Option<fruit::Model> = Fruit::find_by_id(28).one(db).await?;

// Into ActiveModel
let mut pear: fruit::ActiveModel = pear.unwrap().into();

// Update name attribute
pear.name = Set("Sweet pear".to_owned());

// Set a specific attribute as "dirty" (force update)
pear.reset(fruit::Column::CakeId);
// Or, set all attributes as "dirty" (force update)
pear.reset_all();

// Update corresponding row in database using primary key value
// SQL: `UPDATE "fruit" SET "name" = 'Sweet pear', "cake_id" = 10 WHERE "id" = 28`
let pear: fruit::Model = pear.update(db).await?;
```

Expand Down
2 changes: 1 addition & 1 deletion SeaORM/docs/05-basic-crud/07-json.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let cakes: Vec<serde_json::Value> = Cake::find()

assert_eq!(
cakes,
vec![
[
serde_json::json!({
"id": 2,
"name": "Chocolate Forest"
Expand Down
Loading