Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions sea-orm-codegen/src/entity/base_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ pub struct Entity {
pub(crate) relations: Vec<Relation>,
pub(crate) conjunct_relations: Vec<ConjunctRelation>,
pub(crate) primary_keys: Vec<PrimaryKey>,
pub(crate) is_view: bool,
}

impl Entity {
pub fn is_view(&self) -> bool {
self.is_view
}

pub fn get_table_name_snake_case(&self) -> String {
self.table_name.to_snake_case()
}
Expand Down Expand Up @@ -351,6 +356,7 @@ mod tests {
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
is_view: false,
}
}

Expand Down
1 change: 1 addition & 0 deletions sea-orm-codegen/src/entity/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ impl EntityTransformer {
relations: relations.clone(),
conjunct_relations: vec![],
primary_keys,
is_view: false,
};
entities.insert(table_name.clone(), entity.clone());
for mut rel in relations.into_iter() {
Expand Down
17 changes: 17 additions & 0 deletions sea-orm-codegen/src/entity/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,7 @@ mod tests {
via: "cake_filling".to_owned(),
to: "filling".to_owned(),
}],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -970,6 +971,7 @@ mod tests {
},
],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![
PrimaryKey {
name: "cake_id".to_owned(),
Expand Down Expand Up @@ -1019,6 +1021,7 @@ mod tests {
impl_related: true,
}],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![
PrimaryKey {
name: "cake_id".to_owned(),
Expand Down Expand Up @@ -1053,6 +1056,7 @@ mod tests {
via: "cake_filling".to_owned(),
to: "cake".to_owned(),
}],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -1110,6 +1114,7 @@ mod tests {
},
],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -1154,6 +1159,7 @@ mod tests {
impl_related: true,
}],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -1324,6 +1330,7 @@ mod tests {
},
],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -1371,6 +1378,7 @@ mod tests {
via: "cake_filling".to_owned(),
to: "filling".to_owned(),
}],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -1418,6 +1426,7 @@ mod tests {
via: "cake_filling".to_owned(),
to: "filling".to_owned(),
}],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -1452,6 +1461,7 @@ mod tests {
],
relations: vec![],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -1486,6 +1496,7 @@ mod tests {
],
relations: vec![],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -1522,6 +1533,7 @@ mod tests {
impl_related: true,
}],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![
PrimaryKey {
name: "id1".to_owned(),
Expand Down Expand Up @@ -1571,6 +1583,7 @@ mod tests {
impl_related: true,
}],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -2155,6 +2168,7 @@ mod tests {
via: "cake_filling".to_owned(),
to: "filling".to_owned(),
}],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -2832,6 +2846,7 @@ mod tests {
],
relations: vec![],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -2941,6 +2956,7 @@ mod tests {
],
relations: vec![],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down Expand Up @@ -3002,6 +3018,7 @@ mod tests {
],
relations: vec![],
conjunct_relations: vec![],
is_view: false,
primary_keys: vec![PrimaryKey {
name: "id".to_owned(),
}],
Expand Down
1 change: 1 addition & 0 deletions sea-orm-macros/src/derives/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod derive_attr {
pub relation: Option<syn::Ident>,
pub schema_name: Option<syn::LitStr>,
pub table_name: Option<syn::LitStr>,
pub view: Option<()>,
pub comment: Option<syn::LitStr>,
pub table_iden: Option<()>,
pub rename_all: Option<syn::LitStr>,
Expand Down
118 changes: 94 additions & 24 deletions sea-orm-macros/src/derives/entity_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use syn::{
/// Method to derive an Model
pub fn expand_derive_entity_model(data: &Data, attrs: &[Attribute]) -> syn::Result<TokenStream> {
// if #[sea_orm(table_name = "foo", schema_name = "bar")] specified, create Entity struct
// if #[sea_orm(table_name = "foo", view)] specified, create a View entity (read-only)
let mut table_name = None;
let mut is_view = false;
let mut comment = quote! {None};
let mut schema_name = quote! { None };
let mut table_iden = false;
Expand All @@ -28,6 +30,8 @@ pub fn expand_derive_entity_model(data: &Data, attrs: &[Attribute]) -> syn::Resu
comment = quote! { Some(#name) };
} else if meta.path.is_ident("table_name") {
table_name = Some(meta.value()?.parse::<LitStr>()?);
} else if meta.path.is_ident("view") {
is_view = true;
} else if meta.path.is_ident("schema_name") {
let name: Lit = meta.value()?.parse()?;
schema_name = quote! { Some(#name) };
Expand All @@ -48,32 +52,92 @@ pub fn expand_derive_entity_model(data: &Data, attrs: &[Attribute]) -> syn::Resu
})
})?;

let entity_def = table_name
if is_view && table_name.is_none() {
return Err(syn::Error::new(
Span::call_site(),
"Attribute `view` requires `table_name` to be specified, e.g. #[sea_orm(table_name = \"foo\", view)].",
));
}

let entity_name = table_name.clone();

// TODO: Add registry support to views

let entity_def = entity_name
.as_ref()
.map(|table_name| {
let entity_extra_attr = if model_ex {
quote!(#[sea_orm(model_ex = ModelEx, active_model_ex = ActiveModelEx)])
} else {
quote!()
};
quote! {
#[doc = " Generated by sea-orm-macros"]
#[derive(Copy, Clone, Default, Debug, sea_orm::prelude::DeriveEntity)]
#entity_extra_attr
pub struct Entity;

#[automatically_derived]
impl sea_orm::prelude::EntityName for Entity {
fn schema_name(&self) -> Option<&str> {
#schema_name
.map(|name| {
if is_view {
quote! {
#[doc = " Generated by sea-orm-macros"]
#[derive(Copy, Clone, Default, Debug)]
pub struct Entity;

#[automatically_derived]
impl sea_orm::prelude::EntityName for Entity {
fn schema_name(&self) -> Option<&str> {
#schema_name
}

fn table_name(&self) -> &'static str {
#name
}

fn comment(&self) -> Option<&str> {
#comment
}
}

#[automatically_derived]
impl sea_orm::prelude::Iden for Entity {
fn unquoted(&self) -> &str {
#name
}
}

fn table_name(&self) -> &'static str {
#table_name
#[automatically_derived]
impl sea_orm::prelude::IdenStatic for Entity {
fn as_str(&self) -> &'static str {
#name
}
}

#[automatically_derived]
impl sea_orm::prelude::EntityTrait for Entity {
type Model = Model;
type ModelEx = Model;
type ActiveModel = sea_orm::NeverActiveModel<Entity>;
type ActiveModelEx = sea_orm::NeverActiveModel<Entity>;
type Column = Column;
type PrimaryKey = PrimaryKey;
type Relation = Relation;
}
}
} else {
// Generate regular Entity (implements EntityTrait)
let entity_extra_attr = if model_ex {
quote!(#[sea_orm(model_ex = ModelEx, active_model_ex = ActiveModelEx)])
} else {
quote!()
};
quote! {
#[doc = "Generated by sea-orm-macros"]
#[derive(Copy, Clone, Default, Debug, sea_orm::prelude::DeriveEntity)]
#entity_extra_attr
pub struct Entity;

#[automatically_derived]
impl sea_orm::prelude::EntityName for Entity {
fn schema_name(&self) -> Option<&str> {
#schema_name
}

fn comment(&self) -> Option<&str> {
#comment
fn table_name(&self) -> &'static str {
#name
}

fn comment(&self) -> Option<&str> {
#comment
}
}
}
}
Expand All @@ -90,11 +154,11 @@ pub fn expand_derive_entity_model(data: &Data, attrs: &[Attribute]) -> syn::Resu
let mut primary_key_types: Punctuated<_, Comma> = Punctuated::new();
let mut auto_increment = true;
if table_iden {
if let Some(table_name) = &table_name {
if let Some(name) = &entity_name {
let table_field_name = Ident::new("Table", Span::call_site());
columns_enum.push(quote! {
#[doc = " Generated by sea-orm-macros"]
#[sea_orm(table_name=#table_name)]
#[sea_orm(table_name=#name)]
#[strum(disabled)]
#table_field_name
});
Expand Down Expand Up @@ -403,7 +467,13 @@ pub fn expand_derive_entity_model(data: &Data, attrs: &[Attribute]) -> syn::Resu
columns_save_as.push_punct(Comma::default());
}

let primary_key = {
let primary_key = if is_view && primary_keys.is_empty() {
// Views may not have primary keys; use NeverPrimaryKey to satisfy EntityTrait bounds.
quote! {
#[doc = " Generated by sea-orm-macros"]
pub type PrimaryKey = sea_orm::NeverPrimaryKey<Column>;
}
} else {
let auto_increment = auto_increment && primary_keys.len() == 1;
let primary_key_types = if primary_key_types.len() == 1 {
let first = primary_key_types.first();
Expand Down
31 changes: 26 additions & 5 deletions sea-orm-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,21 +146,42 @@ pub fn derive_entity_model(input: TokenStream) -> TokenStream {
panic!("Struct name must be Model");
}

let is_view = attrs.iter().any(|attr| {
if attr.path().is_ident("sea_orm") {
let mut found_view = false;
let _ = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("view") {
found_view = true;
} else {
let _ = meta.value().and_then(|v| v.parse::<syn::Expr>());
}
Ok(())
});
found_view
} else {
false
}
});

let mut ts: TokenStream = derives::expand_derive_entity_model(&data, &attrs)
.unwrap_or_else(Error::into_compile_error)
.into();

// Views are read-only: we still derive Model (ModelTrait + FromQueryResult),
// but skip deriving an ActiveModel / IntoActiveModel.
ts.extend::<TokenStream>(
derives::expand_derive_model(&ident, &data, &attrs)
.unwrap_or_else(Error::into_compile_error)
.into(),
);

ts.extend::<TokenStream>(
derives::expand_derive_active_model(&ident, &data)
.unwrap_or_else(Error::into_compile_error)
.into(),
);
if !is_view {
ts.extend::<TokenStream>(
derives::expand_derive_active_model(&ident, &data)
.unwrap_or_else(Error::into_compile_error)
.into(),
);
}

ts
}
Expand Down
7 changes: 6 additions & 1 deletion src/entity/active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ pub trait ActiveModelTrait: Clone + Debug {
self.get(cols.next()?.into_column()).into_value()?
};
}
match <<<Self::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY {
let arity =
<<<Self::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY;
if arity == 0 {
return None;
}
match arity {
1 => {
let s1 = next!();
Some(ValueTuple::One(s1))
Expand Down
Loading
Loading