-
-
Notifications
You must be signed in to change notification settings - Fork 521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Entity Generator #11
Comments
What is the implementation strategy we have in mind? |
Collect schema info using sea-schema. Sample table info collected by it. TableDef {
info: TableInfo {
name: "cake_filling",
engine: InnoDb,
auto_increment: None,
char_set: Utf8Mb4,
collation: Utf8Mb4GeneralCi,
comment: "",
},
columns: [
ColumnInfo {
name: "cake_id",
col_type: Int(
NumericAttr {
maximum: Some(
11,
),
decimal: None,
unsigned: None,
zero_fill: None,
},
),
null: false,
key: Primary,
default: None,
extra: ColumnExtra {
auto_increment: false,
on_update_current_timestamp: false,
generated: false,
default_generated: false,
},
expression: None,
comment: "",
},
ColumnInfo {
name: "filling_id",
col_type: Int(
NumericAttr {
maximum: Some(
11,
),
decimal: None,
unsigned: None,
zero_fill: None,
},
),
null: false,
key: Primary,
default: None,
extra: ColumnExtra {
auto_increment: false,
on_update_current_timestamp: false,
generated: false,
default_generated: false,
},
expression: None,
comment: "",
},
],
indexes: [
IndexInfo {
unique: false,
name: "filling_id",
parts: [
IndexPart {
column: "filling_id",
order: Ascending,
sub_part: None,
},
],
nullable: false,
idx_type: BTree,
comment: "",
functional: false,
},
IndexInfo {
unique: true,
name: "PRIMARY",
parts: [
IndexPart {
column: "cake_id",
order: Ascending,
sub_part: None,
},
IndexPart {
column: "filling_id",
order: Ascending,
sub_part: None,
},
],
nullable: false,
idx_type: BTree,
comment: "",
functional: false,
},
],
foreign_keys: [
ForeignKeyInfo {
name: "cake_filling_ibfk_1",
columns: [
"cake_id",
],
referenced_table: "cake",
referenced_columns: [
"id",
],
on_update: Restrict,
on_delete: Restrict,
},
ForeignKeyInfo {
name: "cake_filling_ibfk_2",
columns: [
"filling_id",
],
referenced_table: "filling",
referenced_columns: [
"id",
],
on_update: Restrict,
on_delete: Restrict,
},
],
}, Mapping above table info into specification for orm entity, column & relation...
Data structure for generating entity file. // Can actually be implemented as two runtime variable `entities` and `inverse_relations`
pub struct EntityGenerator {
entities: Vec<EntitySpec>,
inverse_relations: HashMap<String, Vec<RelationSpec>>, // Store many-to-one relation in first pass
}
pub struct EntitySpec {
table_name: String,
columns: Vec<ColumnSpec>,
relations: Vec<RelationSpec>,
}
pub struct ColumnSpec {
name: String,
rs_type: String, // Rust type provided by sea-schema (this type info is currently not supported)
col_type: ColumnType, // Infer from `rs_type`
is_primary_key: bool,
}
pub struct RelationSpec {
name: String,
ref_table: String,
columns: Vec<String>,
ref_columns: Vec<String>,
rel_type: RelationType, // Relation parse directly from sea-schema is one-to-one, otherwise is many-to-one since it's from `inverse_relations`
} Having above info should be enough to generate the entity file. Code generation...
|
See latest commit for the POC |
Several comments.
|
|
Still need plenty of refactoring but you can take a glance at the |
Oh so this is like a procedural macro but actually write code to a file? |
Correct! |
Will refactor the code now, too messy loll |
Makes sense. But we'd better separate the discover -> transform -> generate stages. |
Without keeping any internal state? Sth like this?
|
It's easier to unit test if intermediate states have data structures. |
Nice! Will do it this way then |
Refactored |
Seems good so far |
Going to develop the CLI now Any suggestions? |
diesel_cli is worth looking into. |
For now, I'm mapping db column into one of basic rust type listed below. Do you think this is reasonable?
It's difficult to map, for example, an integer db column into specific sea-orm/sea-orm-codegen/src/entity/column.rs Lines 21 to 44 in 2f6b216
|
So, I'm thinking is there any good way to generically convert However, I understand that |
I agree we can improve SeaSchema to output more specific ColumnDef provided that there is no data loss. |
Checkout the quick demo of sea-orm-cli 33f16e1 |
Can't try without the sea query commits :( |
Checkout sea-query/readonly branch |
I tried. Quite good so far. This doesn't compile: fn def(&self) -> RelationDef {
match self {}
} And we should not use And we should also generate some doc comment: //! SeaORM Entity. Generated by sea-orm 0.1.0
|
Oh, and it'd also be a good idea to generate the |
Ah ha good idea |
Several amendments required:
As ref: impl Related<super::filling::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling::Relation::Filling.def()
}
fn via() -> Option<RelationDef> {
Some(super::cake_filling::Relation::Cake.def().rev())
}
} But for 4, if we determined that it is too hard, we can delay it post release. |
What do u mean by "reverse relation"? For
Because foreign key is on Then, we have... // fruit.rs
impl Related<super::cake::Entity> for Entity {
fn to() -> RelationDef {
Relation::Cake.def()
}
} // cake.rs
impl Related<super::fruit::Entity> for Entity {
fn to() -> RelationDef {
super::Fruit::Relation::Cake.def().rev()
}
} |
Basically yes. Each relation should have only one owner where we define it. |
Then basically we won't see has_many loll |
How about impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Fruit => Entity::has_many(super::fruit::Entity).into()
}
}
} That means we actually require that the one side relation exists, and automatically define the has_many relation. |
Done. See d77a7c8 |
To clarify again, if the foreign key column also has an unique constraint, then the reverse of it would also be Note: our current test schema does not have has_one. |
Ah ha! cool |
Got it! |
But then this special has_one still need to define something like... on both end 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(),
}
}
} |
I will add a |
Nice! |
Is this still valid? After we have Check 9a25bb9 |
How can we detect 'junction table' and generate Perhaps:
|
Btw, the examples |
Yes, in terms of output Just two different way to invoke codegen |
Then we can remove one of them |
For 2, yes we can do that when we found all "junction table". For 1, I think the problem is to define what we mean by "junction table"? Table that meets one of the following criteria?
|
I agree, may be we can handle this common enough M-to-N junction case first |
Generating entity file for each db table.
Entity
Model
find_*
helper functionColumn
ColumnTrait
column defPrimaryKey
Relation
RelationTrait
relation defRelated
Work plan in progress...
The text was updated successfully, but these errors were encountered: