Merge branch 'master' into ss/bakery
This commit is contained in:
commit
59322d37d8
31
.github/workflows/rust.yml
vendored
31
.github/workflows/rust.yml
vendored
@ -1,22 +1,33 @@
|
|||||||
name: Rust
|
name: sea-orm
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches:
|
||||||
|
- master
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
test:
|
||||||
|
name: Unit Test
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Build
|
|
||||||
run: cargo build --verbose
|
- uses: actions-rs/toolchain@v1
|
||||||
- name: Run tests
|
with:
|
||||||
run: cargo test --verbose
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
@ -6,6 +6,7 @@ members = [
|
|||||||
"sea-orm-cli",
|
"sea-orm-cli",
|
||||||
"examples/sqlx-mysql",
|
"examples/sqlx-mysql",
|
||||||
"examples/codegen",
|
"examples/codegen",
|
||||||
|
"examples/cli",
|
||||||
]
|
]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
|
@ -20,7 +20,7 @@ SeaSchema is our schema discovery library, but it is not sealed inside SeaORM. S
|
|||||||
|
|
||||||
In addition to the sync vs async foundation, the biggest distinction between Diesel and SeaORM is static vs dynamic. Diesel has an everything-compile-time design which has its pros and cons. SeaORM is dynamic, in which things are established runtime. It offers more flexibility. While you loses some compile-time guarantee, SeaORM helps you to prove correctness by unit testing instead.
|
In addition to the sync vs async foundation, the biggest distinction between Diesel and SeaORM is static vs dynamic. Diesel has an everything-compile-time design which has its pros and cons. SeaORM is dynamic, in which things are established runtime. It offers more flexibility. While you loses some compile-time guarantee, SeaORM helps you to prove correctness by unit testing instead.
|
||||||
|
|
||||||
Both libraries make heavy use of traits and generics, but SeaORM generate less types. (Each column in Diesel is a struct, while each column in SeaORM is a enum variant). That probably means looser type/lifetime constraints and faster compilation.
|
Both libraries make heavy use of traits and generics, but SeaORM generate less types (each column in Diesel is a struct, while each column in SeaORM is a enum variant) and less depthness (there won't be `A<B<C<D>>>`). That probably means looser type/lifetime constraints and faster compilation.
|
||||||
|
|
||||||
You don't have to use macros when using SeaORM. We provide some derive macros for convenience, but they are entirely optional.
|
You don't have to use macros when using SeaORM. We provide some derive macros for convenience, but they are entirely optional.
|
||||||
|
|
||||||
|
2
examples/cli/.env
Normal file
2
examples/cli/.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DATABASE_URI=mysql://sea:sea@localhost/bakery
|
||||||
|
DATABASE_SCHEMA=bakery
|
9
examples/cli/Cargo.toml
Normal file
9
examples/cli/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "sea-orm-cli-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sea-orm = { path = "../../", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "debug-print" ] }
|
||||||
|
strum = { version = "^0.20", features = [ "derive" ] }
|
18
examples/cli/README.md
Normal file
18
examples/cli/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# SeaORM CLI Example
|
||||||
|
|
||||||
|
Prepare:
|
||||||
|
|
||||||
|
Setup a test database and configure the connection string in `.env`.
|
||||||
|
Run `bakery.sql` to setup the test table and data.
|
||||||
|
|
||||||
|
Building sea-orm-cli:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
(cd ../../sea-orm-cli ; cargo build)
|
||||||
|
```
|
||||||
|
|
||||||
|
Generating entity:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
../../target/debug/sea-orm-cli generate entity -o src/entity
|
||||||
|
```
|
83
examples/cli/src/entity/cake.rs
Normal file
83
examples/cli/src/entity/cake.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"cake"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
CakeFilling,
|
||||||
|
Fruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
|
||||||
|
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake_filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::CakeFilling.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub fn find_cake_filling(&self) -> Select<super::cake_filling::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
90
examples/cli/src/entity/cake_filling.rs
Normal file
90
examples/cli/src/entity/cake_filling.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"cake_filling"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub cake_id: i32,
|
||||||
|
pub filling_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
CakeId,
|
||||||
|
FillingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
CakeId,
|
||||||
|
FillingId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Cake,
|
||||||
|
Filling,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::CakeId => ColumnType::Integer.def(),
|
||||||
|
Self::FillingId => ColumnType::Integer.def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
|
.from(Column::CakeId)
|
||||||
|
.to(super::cake::Column::Id)
|
||||||
|
.into(),
|
||||||
|
Self::Filling => Entity::belongs_to(super::filling::Entity)
|
||||||
|
.from(Column::FillingId)
|
||||||
|
.to(super::filling::Column::Id)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Cake.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Filling.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub fn find_cake(&self) -> Select<super::cake::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
pub fn find_filling(&self) -> Select<super::filling::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
72
examples/cli/src/entity/filling.rs
Normal file
72
examples/cli/src/entity/filling.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"filling"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
CakeFilling,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::CakeFilling => Entity::has_many(super::cake_filling::Entity).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake_filling::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::CakeFilling.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub fn find_cake_filling(&self) -> Select<super::cake_filling::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -47,7 +47,7 @@ impl ColumnTrait for Column {
|
|||||||
match self {
|
match self {
|
||||||
Self::Id => ColumnType::Integer.def(),
|
Self::Id => ColumnType::Integer.def(),
|
||||||
Self::Name => ColumnType::String(Some(255u32)).def(),
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
Self::CakeId => ColumnType::Integer.def(),
|
Self::CakeId => ColumnType::Integer.def().null(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ impl ColumnTrait for Column {
|
|||||||
impl RelationTrait for Relation {
|
impl RelationTrait for Relation {
|
||||||
fn def(&self) -> RelationDef {
|
fn def(&self) -> RelationDef {
|
||||||
match self {
|
match self {
|
||||||
Self::Cake => Entity::has_one(super::cake::Entity)
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
.from(Column::CakeId)
|
.from(Column::CakeId)
|
||||||
.to(super::cake::Column::Id)
|
.to(super::cake::Column::Id)
|
||||||
.into(),
|
.into(),
|
78
examples/cli/src/entity/vendor.rs
Normal file
78
examples/cli/src/entity/vendor.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"vendor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub fruit_id: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
FruitId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Fruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
Self::FruitId => ColumnType::Integer.def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Fruit => Entity::has_one(super::fruit::Entity)
|
||||||
|
.from(Column::FruitId)
|
||||||
|
.to(super::fruit::Column::Id)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
3
examples/cli/src/main.rs
Normal file
3
examples/cli/src/main.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod entity;
|
||||||
|
|
||||||
|
fn main() {}
|
92
examples/codegen/src/entity/fruit.rs
Normal file
92
examples/codegen/src/entity/fruit.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"fruit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub cake_id: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
CakeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Cake,
|
||||||
|
Vendor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
Self::CakeId => ColumnType::Integer.def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Cake => Entity::has_one(super::cake::Entity)
|
||||||
|
.from(Column::CakeId)
|
||||||
|
.to(super::cake::Column::Id)
|
||||||
|
.into(),
|
||||||
|
Self::Vendor => Entity::has_many(super::vendor::Entity)
|
||||||
|
.from(Column::Id)
|
||||||
|
.to(super::vendor::Column::FruitId)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Cake.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::vendor::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Vendor.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub fn find_cake(&self) -> Select<super::cake::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
pub fn find_vendor(&self) -> Select<super::vendor::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
7
examples/codegen/src/entity/mod.rs
Normal file
7
examples/codegen/src/entity/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
pub mod cake;
|
||||||
|
pub mod cake_filling;
|
||||||
|
pub mod filling;
|
||||||
|
pub mod fruit;
|
||||||
|
pub mod vendor;
|
7
examples/codegen/src/entity/prelude.rs
Normal file
7
examples/codegen/src/entity/prelude.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
pub use super::cake::Entity as Cake;
|
||||||
|
pub use super::cake_filling::Entity as CakeFilling;
|
||||||
|
pub use super::filling::Entity as Filling;
|
||||||
|
pub use super::fruit::Entity as Fruit;
|
||||||
|
pub use super::vendor::Entity as Vendor;
|
78
examples/codegen/src/entity/vendor.rs
Normal file
78
examples/codegen/src/entity/vendor.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.1.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||||
|
pub struct Entity;
|
||||||
|
|
||||||
|
impl EntityName for Entity {
|
||||||
|
fn table_name(&self) -> &str {
|
||||||
|
"vendor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
|
||||||
|
pub struct Model {
|
||||||
|
pub id: i32,
|
||||||
|
pub name: String,
|
||||||
|
pub fruit_id: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||||
|
pub enum Column {
|
||||||
|
Id,
|
||||||
|
Name,
|
||||||
|
FruitId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||||
|
pub enum PrimaryKey {
|
||||||
|
Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryKeyTrait for PrimaryKey {
|
||||||
|
fn auto_increment() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {
|
||||||
|
Fruit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnTrait for Column {
|
||||||
|
type EntityName = Entity;
|
||||||
|
fn def(&self) -> ColumnDef {
|
||||||
|
match self {
|
||||||
|
Self::Id => ColumnType::Integer.def(),
|
||||||
|
Self::Name => ColumnType::String(Some(255u32)).def(),
|
||||||
|
Self::FruitId => ColumnType::Integer.def(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
match self {
|
||||||
|
Self::Fruit => Entity::has_one(super::fruit::Entity)
|
||||||
|
.from(Column::FruitId)
|
||||||
|
.to(super::fruit::Column::Id)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Fruit.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
|
||||||
|
Entity::find_related().belongs_to::<Entity>(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -1,4 +1,4 @@
|
|||||||
mod out;
|
mod entity;
|
||||||
|
|
||||||
use sea_orm_codegen::{EntityGenerator, Error};
|
use sea_orm_codegen::{EntityGenerator, Error};
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ async fn main() -> Result<(), Error> {
|
|||||||
let _generator = EntityGenerator::discover(uri, schema)
|
let _generator = EntityGenerator::discover(uri, schema)
|
||||||
.await?
|
.await?
|
||||||
.transform()?
|
.transform()?
|
||||||
.generate("src/out")?;
|
.generate("src/entity")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -32,15 +32,27 @@ Model { id: 2, name: "Rasberry", cake_id: Some(1) }
|
|||||||
|
|
||||||
Model { id: 3, name: "Strawberry", cake_id: Some(2) }
|
Model { id: 3, name: "Strawberry", cake_id: Some(2) }
|
||||||
|
|
||||||
|
Model { id: 4, name: "Apple", cake_id: None }
|
||||||
|
|
||||||
|
Model { id: 5, name: "Banana", cake_id: None }
|
||||||
|
|
||||||
|
Model { id: 6, name: "Cherry", cake_id: None }
|
||||||
|
|
||||||
|
Model { id: 7, name: "Lemon", cake_id: None }
|
||||||
|
|
||||||
|
Model { id: 8, name: "Orange", cake_id: None }
|
||||||
|
|
||||||
|
Model { id: 9, name: "Pineapple", cake_id: None }
|
||||||
|
|
||||||
===== =====
|
===== =====
|
||||||
|
|
||||||
find cakes and fruits: SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id` FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`
|
find cakes and fruits: SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id` FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`
|
||||||
|
|
||||||
(Model { id: 1, name: "New York Cheese" }, Model { id: 2, name: "Rasberry", cake_id: Some(1) })
|
(Model { id: 1, name: "New York Cheese" }, Some(Model { id: 1, name: "Blueberry", cake_id: Some(1) }))
|
||||||
|
|
||||||
(Model { id: 1, name: "New York Cheese" }, Model { id: 1, name: "Blueberry", cake_id: Some(1) })
|
(Model { id: 1, name: "New York Cheese" }, Some(Model { id: 2, name: "Rasberry", cake_id: Some(1) }))
|
||||||
|
|
||||||
(Model { id: 2, name: "Chocolate Forest" }, Model { id: 3, name: "Strawberry", cake_id: Some(2) })
|
(Model { id: 2, name: "Chocolate Forest" }, Some(Model { id: 3, name: "Strawberry", cake_id: Some(2) }))
|
||||||
|
|
||||||
===== =====
|
===== =====
|
||||||
|
|
||||||
@ -48,7 +60,7 @@ find one by primary key: SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `ca
|
|||||||
|
|
||||||
Model { id: 1, name: "New York Cheese" }
|
Model { id: 1, name: "New York Cheese" }
|
||||||
|
|
||||||
find one by like: SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%chocolate%' LIMIT 1
|
find one by name: SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%chocolate%' LIMIT 1
|
||||||
|
|
||||||
Some(Model { id: 2, name: "Chocolate Forest" })
|
Some(Model { id: 2, name: "Chocolate Forest" })
|
||||||
|
|
||||||
@ -68,15 +80,11 @@ SelectResult { name: "Chocolate Forest", num_of_fruits: 1 }
|
|||||||
|
|
||||||
===== =====
|
===== =====
|
||||||
|
|
||||||
find cakes and fillings: SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `filling`.`id` AS `B_id`, `filling`.`name` AS `B_name` FROM `cake` LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`
|
find cakes and fillings: SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `filling`.`id` AS `B_id`, `filling`.`name` AS `B_name` FROM `cake` LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id` ORDER BY `cake`.`id` ASC
|
||||||
|
|
||||||
(Model { id: 1, name: "New York Cheese" }, Model { id: 1, name: "Vanilla" })
|
(Model { id: 1, name: "New York Cheese" }, [Model { id: 1, name: "Vanilla" }, Model { id: 2, name: "Lemon" }])
|
||||||
|
|
||||||
(Model { id: 1, name: "New York Cheese" }, Model { id: 2, name: "Lemon" })
|
(Model { id: 2, name: "Chocolate Forest" }, [Model { id: 2, name: "Lemon" }, Model { id: 3, name: "Mango" }])
|
||||||
|
|
||||||
(Model { id: 2, name: "Chocolate Forest" }, Model { id: 2, name: "Lemon" })
|
|
||||||
|
|
||||||
(Model { id: 2, name: "Chocolate Forest" }, Model { id: 3, name: "Mango" })
|
|
||||||
|
|
||||||
find fillings for cheese cake: SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 1 LIMIT 1
|
find fillings for cheese cake: SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 1 LIMIT 1
|
||||||
SELECT `filling`.`id`, `filling`.`name` FROM `filling` INNER JOIN `cake_filling` ON `cake_filling`.`filling_id` = `filling`.`id` INNER JOIN `cake` ON `cake`.`id` = `cake_filling`.`cake_id` WHERE `cake`.`id` = 1
|
SELECT `filling`.`id`, `filling`.`name` FROM `filling` INNER JOIN `cake_filling` ON `cake_filling`.`filling_id` = `filling`.`id` INNER JOIN `cake` ON `cake`.`id` = `cake_filling`.`cake_id` WHERE `cake`.`id` = 1
|
||||||
|
@ -51,10 +51,7 @@ impl ColumnTrait for Column {
|
|||||||
impl RelationTrait for Relation {
|
impl RelationTrait for Relation {
|
||||||
fn def(&self) -> RelationDef {
|
fn def(&self) -> RelationDef {
|
||||||
match self {
|
match self {
|
||||||
Self::Fruit => Entity::has_many(super::fruit::Entity)
|
Self::Fruit => Entity::has_many(super::fruit::Entity).into(),
|
||||||
.from(Column::Id)
|
|
||||||
.to(super::fruit::Column::CakeId)
|
|
||||||
.into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,14 +72,4 @@ impl Related<super::filling::Entity> for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
|
||||||
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
|
|
||||||
Entity::find_related().belongs_to::<Entity>(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_filling(&self) -> Select<super::filling::Entity> {
|
|
||||||
Entity::find_related().belongs_to::<Entity>(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
@ -53,11 +53,11 @@ impl ColumnTrait for Column {
|
|||||||
impl RelationTrait for Relation {
|
impl RelationTrait for Relation {
|
||||||
fn def(&self) -> RelationDef {
|
fn def(&self) -> RelationDef {
|
||||||
match self {
|
match self {
|
||||||
Self::Cake => Entity::has_one(super::cake::Entity)
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
.from(Column::CakeId)
|
.from(Column::CakeId)
|
||||||
.to(super::cake::Column::Id)
|
.to(super::cake::Column::Id)
|
||||||
.into(),
|
.into(),
|
||||||
Self::Filling => Entity::has_one(super::filling::Entity)
|
Self::Filling => Entity::belongs_to(super::filling::Entity)
|
||||||
.from(Column::FillingId)
|
.from(Column::FillingId)
|
||||||
.to(super::filling::Column::Id)
|
.to(super::filling::Column::Id)
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -62,10 +62,4 @@ impl Related<super::cake::Entity> for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
|
||||||
pub fn find_cake(&self) -> Select<super::cake::Entity> {
|
|
||||||
Entity::find_related().belongs_to::<Entity>(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
@ -35,7 +35,9 @@ impl PrimaryKeyTrait for PrimaryKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
pub enum Relation {}
|
pub enum Relation {
|
||||||
|
Cake,
|
||||||
|
}
|
||||||
|
|
||||||
impl ColumnTrait for Column {
|
impl ColumnTrait for Column {
|
||||||
type EntityName = Entity;
|
type EntityName = Entity;
|
||||||
@ -51,7 +53,18 @@ impl ColumnTrait for Column {
|
|||||||
|
|
||||||
impl RelationTrait for Relation {
|
impl RelationTrait for Relation {
|
||||||
fn def(&self) -> RelationDef {
|
fn def(&self) -> RelationDef {
|
||||||
panic!()
|
match self {
|
||||||
|
Self::Cake => Entity::belongs_to(super::cake::Entity)
|
||||||
|
.from(Column::CakeId)
|
||||||
|
.to(super::cake::Column::Id)
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::cake::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Cake.def()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +20,12 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), ExecErr> {
|
|||||||
name: Set("pear".to_owned()),
|
name: Set("pear".to_owned()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res = Fruit::insert(pear).exec(db).await?;
|
let res: InsertResult = Fruit::insert(pear).exec(db).await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
println!("Inserted: {:?}\n", res);
|
println!("Inserted: last_insert_id = {}\n", res.last_insert_id);
|
||||||
|
|
||||||
let pear = Fruit::find_by_id(res.last_insert_id)
|
let pear: Option<fruit::Model> = Fruit::find_by_id(res.last_insert_id)
|
||||||
.one(db)
|
.one(db)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ExecErr)?;
|
.map_err(|_| ExecErr)?;
|
||||||
@ -36,10 +36,10 @@ pub async fn insert_and_update(db: &DbConn) -> Result<(), ExecErr> {
|
|||||||
let mut pear: fruit::ActiveModel = pear.unwrap().into();
|
let mut pear: fruit::ActiveModel = pear.unwrap().into();
|
||||||
pear.name = Set("Sweet pear".to_owned());
|
pear.name = Set("Sweet pear".to_owned());
|
||||||
|
|
||||||
let res = Fruit::update(pear).exec(db).await?;
|
let pear: fruit::ActiveModel = Fruit::update(pear).exec(db).await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
println!("Updated: {:?}\n", res);
|
println!("Updated: {:?}\n", pear);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ pub async fn all_about_select(db: &DbConn) -> Result<(), QueryErr> {
|
|||||||
async fn find_all(db: &DbConn) -> Result<(), QueryErr> {
|
async fn find_all(db: &DbConn) -> Result<(), QueryErr> {
|
||||||
print!("find all cakes: ");
|
print!("find all cakes: ");
|
||||||
|
|
||||||
let cakes = Cake::find().all(db).await?;
|
let cakes: Vec<cake::Model> = Cake::find().all(db).await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
for cc in cakes.iter() {
|
for cc in cakes.iter() {
|
||||||
@ -66,7 +66,10 @@ async fn find_all(db: &DbConn) -> Result<(), QueryErr> {
|
|||||||
async fn find_together(db: &DbConn) -> Result<(), QueryErr> {
|
async fn find_together(db: &DbConn) -> Result<(), QueryErr> {
|
||||||
print!("find cakes and fruits: ");
|
print!("find cakes and fruits: ");
|
||||||
|
|
||||||
let both = Cake::find().left_join_and_select(Fruit).all(db).await?;
|
let both = Cake::find()
|
||||||
|
.find_also_related(Fruit)
|
||||||
|
.all(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
for bb in both.iter() {
|
for bb in both.iter() {
|
||||||
@ -102,7 +105,7 @@ async fn find_one(db: &DbConn) -> Result<(), QueryErr> {
|
|||||||
|
|
||||||
print!("find models belong to: ");
|
print!("find models belong to: ");
|
||||||
|
|
||||||
let fruits = cheese.find_fruit().all(db).await?;
|
let fruits = cheese.find_related(Fruit).all(db).await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
for ff in fruits.iter() {
|
for ff in fruits.iter() {
|
||||||
@ -141,7 +144,10 @@ async fn count_fruits_by_cake(db: &DbConn) -> Result<(), QueryErr> {
|
|||||||
async fn find_many_to_many(db: &DbConn) -> Result<(), QueryErr> {
|
async fn find_many_to_many(db: &DbConn) -> Result<(), QueryErr> {
|
||||||
print!("find cakes and fillings: ");
|
print!("find cakes and fillings: ");
|
||||||
|
|
||||||
let both = Cake::find().left_join_and_select(Filling).all(db).await?;
|
let both: Vec<(cake::Model, Vec<filling::Model>)> = Cake::find()
|
||||||
|
.find_with_related(Filling)
|
||||||
|
.all(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
for bb in both.iter() {
|
for bb in both.iter() {
|
||||||
@ -153,7 +159,7 @@ async fn find_many_to_many(db: &DbConn) -> Result<(), QueryErr> {
|
|||||||
let cheese = Cake::find_by_id(1).one(db).await?;
|
let cheese = Cake::find_by_id(1).one(db).await?;
|
||||||
|
|
||||||
if let Some(cheese) = cheese {
|
if let Some(cheese) = cheese {
|
||||||
let fillings: Vec<filling::Model> = cheese.find_filling().all(db).await?;
|
let fillings: Vec<filling::Model> = cheese.find_related(Filling).all(db).await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
for ff in fillings.iter() {
|
for ff in fillings.iter() {
|
||||||
@ -166,7 +172,7 @@ async fn find_many_to_many(db: &DbConn) -> Result<(), QueryErr> {
|
|||||||
let lemon = Filling::find_by_id(2).one(db).await?;
|
let lemon = Filling::find_by_id(2).one(db).await?;
|
||||||
|
|
||||||
if let Some(lemon) = lemon {
|
if let Some(lemon) = lemon {
|
||||||
let cakes: Vec<cake::Model> = lemon.find_cake().all(db).await?;
|
let cakes: Vec<cake::Model> = lemon.find_related(Cake).all(db).await?;
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
for cc in cakes.iter() {
|
for cc in cakes.iter() {
|
||||||
@ -211,7 +217,7 @@ async fn find_together_json(db: &DbConn) -> Result<(), QueryErr> {
|
|||||||
print!("find cakes and fruits: ");
|
print!("find cakes and fruits: ");
|
||||||
|
|
||||||
let cakes_fruits = Cake::find()
|
let cakes_fruits = Cake::find()
|
||||||
.left_join_and_select(Fruit)
|
.find_with_related(Fruit)
|
||||||
.into_json()
|
.into_json()
|
||||||
.all(db)
|
.all(db)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -12,10 +12,11 @@ keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite", "cli" ]
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "sea-orm"
|
name = "sea-orm-cli"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "^2.33.3" }
|
clap = { version = "^2.33.3" }
|
||||||
|
dotenv = { version = "^0.15" }
|
||||||
async-std = { version = "^1.9", features = [ "attributes" ] }
|
async-std = { version = "^1.9", features = [ "attributes" ] }
|
||||||
sea-orm-codegen = { path = "../sea-orm-codegen" }
|
sea-orm-codegen = { path = "../sea-orm-codegen" }
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use clap::{App, AppSettings, Arg, SubCommand};
|
use clap::{App, AppSettings, Arg, SubCommand};
|
||||||
|
|
||||||
pub fn build_cli() -> App<'static, 'static> {
|
pub fn build_cli() -> App<'static, 'static> {
|
||||||
let entity_subcommand = SubCommand::with_name("entity")
|
let entity_subcommand = SubCommand::with_name("generate")
|
||||||
.about("Entity related commands")
|
.about("Codegen related commands")
|
||||||
.setting(AppSettings::VersionlessSubcommands)
|
.setting(AppSettings::VersionlessSubcommands)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("generate")
|
SubCommand::with_name("entity")
|
||||||
.about("Generate entity")
|
.about("Generate entity")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("DATABASE_URI")
|
Arg::with_name("DATABASE_URI")
|
||||||
@ -13,7 +13,8 @@ pub fn build_cli() -> App<'static, 'static> {
|
|||||||
.short("u")
|
.short("u")
|
||||||
.help("Database URI")
|
.help("Database URI")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true),
|
.required(true)
|
||||||
|
.env("DATABASE_URI"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("DATABASE_SCHEMA")
|
Arg::with_name("DATABASE_SCHEMA")
|
||||||
@ -21,7 +22,8 @@ pub fn build_cli() -> App<'static, 'static> {
|
|||||||
.short("s")
|
.short("s")
|
||||||
.help("Database schema")
|
.help("Database schema")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true),
|
.required(true)
|
||||||
|
.env("DATABASE_SCHEMA"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("OUTPUT_DIR")
|
Arg::with_name("OUTPUT_DIR")
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
use dotenv::dotenv;
|
||||||
use sea_orm_codegen::EntityGenerator;
|
use sea_orm_codegen::EntityGenerator;
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
|
|
||||||
@ -6,19 +7,21 @@ mod cli;
|
|||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
dotenv().ok();
|
||||||
|
|
||||||
let matches = cli::build_cli().get_matches();
|
let matches = cli::build_cli().get_matches();
|
||||||
|
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("entity", Some(matches)) => run_entity_command(matches)
|
("generate", Some(matches)) => run_generate_command(matches)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(handle_error),
|
.unwrap_or_else(handle_error),
|
||||||
_ => unreachable!("You should never see this message"),
|
_ => unreachable!("You should never see this message"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_entity_command(matches: &ArgMatches<'_>) -> Result<(), Box<dyn Error>> {
|
async fn run_generate_command(matches: &ArgMatches<'_>) -> Result<(), Box<dyn Error>> {
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("generate", Some(args)) => {
|
("entity", Some(args)) => {
|
||||||
let uri = args.value_of("DATABASE_URI").unwrap();
|
let uri = args.value_of("DATABASE_URI").unwrap();
|
||||||
let schema = args.value_of("DATABASE_SCHEMA").unwrap();
|
let schema = args.value_of("DATABASE_SCHEMA").unwrap();
|
||||||
let output_dir = args.value_of("OUTPUT_DIR").unwrap();
|
let output_dir = args.value_of("OUTPUT_DIR").unwrap();
|
||||||
|
@ -9,6 +9,7 @@ pub struct Column {
|
|||||||
pub(crate) col_type: ColumnType,
|
pub(crate) col_type: ColumnType,
|
||||||
pub(crate) auto_increment: bool,
|
pub(crate) auto_increment: bool,
|
||||||
pub(crate) not_null: bool,
|
pub(crate) not_null: bool,
|
||||||
|
pub(crate) unique: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Column {
|
impl Column {
|
||||||
@ -50,7 +51,7 @@ impl Column {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_def(&self) -> TokenStream {
|
pub fn get_def(&self) -> TokenStream {
|
||||||
match &self.col_type {
|
let mut col_def = match &self.col_type {
|
||||||
ColumnType::Char(s) => match s {
|
ColumnType::Char(s) => match s {
|
||||||
Some(s) => quote! { ColumnType::Char(Some(#s)).def() },
|
Some(s) => quote! { ColumnType::Char(Some(#s)).def() },
|
||||||
None => quote! { ColumnType::Char(None).def() },
|
None => quote! { ColumnType::Char(None).def() },
|
||||||
@ -86,7 +87,18 @@ impl Column {
|
|||||||
let s = s.to_string();
|
let s = s.to_string();
|
||||||
quote! { ColumnType::Custom(#s.to_owned()).def() }
|
quote! { ColumnType::Custom(#s.to_owned()).def() }
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if !self.not_null {
|
||||||
|
col_def.extend(quote! {
|
||||||
|
.null()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
if self.unique {
|
||||||
|
col_def.extend(quote! {
|
||||||
|
.unique()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
col_def
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,11 +127,21 @@ impl From<&ColumnDef> for Column {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let not_null = !not_nulls.is_empty();
|
let not_null = !not_nulls.is_empty();
|
||||||
|
let uniques: Vec<bool> = col_def
|
||||||
|
.get_column_spec()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|spec| match spec {
|
||||||
|
ColumnSpec::UniqueKey => Some(true),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let unique = !uniques.is_empty();
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
col_type,
|
col_type,
|
||||||
auto_increment,
|
auto_increment,
|
||||||
not_null,
|
not_null,
|
||||||
|
unique,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,10 @@ impl Entity {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_relation_defs(&self) -> Vec<TokenStream> {
|
||||||
|
self.relations.iter().map(|rel| rel.get_def()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_relation_rel_types(&self) -> Vec<Ident> {
|
pub fn get_relation_rel_types(&self) -> Vec<Ident> {
|
||||||
self.relations
|
self.relations
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
use heck::{CamelCase, SnakeCase};
|
use heck::{CamelCase, SnakeCase};
|
||||||
use proc_macro2::Ident;
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::format_ident;
|
use quote::{format_ident, quote};
|
||||||
use sea_orm::RelationType;
|
|
||||||
use sea_query::TableForeignKey;
|
use sea_query::TableForeignKey;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum RelationType {
|
||||||
|
HasOne,
|
||||||
|
HasMany,
|
||||||
|
BelongsTo,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Relation {
|
pub struct Relation {
|
||||||
pub(crate) ref_table: String,
|
pub(crate) ref_table: String,
|
||||||
@ -21,10 +27,33 @@ impl Relation {
|
|||||||
format_ident!("{}", self.ref_table.to_camel_case())
|
format_ident!("{}", self.ref_table.to_camel_case())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_def(&self) -> TokenStream {
|
||||||
|
let rel_type = self.get_rel_type();
|
||||||
|
let ref_table_snake_case = self.get_ref_table_snake_case();
|
||||||
|
match self.rel_type {
|
||||||
|
RelationType::HasOne | RelationType::HasMany => {
|
||||||
|
quote! {
|
||||||
|
Entity::#rel_type(super::#ref_table_snake_case::Entity).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RelationType::BelongsTo => {
|
||||||
|
let column_camel_case = self.get_column_camel_case();
|
||||||
|
let ref_column_camel_case = self.get_ref_column_camel_case();
|
||||||
|
quote! {
|
||||||
|
Entity::#rel_type(super::#ref_table_snake_case::Entity)
|
||||||
|
.from(Column::#column_camel_case)
|
||||||
|
.to(super::#ref_table_snake_case::Column::#ref_column_camel_case)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_rel_type(&self) -> Ident {
|
pub fn get_rel_type(&self) -> Ident {
|
||||||
match self.rel_type {
|
match self.rel_type {
|
||||||
RelationType::HasOne => format_ident!("has_one"),
|
RelationType::HasOne => format_ident!("has_one"),
|
||||||
RelationType::HasMany => format_ident!("has_many"),
|
RelationType::HasMany => format_ident!("has_many"),
|
||||||
|
RelationType::BelongsTo => format_ident!("belongs_to"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +78,7 @@ impl From<&TableForeignKey> for Relation {
|
|||||||
};
|
};
|
||||||
let columns = tbl_fk.get_columns();
|
let columns = tbl_fk.get_columns();
|
||||||
let ref_columns = tbl_fk.get_ref_columns();
|
let ref_columns = tbl_fk.get_ref_columns();
|
||||||
let rel_type = RelationType::HasOne;
|
let rel_type = RelationType::BelongsTo;
|
||||||
Self {
|
Self {
|
||||||
ref_table,
|
ref_table,
|
||||||
columns,
|
columns,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::{Entity, EntityWriter, Error, PrimaryKey, Relation};
|
use crate::{Column, Entity, EntityWriter, Error, PrimaryKey, Relation, RelationType};
|
||||||
use sea_orm::RelationType;
|
|
||||||
use sea_query::TableStatement;
|
use sea_query::TableStatement;
|
||||||
use sea_schema::mysql::def::Schema;
|
use sea_schema::mysql::def::Schema;
|
||||||
use std::{collections::HashMap, mem::swap};
|
use std::{collections::HashMap, mem::swap};
|
||||||
@ -31,11 +30,16 @@ impl EntityTransformer {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let columns = table_create
|
let columns: Vec<Column> = table_create
|
||||||
.get_columns()
|
.get_columns()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|col_def| col_def.into())
|
.map(|col_def| col_def.into())
|
||||||
.collect();
|
.collect();
|
||||||
|
let unique_columns: Vec<String> = columns
|
||||||
|
.iter()
|
||||||
|
.filter(|col| col.unique)
|
||||||
|
.map(|col| col.name.clone())
|
||||||
|
.collect();
|
||||||
let relations = table_create
|
let relations = table_create
|
||||||
.get_foreign_key_create_stmts()
|
.get_foreign_key_create_stmts()
|
||||||
.iter()
|
.iter()
|
||||||
@ -64,9 +68,22 @@ impl EntityTransformer {
|
|||||||
entities.push(entity);
|
entities.push(entity);
|
||||||
for mut rel in relations.into_iter() {
|
for mut rel in relations.into_iter() {
|
||||||
let ref_table = rel.ref_table;
|
let ref_table = rel.ref_table;
|
||||||
swap(&mut rel.columns, &mut rel.ref_columns);
|
let mut unique = true;
|
||||||
rel.rel_type = RelationType::HasMany;
|
for col in rel.columns.iter() {
|
||||||
|
if !unique_columns.contains(col) {
|
||||||
|
unique = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let rel_type = if unique {
|
||||||
|
RelationType::HasOne
|
||||||
|
} else {
|
||||||
|
RelationType::HasMany
|
||||||
|
};
|
||||||
|
rel.rel_type = rel_type;
|
||||||
rel.ref_table = table_name.clone();
|
rel.ref_table = table_name.clone();
|
||||||
|
rel.columns = Vec::new();
|
||||||
|
rel.ref_columns = Vec::new();
|
||||||
if let Some(vec) = inverse_relations.get_mut(&ref_table) {
|
if let Some(vec) = inverse_relations.get_mut(&ref_table) {
|
||||||
vec.push(rel);
|
vec.push(rel);
|
||||||
} else {
|
} else {
|
||||||
|
@ -217,20 +217,14 @@ impl EntityWriter {
|
|||||||
|
|
||||||
pub fn gen_impl_relation_trait(entity: &Entity) -> TokenStream {
|
pub fn gen_impl_relation_trait(entity: &Entity) -> TokenStream {
|
||||||
let relation_ref_tables_camel_case = entity.get_relation_ref_tables_camel_case();
|
let relation_ref_tables_camel_case = entity.get_relation_ref_tables_camel_case();
|
||||||
let relation_rel_types = entity.get_relation_rel_types();
|
let relation_defs = entity.get_relation_defs();
|
||||||
let relation_ref_tables_snake_case = entity.get_relation_ref_tables_snake_case();
|
|
||||||
let relation_columns_camel_case = entity.get_relation_columns_camel_case();
|
|
||||||
let relation_ref_columns_camel_case = entity.get_relation_ref_columns_camel_case();
|
|
||||||
let quoted = if relation_ref_tables_camel_case.is_empty() {
|
let quoted = if relation_ref_tables_camel_case.is_empty() {
|
||||||
quote! {
|
quote! {
|
||||||
_ => panic!("No RelationDef"),
|
_ => panic!("No RelationDef"),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
#(Self::#relation_ref_tables_camel_case => Entity::#relation_rel_types(super::#relation_ref_tables_snake_case::Entity)
|
#(Self::#relation_ref_tables_camel_case => #relation_defs),*
|
||||||
.from(Column::#relation_columns_camel_case)
|
|
||||||
.to(super::#relation_ref_tables_snake_case::Column::#relation_ref_columns_camel_case)
|
|
||||||
.into()),*
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{ExecErr, ExecResult, QueryErr, QueryResult, Statement};
|
use crate::{ExecErr, ExecResult, QueryErr, QueryResult, Statement, Transaction};
|
||||||
use sea_query::{
|
use sea_query::{
|
||||||
MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SqliteQueryBuilder,
|
MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SqliteQueryBuilder,
|
||||||
};
|
};
|
||||||
@ -118,6 +118,12 @@ impl DatabaseConnection {
|
|||||||
pub fn as_mock_connection(&self) -> Option<bool> {
|
pub fn as_mock_connection(&self) -> Option<bool> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
pub fn into_transaction_log(self) -> Vec<Transaction> {
|
||||||
|
let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap();
|
||||||
|
mocker.drain_transaction_log()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QueryBuilderBackend {
|
impl QueryBuilderBackend {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::Statement;
|
use crate::Statement;
|
||||||
|
use sea_query::{Value, Values};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
@ -6,6 +7,16 @@ pub struct Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
|
pub fn from_sql_and_values<I>(sql: &str, values: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Value>,
|
||||||
|
{
|
||||||
|
Self::one(Statement {
|
||||||
|
sql: sql.to_owned(),
|
||||||
|
values: Some(Values(values.into_iter().collect())),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a Transaction with one statement
|
/// Create a Transaction with one statement
|
||||||
pub fn one(stmt: Statement) -> Self {
|
pub fn one(stmt: Statement) -> Self {
|
||||||
Self { stmts: vec![stmt] }
|
Self { stmts: vec![stmt] }
|
||||||
|
@ -42,17 +42,6 @@ impl Default for ActiveValueState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait OneOrManyActiveModel<A>
|
|
||||||
where
|
|
||||||
A: ActiveModelTrait,
|
|
||||||
{
|
|
||||||
fn is_one() -> bool;
|
|
||||||
fn get_one(self) -> A;
|
|
||||||
|
|
||||||
fn is_many() -> bool;
|
|
||||||
fn get_many(self) -> Vec<A>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn unchanged_active_value_not_intended_for_public_use<V>(value: V) -> ActiveValue<V>
|
pub fn unchanged_active_value_not_intended_for_public_use<V>(value: V) -> ActiveValue<V>
|
||||||
where
|
where
|
||||||
@ -197,44 +186,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A> OneOrManyActiveModel<A> for A
|
|
||||||
where
|
|
||||||
A: ActiveModelTrait,
|
|
||||||
{
|
|
||||||
fn is_one() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
fn get_one(self) -> A {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_many() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
fn get_many(self) -> Vec<A> {
|
|
||||||
panic!("not many")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A> OneOrManyActiveModel<A> for Vec<A>
|
|
||||||
where
|
|
||||||
A: ActiveModelTrait,
|
|
||||||
{
|
|
||||||
fn is_one() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
fn get_one(self) -> A {
|
|
||||||
panic!("not one")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_many() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
fn get_many(self) -> Vec<A> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert the model if primary key is unset, update otherwise.
|
/// Insert the model if primary key is unset, update otherwise.
|
||||||
/// Only works if the entity has auto increment primary key.
|
/// Only works if the entity has auto increment primary key.
|
||||||
pub async fn save_active_model<A, E>(mut am: A, db: &DatabaseConnection) -> Result<A, ExecErr>
|
pub async fn save_active_model<A, E>(mut am: A, db: &DatabaseConnection) -> Result<A, ExecErr>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ActiveModelTrait, ColumnTrait, Delete, DeleteOne, FromQueryResult, Insert, ModelTrait,
|
ActiveModelTrait, ColumnTrait, Delete, DeleteMany, DeleteOne, FromQueryResult, Insert,
|
||||||
OneOrManyActiveModel, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, Related,
|
ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, Related, RelationBuilder,
|
||||||
RelationBuilder, RelationTrait, RelationType, Select, Update, UpdateOne,
|
RelationTrait, RelationType, Select, Update, UpdateMany, UpdateOne,
|
||||||
};
|
};
|
||||||
use sea_query::{Iden, IntoValueTuple};
|
use sea_query::{Iden, IntoValueTuple};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@ -50,14 +50,27 @@ pub trait EntityTrait: EntityName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||||
|
///
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// cake::Entity::find().one(&db).await;
|
||||||
|
/// cake::Entity::find().all(&db).await;
|
||||||
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// db.into_transaction_log(),
|
||||||
/// .build(PostgresQueryBuilder)
|
/// vec![
|
||||||
/// .to_string(),
|
/// Transaction::from_sql_and_values(
|
||||||
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#, vec![1u64.into()]
|
||||||
/// );
|
/// ),
|
||||||
|
/// Transaction::from_sql_and_values(
|
||||||
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![]
|
||||||
|
/// ),
|
||||||
|
/// ]);
|
||||||
/// ```
|
/// ```
|
||||||
fn find() -> Select<Self> {
|
fn find() -> Select<Self> {
|
||||||
Select::new()
|
Select::new()
|
||||||
@ -65,28 +78,42 @@ pub trait EntityTrait: EntityName {
|
|||||||
|
|
||||||
/// Find a model by primary key
|
/// Find a model by primary key
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||||
|
///
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// cake::Entity::find_by_id(11).all(&db).await;
|
||||||
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find_by_id(11)
|
/// db.into_transaction_log(),
|
||||||
/// .build(PostgresQueryBuilder)
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// .to_string(),
|
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#, vec![11i32.into()]
|
||||||
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = 11"#
|
/// )]);
|
||||||
/// );
|
|
||||||
/// ```
|
/// ```
|
||||||
/// Find by composite key
|
/// Find by composite key
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling, sea_query::PostgresQueryBuilder};
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
|
||||||
|
///
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// cake_filling::Entity::find_by_id((2, 3)).all(&db).await;
|
||||||
|
/// # });
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake_filling::Entity::find_by_id((2, 3))
|
/// db.into_transaction_log(),
|
||||||
/// .build(PostgresQueryBuilder)
|
/// vec![Transaction::from_sql_and_values([
|
||||||
/// .to_string(),
|
|
||||||
/// [
|
|
||||||
/// r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
|
/// r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
|
||||||
/// r#"WHERE "cake_filling"."cake_id" = 2 AND "cake_filling"."filling_id" = 3"#,
|
/// r#"WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
|
||||||
/// ].join(" ")
|
/// ].join(" ").as_str(),
|
||||||
/// );
|
/// vec![2i32.into(), 3i32.into()]
|
||||||
|
/// )]);
|
||||||
/// ```
|
/// ```
|
||||||
fn find_by_id<V>(values: V) -> Select<Self>
|
fn find_by_id<V>(values: V) -> Select<Self>
|
||||||
where
|
where
|
||||||
@ -108,69 +135,29 @@ pub trait EntityTrait: EntityName {
|
|||||||
select
|
select
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert one
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||||
///
|
///
|
||||||
/// let apple = cake::ActiveModel {
|
/// let apple = cake::ActiveModel {
|
||||||
/// name: Set("Apple Pie".to_owned()),
|
/// name: Set("Apple Pie".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
/// assert_eq!(
|
|
||||||
/// cake::Entity::insert(apple)
|
|
||||||
/// .build(PostgresQueryBuilder)
|
|
||||||
/// .to_string(),
|
|
||||||
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
/// Insert many
|
|
||||||
/// ```
|
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
|
||||||
///
|
///
|
||||||
/// let apple = cake::ActiveModel {
|
/// # async_std::task::block_on(async {
|
||||||
/// name: Set("Apple Pie".to_owned()),
|
/// cake::Entity::insert(apple).exec(&db).await;
|
||||||
/// ..Default::default()
|
/// # });
|
||||||
/// };
|
|
||||||
/// let orange = cake::ActiveModel {
|
|
||||||
/// name: Set("Orange Scone".to_owned()),
|
|
||||||
/// ..Default::default()
|
|
||||||
/// };
|
|
||||||
/// assert_eq!(
|
|
||||||
/// cake::Entity::insert(vec![apple, orange])
|
|
||||||
/// .build(PostgresQueryBuilder)
|
|
||||||
/// .to_string(),
|
|
||||||
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
fn insert<A, C>(models: C) -> Insert<A>
|
|
||||||
where
|
|
||||||
A: ActiveModelTrait<Entity = Self>,
|
|
||||||
C: OneOrManyActiveModel<A>,
|
|
||||||
{
|
|
||||||
if C::is_one() {
|
|
||||||
Self::insert_one(models.get_one())
|
|
||||||
} else if C::is_many() {
|
|
||||||
Self::insert_many(models.get_many())
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ```
|
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
|
||||||
///
|
///
|
||||||
/// let apple = cake::ActiveModel {
|
|
||||||
/// name: Set("Apple Pie".to_owned()),
|
|
||||||
/// ..Default::default()
|
|
||||||
/// };
|
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::insert_one(apple)
|
/// db.into_transaction_log(),
|
||||||
/// .build(PostgresQueryBuilder)
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// .to_string(),
|
/// r#"INSERT INTO "cake" ("name") VALUES ($1)"#, vec!["Apple Pie".into()]
|
||||||
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
|
/// )]);
|
||||||
/// );
|
|
||||||
/// ```
|
/// ```
|
||||||
fn insert_one<A>(model: A) -> Insert<A>
|
fn insert<A>(model: A) -> Insert<A>
|
||||||
where
|
where
|
||||||
A: ActiveModelTrait<Entity = Self>,
|
A: ActiveModelTrait<Entity = Self>,
|
||||||
{
|
{
|
||||||
@ -178,7 +165,11 @@ pub trait EntityTrait: EntityName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
|
||||||
///
|
///
|
||||||
/// let apple = cake::ActiveModel {
|
/// let apple = cake::ActiveModel {
|
||||||
/// name: Set("Apple Pie".to_owned()),
|
/// name: Set("Apple Pie".to_owned()),
|
||||||
@ -188,12 +179,17 @@ pub trait EntityTrait: EntityName {
|
|||||||
/// name: Set("Orange Scone".to_owned()),
|
/// name: Set("Orange Scone".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
|
///
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// cake::Entity::insert_many(vec![apple, orange]).exec(&db).await;
|
||||||
|
/// # });
|
||||||
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::insert_many(vec![apple, orange])
|
/// db.into_transaction_log(),
|
||||||
/// .build(PostgresQueryBuilder)
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// .to_string(),
|
/// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2)"#,
|
||||||
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
|
/// vec!["Apple Pie".into(), "Orange Scone".into()]
|
||||||
/// );
|
/// )]);
|
||||||
/// ```
|
/// ```
|
||||||
fn insert_many<A, I>(models: I) -> Insert<A>
|
fn insert_many<A, I>(models: I) -> Insert<A>
|
||||||
where
|
where
|
||||||
@ -204,19 +200,27 @@ pub trait EntityTrait: EntityName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::PostgresQueryBuilder};
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||||
///
|
///
|
||||||
/// let orange = fruit::ActiveModel {
|
/// let orange = fruit::ActiveModel {
|
||||||
/// id: Set(1),
|
/// id: Set(1),
|
||||||
/// name: Set("Orange".to_owned()),
|
/// name: Set("Orange".to_owned()),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
|
///
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// fruit::Entity::update(orange).exec(&db).await;
|
||||||
|
/// # });
|
||||||
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// fruit::Entity::update(orange)
|
/// db.into_transaction_log(),
|
||||||
/// .build(PostgresQueryBuilder)
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// .to_string(),
|
/// r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2"#, vec!["Orange".into(), 1i32.into()]
|
||||||
/// r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"#,
|
/// )]);
|
||||||
/// );
|
|
||||||
/// ```
|
/// ```
|
||||||
fn update<A>(model: A) -> UpdateOne<A>
|
fn update<A>(model: A) -> UpdateOne<A>
|
||||||
where
|
where
|
||||||
@ -226,18 +230,51 @@ pub trait EntityTrait: EntityName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::PostgresQueryBuilder};
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::{Expr, Value}};
|
||||||
|
///
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// fruit::Entity::update_many()
|
||||||
|
/// .col_expr(fruit::Column::CakeId, Expr::value(Value::Null))
|
||||||
|
/// .filter(fruit::Column::Name.contains("Apple"))
|
||||||
|
/// .exec(&db)
|
||||||
|
/// .await;
|
||||||
|
/// # });
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// db.into_transaction_log(),
|
||||||
|
/// vec![Transaction::from_sql_and_values(
|
||||||
|
/// r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#, vec![Value::Null, "%Apple%".into()]
|
||||||
|
/// )]);
|
||||||
|
/// ```
|
||||||
|
fn update_many() -> UpdateMany<Self> {
|
||||||
|
Update::many(Self::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||||
///
|
///
|
||||||
/// let orange = fruit::ActiveModel {
|
/// let orange = fruit::ActiveModel {
|
||||||
/// id: Set(3),
|
/// id: Set(3),
|
||||||
/// ..Default::default()
|
/// ..Default::default()
|
||||||
/// };
|
/// };
|
||||||
|
///
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// fruit::Entity::delete(orange).exec(&db).await;
|
||||||
|
/// # });
|
||||||
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// fruit::Entity::delete(orange)
|
/// db.into_transaction_log(),
|
||||||
/// .build(PostgresQueryBuilder)
|
/// vec![Transaction::from_sql_and_values(
|
||||||
/// .to_string(),
|
/// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#, vec![3i32.into()]
|
||||||
/// r#"DELETE FROM "fruit" WHERE "fruit"."id" = 3"#,
|
/// )]);
|
||||||
/// );
|
|
||||||
/// ```
|
/// ```
|
||||||
fn delete<A>(model: A) -> DeleteOne<A>
|
fn delete<A>(model: A) -> DeleteOne<A>
|
||||||
where
|
where
|
||||||
@ -245,4 +282,28 @@ pub trait EntityTrait: EntityName {
|
|||||||
{
|
{
|
||||||
Delete::one(model)
|
Delete::one(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "mock")]
|
||||||
|
/// # use sea_orm::{MockDatabase, Transaction};
|
||||||
|
/// # let db = MockDatabase::new().into_connection();
|
||||||
|
/// #
|
||||||
|
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
|
||||||
|
///
|
||||||
|
/// # async_std::task::block_on(async {
|
||||||
|
/// fruit::Entity::delete_many()
|
||||||
|
/// .filter(fruit::Column::Name.contains("Apple"))
|
||||||
|
/// .exec(&db)
|
||||||
|
/// .await;
|
||||||
|
/// # });
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// db.into_transaction_log(),
|
||||||
|
/// vec![Transaction::from_sql_and_values(
|
||||||
|
/// r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#, vec!["%Apple%".into()]
|
||||||
|
/// )]);
|
||||||
|
/// ```
|
||||||
|
fn delete_many() -> DeleteMany<Self> {
|
||||||
|
Delete::many(Self::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{EntityTrait, QueryResult, TypeErr};
|
use crate::{EntityTrait, QueryFilter, QueryResult, Related, Select, TypeErr};
|
||||||
pub use sea_query::Value;
|
pub use sea_query::Value;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
@ -8,10 +8,25 @@ pub trait ModelTrait: Clone + Debug {
|
|||||||
fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> Value;
|
fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> Value;
|
||||||
|
|
||||||
fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
|
fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
|
||||||
|
|
||||||
|
fn find_related<R>(&self, _: R) -> Select<R>
|
||||||
|
where
|
||||||
|
R: EntityTrait,
|
||||||
|
Self::Entity: Related<R>,
|
||||||
|
{
|
||||||
|
<Self::Entity as Related<R>>::find_related().belongs_to(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromQueryResult {
|
pub trait FromQueryResult {
|
||||||
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, TypeErr>
|
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, TypeErr>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
|
fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, TypeErr>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Ok(Self::from_query_result(res, pre).ok())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,9 +175,7 @@ mod tests {
|
|||||||
query_builder.build(select.offset(4).limit(2)),
|
query_builder.build(select.offset(4).limit(2)),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap();
|
assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
|
||||||
|
|
||||||
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,9 +209,7 @@ mod tests {
|
|||||||
query_builder.build(select.offset(4).limit(2)),
|
query_builder.build(select.offset(4).limit(2)),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap();
|
assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
|
||||||
|
|
||||||
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,9 +240,8 @@ mod tests {
|
|||||||
|
|
||||||
let query_builder = db.get_query_builder_backend();
|
let query_builder = db.get_query_builder_backend();
|
||||||
let stmts = vec![query_builder.build(&select)];
|
let stmts = vec![query_builder.build(&select)];
|
||||||
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
|
assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,9 +292,7 @@ mod tests {
|
|||||||
query_builder.build(select.offset(4).limit(2)),
|
query_builder.build(select.offset(4).limit(2)),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap();
|
assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
|
||||||
|
|
||||||
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,9 +324,7 @@ mod tests {
|
|||||||
query_builder.build(select.offset(4).limit(2)),
|
query_builder.build(select.offset(4).limit(2)),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap();
|
assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
|
||||||
|
|
||||||
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
query::combine, DatabaseConnection, EntityTrait, FromQueryResult, JsonValue, Paginator,
|
query::combine, DatabaseConnection, EntityTrait, FromQueryResult, Iterable, JsonValue,
|
||||||
QueryErr, QueryResult, Select, SelectTwo, TypeErr,
|
ModelTrait, Paginator, PrimaryKeyToColumn, QueryErr, QueryResult, Select, SelectTwo,
|
||||||
|
SelectTwoMany, TypeErr,
|
||||||
};
|
};
|
||||||
use sea_query::SelectStatement;
|
use sea_query::SelectStatement;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@ -52,12 +53,12 @@ where
|
|||||||
M: FromQueryResult + Sized,
|
M: FromQueryResult + Sized,
|
||||||
N: FromQueryResult + Sized,
|
N: FromQueryResult + Sized,
|
||||||
{
|
{
|
||||||
type Item = (M, N);
|
type Item = (M, Option<N>);
|
||||||
|
|
||||||
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, TypeErr> {
|
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, TypeErr> {
|
||||||
Ok((
|
Ok((
|
||||||
M::from_query_result(&res, combine::SELECT_A)?,
|
M::from_query_result(&res, combine::SELECT_A)?,
|
||||||
N::from_query_result(&res, combine::SELECT_B)?,
|
N::from_query_result_optional(&res, combine::SELECT_B)?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,15 +129,58 @@ where
|
|||||||
pub async fn one(
|
pub async fn one(
|
||||||
self,
|
self,
|
||||||
db: &DatabaseConnection,
|
db: &DatabaseConnection,
|
||||||
) -> Result<Option<(E::Model, F::Model)>, QueryErr> {
|
) -> Result<Option<(E::Model, Option<F::Model>)>, QueryErr> {
|
||||||
self.into_model::<E::Model, F::Model>().one(db).await
|
self.into_model::<E::Model, F::Model>().one(db).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn all(self, db: &DatabaseConnection) -> Result<Vec<(E::Model, F::Model)>, QueryErr> {
|
pub async fn all(
|
||||||
|
self,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
) -> Result<Vec<(E::Model, Option<F::Model>)>, QueryErr> {
|
||||||
self.into_model::<E::Model, F::Model>().all(db).await
|
self.into_model::<E::Model, F::Model>().all(db).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E, F> SelectTwoMany<E, F>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
F: EntityTrait,
|
||||||
|
{
|
||||||
|
fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
|
||||||
|
where
|
||||||
|
M: FromQueryResult,
|
||||||
|
N: FromQueryResult,
|
||||||
|
{
|
||||||
|
Selector {
|
||||||
|
query: self.query,
|
||||||
|
selector: SelectTwoModel { model: PhantomData },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "with-json")]
|
||||||
|
pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> {
|
||||||
|
Selector {
|
||||||
|
query: self.query,
|
||||||
|
selector: SelectTwoModel { model: PhantomData },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn one(
|
||||||
|
self,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
) -> Result<Option<(E::Model, Option<F::Model>)>, QueryErr> {
|
||||||
|
self.into_model::<E::Model, F::Model>().one(db).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn all(
|
||||||
|
self,
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
) -> Result<Vec<(E::Model, Vec<F::Model>)>, QueryErr> {
|
||||||
|
let rows = self.into_model::<E::Model, F::Model>().all(db).await?;
|
||||||
|
Ok(consolidate_query_result::<E, F>(rows))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S> Selector<S>
|
impl<S> Selector<S>
|
||||||
where
|
where
|
||||||
S: SelectorTrait,
|
S: SelectorTrait,
|
||||||
@ -171,3 +215,39 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn consolidate_query_result<L, R>(
|
||||||
|
rows: Vec<(L::Model, Option<R::Model>)>,
|
||||||
|
) -> Vec<(L::Model, Vec<R::Model>)>
|
||||||
|
where
|
||||||
|
L: EntityTrait,
|
||||||
|
R: EntityTrait,
|
||||||
|
{
|
||||||
|
let mut acc: Vec<(L::Model, Vec<R::Model>)> = Vec::new();
|
||||||
|
for (l, r) in rows {
|
||||||
|
if let Some((last_l, last_r)) = acc.last_mut() {
|
||||||
|
let mut same_l = true;
|
||||||
|
for pk_col in <L::PrimaryKey as Iterable>::iter() {
|
||||||
|
let col = pk_col.into_column();
|
||||||
|
let val = l.get(col);
|
||||||
|
let last_val = last_l.get(col);
|
||||||
|
if !val.eq(&last_val) {
|
||||||
|
same_l = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if same_l {
|
||||||
|
if let Some(r) = r {
|
||||||
|
last_r.push(r);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.is_some() {
|
||||||
|
acc.push((l, vec![r.unwrap()]));
|
||||||
|
} else {
|
||||||
|
acc.push((l, vec![]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
126
src/lib.rs
126
src/lib.rs
@ -1,8 +1,134 @@
|
|||||||
|
//! # Select
|
||||||
|
//! ```
|
||||||
|
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*};
|
||||||
|
//! # async fn function(db: &DbConn) -> Result<(), QueryErr> {
|
||||||
|
//! #
|
||||||
|
//! // 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?;
|
||||||
|
//!
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//! # Insert
|
||||||
|
//! ```
|
||||||
|
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*};
|
||||||
|
//! # async fn function(db: &DbConn) -> Result<(), ExecErr> {
|
||||||
|
//! #
|
||||||
|
//! 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 res: InsertResult = Fruit::insert(pear).exec(db).await?;
|
||||||
|
//!
|
||||||
|
//! println!("InsertResult: {}", res.last_insert_id);
|
||||||
|
//! #
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! #
|
||||||
|
//! # async fn function2(db: &DbConn) -> Result<(), ExecErr> {
|
||||||
|
//! # 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 many
|
||||||
|
//! Fruit::insert_many(vec![apple, pear]).exec(db).await?;
|
||||||
|
//! #
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//! # Update
|
||||||
|
//! ```
|
||||||
|
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*};
|
||||||
|
//! #
|
||||||
|
//! use sea_orm::sea_query::{Expr, Value};
|
||||||
|
//!
|
||||||
|
//! # async fn function(db: &DbConn) -> Result<(), QueryErr> {
|
||||||
|
//! let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! #
|
||||||
|
//! # async fn function2(db: &DbConn) -> Result<(), ExecErr> {
|
||||||
|
//! # let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await.unwrap();
|
||||||
|
//!
|
||||||
|
//! let mut pear: fruit::ActiveModel = pear.unwrap().into();
|
||||||
|
//! pear.name = Set("Sweet pear".to_owned());
|
||||||
|
//!
|
||||||
|
//! // update one
|
||||||
|
//! let pear: fruit::ActiveModel = Fruit::update(pear).exec(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::Null))
|
||||||
|
//! .filter(fruit::Column::Name.contains("Apple"))
|
||||||
|
//! .exec(db)
|
||||||
|
//! .await?;
|
||||||
|
//!
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//! # Delete
|
||||||
|
//! ```
|
||||||
|
//! # use sea_orm::{DbConn, entity::*, query::*, tests_cfg::*};
|
||||||
|
//! #
|
||||||
|
//! # async fn function(db: &DbConn) -> Result<(), QueryErr> {
|
||||||
|
//! let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! #
|
||||||
|
//! # async fn function2(db: &DbConn) -> Result<(), ExecErr> {
|
||||||
|
//! # let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await.unwrap();
|
||||||
|
//! let orange: fruit::ActiveModel = orange.unwrap().into();
|
||||||
|
//!
|
||||||
|
//! // delete one
|
||||||
|
//! fruit::Entity::delete(orange).exec(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?;
|
||||||
|
//!
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
mod database;
|
mod database;
|
||||||
mod driver;
|
mod driver;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
mod executor;
|
mod executor;
|
||||||
pub mod query;
|
pub mod query;
|
||||||
|
#[doc(hidden)]
|
||||||
pub mod tests_cfg;
|
pub mod tests_cfg;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{EntityTrait, IntoSimpleExpr, Iterable, QueryTrait, Select, SelectTwo};
|
use crate::{EntityTrait, IntoSimpleExpr, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
pub use sea_query::JoinType;
|
pub use sea_query::JoinType;
|
||||||
use sea_query::{Alias, ColumnRef, Iden, SeaRc, SelectExpr, SelectStatement, SimpleExpr};
|
use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr};
|
||||||
|
|
||||||
pub const SELECT_A: &str = "A_";
|
pub const SELECT_A: &str = "A_";
|
||||||
pub const SELECT_B: &str = "B_";
|
pub const SELECT_B: &str = "B_";
|
||||||
@ -40,6 +40,14 @@ where
|
|||||||
self = self.apply_alias(SELECT_A);
|
self = self.apply_alias(SELECT_A);
|
||||||
SelectTwo::new(self.into_query())
|
SelectTwo::new(self.into_query())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select_with<F>(mut self, _: F) -> SelectTwoMany<E, F>
|
||||||
|
where
|
||||||
|
F: EntityTrait,
|
||||||
|
{
|
||||||
|
self = self.apply_alias(SELECT_A);
|
||||||
|
SelectTwoMany::new(self.into_query())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, F> SelectTwo<E, F>
|
impl<E, F> SelectTwo<E, F>
|
||||||
@ -48,23 +56,58 @@ where
|
|||||||
F: EntityTrait,
|
F: EntityTrait,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(query: SelectStatement) -> Self {
|
pub(crate) fn new(query: SelectStatement) -> Self {
|
||||||
let myself = Self {
|
Self {
|
||||||
query,
|
query,
|
||||||
entity: PhantomData,
|
entity: PhantomData,
|
||||||
};
|
}
|
||||||
myself.prepare_select()
|
.prepare_select()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_select(mut self) -> Self {
|
fn prepare_select(mut self) -> Self {
|
||||||
|
prepare_select_two::<F, Self>(&mut self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, F> SelectTwoMany<E, F>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
F: EntityTrait,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(query: SelectStatement) -> Self {
|
||||||
|
Self {
|
||||||
|
query,
|
||||||
|
entity: PhantomData,
|
||||||
|
}
|
||||||
|
.prepare_select()
|
||||||
|
.prepare_order_by()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_select(mut self) -> Self {
|
||||||
|
prepare_select_two::<F, Self>(&mut self);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_order_by(mut self) -> Self {
|
||||||
|
for col in <E::PrimaryKey as Iterable>::iter() {
|
||||||
|
self.query.order_by((E::default(), col), Order::Asc);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_select_two<F, S>(selector: &mut S)
|
||||||
|
where
|
||||||
|
F: EntityTrait,
|
||||||
|
S: QueryTrait<QueryStatement = SelectStatement>,
|
||||||
|
{
|
||||||
for col in <F::Column as Iterable>::iter() {
|
for col in <F::Column as Iterable>::iter() {
|
||||||
let alias = format!("{}{}", SELECT_B, col.to_string().as_str());
|
let alias = format!("{}{}", SELECT_B, col.to_string().as_str());
|
||||||
self.query.expr(SelectExpr {
|
selector.query().expr(SelectExpr {
|
||||||
expr: col.into_simple_expr(),
|
expr: col.into_simple_expr(),
|
||||||
alias: Some(SeaRc::new(Alias::new(&alias))),
|
alias: Some(SeaRc::new(Alias::new(&alias))),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -101,6 +144,23 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select_with_1() {
|
||||||
|
assert_eq!(
|
||||||
|
cake::Entity::find()
|
||||||
|
.left_join(fruit::Entity)
|
||||||
|
.select_with(fruit::Entity)
|
||||||
|
.build(MysqlQueryBuilder)
|
||||||
|
.to_string(),
|
||||||
|
[
|
||||||
|
"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
|
||||||
|
"`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
|
||||||
|
"FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||||
|
"ORDER BY `cake`.`id` ASC",
|
||||||
|
].join(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn select_also_2() {
|
fn select_also_2() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -119,4 +179,24 @@ mod tests {
|
|||||||
].join(" ")
|
].join(" ")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn select_with_2() {
|
||||||
|
assert_eq!(
|
||||||
|
cake::Entity::find()
|
||||||
|
.left_join(fruit::Entity)
|
||||||
|
.select_with(fruit::Entity)
|
||||||
|
.filter(cake::Column::Id.eq(1))
|
||||||
|
.filter(fruit::Column::Id.eq(2))
|
||||||
|
.build(MysqlQueryBuilder)
|
||||||
|
.to_string(),
|
||||||
|
[
|
||||||
|
"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
|
||||||
|
"`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
|
||||||
|
"FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||||
|
"WHERE `cake`.`id` = 1 AND `fruit`.`id` = 2",
|
||||||
|
"ORDER BY `cake`.`id` ASC",
|
||||||
|
].join(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,11 +258,11 @@ pub trait QueryFilter: Sized {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a where condition using the model's primary key
|
/// Apply a where condition using the model's primary key
|
||||||
fn belongs_to<E>(mut self, model: &E::Model) -> Self
|
fn belongs_to<M>(mut self, model: &M) -> Self
|
||||||
where
|
where
|
||||||
E: EntityTrait,
|
M: ModelTrait,
|
||||||
{
|
{
|
||||||
for key in E::PrimaryKey::iter() {
|
for key in <M::Entity as EntityTrait>::PrimaryKey::iter() {
|
||||||
let col = key.into_column();
|
let col = key.into_column();
|
||||||
self = self.filter(col.eq(model.get(col)));
|
self = self.filter(col.eq(model.get(col)));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{EntityTrait, QuerySelect, Related, Select, SelectTwo};
|
use crate::{EntityTrait, QuerySelect, Related, Select, SelectTwo, SelectTwoMany};
|
||||||
pub use sea_query::JoinType;
|
pub use sea_query::JoinType;
|
||||||
|
|
||||||
impl<E> Select<E>
|
impl<E> Select<E>
|
||||||
@ -41,19 +41,28 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Left Join with a Related Entity and select both Entity.
|
/// Left Join with a Related Entity and select both Entity.
|
||||||
pub fn left_join_and_select<R>(self, r: R) -> SelectTwo<E, R>
|
pub fn find_also_related<R>(self, r: R) -> SelectTwo<E, R>
|
||||||
where
|
where
|
||||||
R: EntityTrait,
|
R: EntityTrait,
|
||||||
E: Related<R>,
|
E: Related<R>,
|
||||||
{
|
{
|
||||||
self.left_join(r).select_also(r)
|
self.left_join(r).select_also(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Left Join with a Related Entity and select the related Entity as a `Vec`
|
||||||
|
pub fn find_with_related<R>(self, r: R) -> SelectTwoMany<E, R>
|
||||||
|
where
|
||||||
|
R: EntityTrait,
|
||||||
|
E: Related<R>,
|
||||||
|
{
|
||||||
|
self.left_join(r).select_with(r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests_cfg::{cake, filling, fruit};
|
use crate::tests_cfg::{cake, filling, fruit};
|
||||||
use crate::{ColumnTrait, EntityTrait, QueryFilter, QueryTrait};
|
use crate::{ColumnTrait, EntityTrait, ModelTrait, QueryFilter, QueryTrait};
|
||||||
use sea_query::MysqlQueryBuilder;
|
use sea_query::MysqlQueryBuilder;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -130,7 +139,10 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cake_model.find_fruit().build(MysqlQueryBuilder).to_string(),
|
cake_model
|
||||||
|
.find_related(fruit::Entity)
|
||||||
|
.build(MysqlQueryBuilder)
|
||||||
|
.to_string(),
|
||||||
[
|
[
|
||||||
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
||||||
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
|
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||||
|
@ -20,4 +20,4 @@ pub use select::*;
|
|||||||
pub use traits::*;
|
pub use traits::*;
|
||||||
pub use update::*;
|
pub use update::*;
|
||||||
|
|
||||||
pub use crate::executor::{ExecErr, QueryErr};
|
pub use crate::executor::{ExecErr, InsertResult, QueryErr, UpdateResult};
|
||||||
|
@ -23,6 +23,16 @@ where
|
|||||||
pub(crate) entity: PhantomData<(E, F)>,
|
pub(crate) entity: PhantomData<(E, F)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SelectTwoMany<E, F>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
F: EntityTrait,
|
||||||
|
{
|
||||||
|
pub(crate) query: SelectStatement,
|
||||||
|
pub(crate) entity: PhantomData<(E, F)>,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait IntoSimpleExpr {
|
pub trait IntoSimpleExpr {
|
||||||
fn into_simple_expr(self) -> SimpleExpr;
|
fn into_simple_expr(self) -> SimpleExpr;
|
||||||
}
|
}
|
||||||
@ -51,6 +61,18 @@ macro_rules! impl_trait {
|
|||||||
&mut self.query
|
&mut self.query
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E, F> $trait for SelectTwoMany<E, F>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
F: EntityTrait,
|
||||||
|
{
|
||||||
|
type QueryStatement = SelectStatement;
|
||||||
|
|
||||||
|
fn query(&mut self) -> &mut SelectStatement {
|
||||||
|
&mut self.query
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,11 +140,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, F> QueryTrait for SelectTwo<E, F>
|
macro_rules! select_two {
|
||||||
where
|
( $selector: ident ) => {
|
||||||
|
impl<E, F> QueryTrait for $selector<E, F>
|
||||||
|
where
|
||||||
E: EntityTrait,
|
E: EntityTrait,
|
||||||
F: EntityTrait,
|
F: EntityTrait,
|
||||||
{
|
{
|
||||||
type QueryStatement = SelectStatement;
|
type QueryStatement = SelectStatement;
|
||||||
fn query(&mut self) -> &mut SelectStatement {
|
fn query(&mut self) -> &mut SelectStatement {
|
||||||
&mut self.query
|
&mut self.query
|
||||||
@ -133,4 +157,9 @@ where
|
|||||||
fn into_query(self) -> SelectStatement {
|
fn into_query(self) -> SelectStatement {
|
||||||
self.query
|
self.query
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select_two!(SelectTwo);
|
||||||
|
select_two!(SelectTwoMany);
|
||||||
|
@ -73,14 +73,4 @@ impl Related<super::filling::Entity> for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
|
||||||
pub fn find_fruit(&self) -> Select<super::fruit::Entity> {
|
|
||||||
Entity::find_related().belongs_to::<Entity>(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_filling(&self) -> Select<super::filling::Entity> {
|
|
||||||
Entity::find_related().belongs_to::<Entity>(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
@ -63,10 +63,4 @@ impl Related<super::cake::Entity> for Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
|
||||||
pub fn find_cake(&self) -> Select<super::cake::Entity> {
|
|
||||||
Entity::find_related().belongs_to::<Entity>(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
@ -4,3 +4,8 @@ pub mod cake;
|
|||||||
pub mod cake_filling;
|
pub mod cake_filling;
|
||||||
pub mod filling;
|
pub mod filling;
|
||||||
pub mod fruit;
|
pub mod fruit;
|
||||||
|
|
||||||
|
pub use cake::Entity as Cake;
|
||||||
|
pub use cake_filling::Entity as CakeFilling;
|
||||||
|
pub use filling::Entity as Filling;
|
||||||
|
pub use fruit::Entity as Fruit;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user