Skip to content

Commit

Permalink
SeaORM 0.12.x Docs - 1 (#99)
Browse files Browse the repository at this point in the history
* Optional Field SeaQL/sea-orm#1513

* .gitignore SeaQL/sea-orm#1334

* migration table custom name SeaQL/sea-orm#1511

* OR condition relation SeaQL/sea-orm#1433

* DerivePartialModel SeaQL/sea-orm#1597

* space for migration file naming SeaQL/sea-orm#1570

* composite key up to 12 SeaQL/sea-orm#1508

* seaography integration SeaQL/sea-orm#1599

* QuerySelect SimpleExpr SeaQL/sea-orm#1702

* sqlErr SeaQL/sea-orm#1707

* migration check SeaQL/sea-orm#1519

* postgres array SeaQL/sea-orm#1565

* param intoString SeaQL/sea-orm#1439

* **skipped** re-export SeaQL/sea-orm#1661

* ping SeaQL/sea-orm#1627

* on empty do nothing SeaQL/sea-orm#1708

* on conflict do nothing SeaQL/sea-orm#1712

* **skipped** upgrade versions

* active enum fail safe SeaQL/sea-orm#1374

* relation generation check SeaQL/sea-orm#1435

* entity generation bug SeaQL/sea-schema#105

* **skipped** bug fix that does not require edits

* EnumIter change SeaQL/sea-orm#1535

* completed and fixed a previous todo SeaQL/sea-orm#1570

* amended wordings and structures

* Edit

* Remove temp file

---------

Co-authored-by: Billy Chan <ccw.billy.123@gmail.com>
  • Loading branch information
darkmmon and billy1624 committed Jul 19, 2023
1 parent 15f69c6 commit f9ea2e2
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ Basically, they are in the form of `runtime-ASYNC_RUNTIME-TLS_LIB`:
+ `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
+ `postgres-array` - support array types in Postgres (automatically enabled when `sqlx-postgres` feature is turned on)
+ `sea-orm-internal` - opt-in unstable internal APIs (for accessing re-export SQLx types)
16 changes: 14 additions & 2 deletions SeaORM/docs/02-install-and-config/02-connection.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Multiple queries will execute in parallel as you `await` on them.
To configure the connection, use the [`ConnectOptions`](https://docs.rs/sea-orm/*/sea_orm/struct.ConnectOptions.html) interface:

```rust
let mut opt = ConnectOptions::new("protocol://username:password@host/database".to_owned());
let mut opt = ConnectOptions::new("protocol://username:password@host/database");
opt.max_connections(100)
.min_connections(5)
.connect_timeout(Duration::from_secs(8))
Expand All @@ -30,11 +30,23 @@ opt.max_connections(100)
.max_lifetime(Duration::from_secs(8))
.sqlx_logging(true)
.sqlx_logging_level(log::LevelFilter::Info)
.set_schema_search_path("my_schema".into()); // Setting default PostgreSQL schema
.set_schema_search_path("my_schema"); // Setting default PostgreSQL schema

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

## Checking Connection is Valid

Checks if a connection to the database is still valid.

```rust
|db: DatabaseConnection| {
assert!(db.ping().await.is_ok());
db.clone().close().await;
assert!(matches!(db.ping().await, Err(DbErr::ConnectionAcquire)));
}
```

## Closing Connection

The connection will be automatically closed on drop. To close the connection explicitly, call the `close` method.
Expand Down
23 changes: 22 additions & 1 deletion SeaORM/docs/03-migration/01-setting-up-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,26 @@ If you are starting from a fresh database, it's better to version control your d

## Migration Table

A table named `seaql_migrations` will be created in your database to keep track of the applied migrations. It will be created automatically when you run the migration.
A table will be created in your database to keep track of the applied migrations. It will be created automatically when you run the migration.

By default, it will be named `seaql_migrations`. You can also use a custom name for your migration table.

```rust
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20220118_000001_create_cake_table::Migration),
Box::new(m20220118_000002_create_fruit_table::Migration),
]
}

// Override the name of migration table
fn migration_table_name() -> sea_orm::DynIden {
Alias::new("override_migration_table_name").into_iden()
}
}
```

## Creating Migration Directory

Expand Down Expand Up @@ -45,6 +64,8 @@ migration
└── main.rs # Migrator CLI, for running manually
```

Note that if you setup the migration directory directly within a Git repository, a `.gitignore` file will also be created.

## Workspace Structure

It is recommended to structure your cargo workspace as follows to share SeaORM entities between the app crate and the migration crate. Checkout the [integration examples](https://github.com/SeaQL/sea-orm/tree/master/examples) for demonstration.
Expand Down
5 changes: 5 additions & 0 deletions SeaORM/docs/03-migration/02-writing-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ Each migration contains two methods: `up` and `down`. The `up` method is used to

Generate a new migration file by executing `sea-orm-cli migrate generate` command.

If you name the file with spaces, it will be converted according to the convention automatically.

```shell
sea-orm-cli migrate generate NAME_OF_MIGRATION [--local-time]

# E.g. to generate `migration/src/m20220101_000001_create_table.rs` shown below
sea-orm-cli migrate generate create_table

# This create the same migration file as above command
sea-orm-cli migrate generate "create table"
```

Or you can create a migration file using the template below. Name the file according to the naming convention `mYYYYMMDD_HHMMSS_migration_name.rs`.
Expand Down
17 changes: 15 additions & 2 deletions SeaORM/docs/03-migration/03-running-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,24 @@ For CLI, you can specify the target schema with `-s` / `--database_schema` optio
You can also run the migration on the target schema programmatically:

```rust
let connect_options = ConnectOptions::new("postgres://root:root@localhost/database".into())
.set_schema_search_path("my_schema".into()) // Override the default schema
let connect_options = ConnectOptions::new("postgres://root:root@localhost/database")
.set_schema_search_path("my_schema") // Override the default schema
.to_owned();

let db = Database::connect(connect_options).await?

migration::Migrator::up(&db, None).await?;
```

## Checking Migration Status

You can use `Migration::name()` and `Migration::status()` to get the name and status of a `sea_orm_migration::Migration`

```rust
let migrations = Migrator::get_pending_migrations(db).await?;
assert_eq!(migrations.len(), 5);

let migration = migrations.get(0).unwrap();
assert_eq!(migration.name(), "m20220118_000002_create_fruit_table");
assert_eq!(migration.status(), MigrationStatus::Pending);
```
1 change: 1 addition & 0 deletions SeaORM/docs/04-generate-entity/01-sea-orm-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Command line options:
- `--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
- `--seaography`: an additional RelatedEntity enum for seaography integration will be generated.

```shell
# Generate entity files of database `bakery` to `entity/src`
Expand Down
2 changes: 2 additions & 0 deletions SeaORM/docs/04-generate-entity/02-entity-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ pub id: i32

This is usually the case in junction tables, where a two-column tuple is used as the primary key. Simply annotate multiple columns to define a composite primary key. By default, `auto_increment` is `false` for composite key.

You can define up to 12 primary key in a model.

```rust
pub struct Model {
#[sea_orm(primary_key)]
Expand Down
26 changes: 26 additions & 0 deletions SeaORM/docs/05-basic-crud/03-insert.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,18 @@ let res: InsertResult = Fruit::insert_many([apple, orange]).exec(db).await?;
assert_eq!(res.last_insert_id, 30)
```

Supplying an empty iterator to `insert_many` method will yield an error. However, you can change the behaviour with `on_empty_do_nothing` method to handle inserting an empty iterator properly.

```rust
// now, you can do:
let res = Bakery::insert_many(std::iter::empty())
.on_empty_do_nothing()
.exec(db)
.await;

assert!(matches!(res, Ok(TryInsertResult::Empty)));
```

## On Conflict

Insert active model with on conflict behaviour.
Expand Down Expand Up @@ -269,3 +281,17 @@ let res = Entity::insert_many([

assert_eq!(res.err(), Some(DbErr::RecordNotInserted));
```

Or you can use `.do_nothing()` to handle insert with conflict properly.

```rust
let on = OnConflict::column(Column::Id).do_nothing().to_owned();

// Existing behaviour
let res = Entity::insert_many([..]).on_conflict(on).exec(db).await;
assert!(matches!(res, Err(DbErr::RecordNotInserted)));

// you can also:
let res = Entity::insert_many([..]).on_conflict(on).do_nothing().exec(db).await;
assert!(matches!(res, Ok(TryInsertResult::Conflicted)));
```
6 changes: 3 additions & 3 deletions SeaORM/docs/05-basic-crud/08-raw-sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ You can build SQL statements using `sea-query` and query / execute it directly o
let query_res: Option<QueryResult> = db
.query_one(Statement::from_string(
DatabaseBackend::MySql,
"SELECT * FROM `cake`;".to_owned(),
"SELECT * FROM `cake`;",
))
.await?;
let query_res = query_res.unwrap();
Expand All @@ -97,7 +97,7 @@ let id: i32 = query_res.try_get("", "id")?;
let query_res_vec: Vec<QueryResult> = db
.query_all(Statement::from_string(
DatabaseBackend::MySql,
"SELECT * FROM `cake`;".to_owned(),
"SELECT * FROM `cake`;",
))
.await?;
```
Expand All @@ -108,7 +108,7 @@ let query_res_vec: Vec<QueryResult> = db
let exec_res: ExecResult = db
.execute(Statement::from_string(
DatabaseBackend::MySql,
"DROP DATABASE IF EXISTS `sea`;".to_owned(),
"DROP DATABASE IF EXISTS `sea`;",
))
.await?;
assert_eq!(exec_res.rows_affected(), 1);
Expand Down
41 changes: 41 additions & 0 deletions SeaORM/docs/06-relation/03-many-to-many.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,44 @@ pub enum Relation {
Filling,
}
```

Note that the implementation of `Related<R>` with `via` and `to` methods will not be generated if there exists multiple paths via an intermediate table.

For example, in the schema defined below, there are two paths:
- Path 1. `users <-> users_votes <-> bills`
- Path 2. `users <-> users_saved_bills <-> bills`

Therefore, the implementation of `Related<R>` will not be generated

```sql
CREATE TABLE users
(
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
email TEXT UNIQUE NOT NULL,
...
);
```
```sql
CREATE TABLE bills
(
id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
...
);
```
```sql
CREATE TABLE users_votes
(
user_id uuid REFERENCES users (id) ON UPDATE CASCADE ON DELETE CASCADE,
bill_id uuid REFERENCES bills (id) ON UPDATE CASCADE ON DELETE CASCADE,
vote boolean NOT NULL,
CONSTRAINT users_bills_pkey PRIMARY KEY (user_id, bill_id)
);
```
```sql
CREATE TABLE users_saved_bills
(
user_id uuid REFERENCES users (id) ON UPDATE CASCADE ON DELETE CASCADE,
bill_id uuid REFERENCES bills (id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT users_saved_bills_pkey PRIMARY KEY (user_id, bill_id)
);
```
25 changes: 24 additions & 1 deletion SeaORM/docs/06-relation/06-custom-join-condition.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ It can be done via one of the following ways.

Add your additional join conditions directly to the relation enum. The easiest way is to provide a `sea_query::SimpleExpr` via `on_condition` procedural macros attribute.

If you want to have a `OR` condition relation, you can use `condition_type = "any"` to alter the relation.

```rust
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
Expand All @@ -29,6 +31,13 @@ pub enum Relation {
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
)]
TropicalFruit,
// specify `condition_type = "any"` to override it
#[sea_orm(
has_many = "super::fruit::Entity",
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
condition_type = "any",
)]
OrTropicalFruit,
}
```

Expand Down Expand Up @@ -126,7 +135,7 @@ assert_eq!(

## Custom Join

Last but not least, custom join conditions can be defined at the point you construct the join expression.
Last but not least, custom join conditions can be defined at the point you construct the join expression. Overriding condition relation can also be done in custom join on the fly.

```rust
assert_eq!(
Expand All @@ -153,6 +162,19 @@ assert_eq!(
.into_condition()
})
)
.join_as_rev(
JoinType::LeftJoin,
cake_filling::Relation::Cake
.def()
// chained AND / OR join on condition
.condition_type(ConditionType::Any)
.on_condition(|left, _right| {
Expr::col((left, cake_filling::Column::CakeId))
.gt(10)
.into_condition()
}),
Alias::new("cake_filling_alias")
)
.join(JoinType::LeftJoin, filling::Relation::Vendor.def())
.build(DbBackend::MySql)
.to_string(),
Expand All @@ -161,6 +183,7 @@ assert_eq!(
"LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
"LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` AND `cake_filling`.`cake_id` > 10",
"LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id` AND `filling`.`name` LIKE '%lemon%'",
"LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` OR `cake_filling_alias`.`cake_id` > 10",
"LEFT JOIN `vendor` ON `filling`.`vendor_id` = `vendor`.`id`",
]
.join(" ")
Expand Down
66 changes: 66 additions & 0 deletions SeaORM/docs/08-advanced-query/01-custom-select.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,33 @@ assert_eq!(
);
```

### Optional field

Since 0.12, SeaORM supports for partial select of `Option<T>` model field. A `None` value will be filled when the select result does not contain the `Option<T>` field without throwing an error.

```rust
customer::ActiveModel {
name: Set("Alice".to_owned()),
notes: Set(Some("Want to communicate with Bob".to_owned())),
..Default::default()
}
.save(db)
.await?;

// The `notes` field was intentionally leaved out
let customer = Customer::find()
.select_only()
.column(customer::Column::Id)
.column(customer::Column::Name)
.one(db)
.await
.unwrap();

// The select result does not contain `notes` field.
// Since it's of type `Option<String>`, it'll be `None` and no error will be thrown.
assert_eq!(customers.notes, None);
```

## Select Custom Expressions

Select any custom expression with `column_as` method, it takes any [`sea_query::SimpleExpr`](https://docs.rs/sea-query/*/sea_query/expr/enum.SimpleExpr.html) and an alias. Use [`sea_query::Expr`](https://docs.rs/sea-query/*/sea_query/expr/struct.Expr.html) helper to build `SimpleExpr`.
Expand All @@ -74,6 +101,45 @@ assert_eq!(
);
```

Alternatively, you can simply select with `expr`, `exprs` and `expr_as` methods.

```rust
use sea_orm::sea_query::Expr;
use sea_orm::{entity::*, tests_cfg::cake, DbBackend, QuerySelect, QueryTrait};

assert_eq!(
cake::Entity::find()
.select_only()
.expr(Expr::col((cake::Entity, cake::Column::Id)))
.build(DbBackend::MySql)
.to_string(),
"SELECT `cake`.`id` FROM `cake`"
);

assert_eq!(
cake::Entity::find()
.select_only()
.exprs([
Expr::col((cake::Entity, cake::Column::Id)),
Expr::col((cake::Entity, cake::Column::Name)),
])
.build(DbBackend::MySql)
.to_string(),
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`"
);

assert_eq!(
cake::Entity::find()
.expr_as(
Func::upper(Expr::col((cake::Entity, cake::Column::Name))),
"name_upper"
)
.build(DbBackend::MySql)
.to_string(),
"SELECT `cake`.`id`, `cake`.`name`, UPPER(`cake`.`name`) AS `name_upper` FROM `cake`"
);
```

## Handling Select Results

### Custom Struct
Expand Down
Loading

0 comments on commit f9ea2e2

Please sign in to comment.