Skip to content

--entity-format=dense generates logically different code than compact and expanded #2962

@Deedasmi

Description

@Deedasmi

Description

https://codeberg.org/deedasmi/sheltertool

./generate.sh is used to re-run entity generation

sea-orm-cli generate entity -o packages/api/src/model/backend/generated/ --preserve-user-modifications=false --entity-format dense --date-time-crate time --with-serde both --with-prelude none

I switched it to --entity-format compact and recompiled to immediately receive errors related to ActiveModel and EntityLoader

  --> packages/api/src/model/backend/logic/person.rs:44:25
   |
44 |         person::Entity::load()
   |                         ^^^^ function or associated item not found in `generated::person::Entity`
   |
  ::: packages/api/src/model/backend/generated/person.rs:8:1
   |
 8 | pub struct Entity;
   | ----------------- function or associated item `load` not found for this struct
cannot find type `ModelEx` in module `session`
   --> packages/api/src/model/backend/logic/user.rs:130:29
    |
130 |     type ModelEx = session::ModelEx;
    |                             ^^^^^^^ help: a struct with a similar name exists: `Model`
    |
   ::: packages/api/src/model/backend/generated/session.rs:16:1
    |
 16 | pub struct Model {
    | ---------------- similarly named struct `Model` defined here

Switching back to dense causes the code to work again. Switching to expanded causes the same errors again.

Steps to Reproduce

  1. Clone sheltertool repo
  2. Run migrate up on empty postgres DB
  3. ./generate.sh
  4. cargo check --features server returns no errors
  5. Change ./generate.sh to use --entity-format compact
  6. ./generate.sh
  7. cargo check --features server fails.
  8. Change ./generate.sh to use --entity-format dense
  9. ./generate.sh
  10. cargo check --features server succeeds again.

Expected Behavior

Cargo check/build --features server should compile regardless of entity format

Actual Behavior

Only dense format compiles successfully.

Dense:

#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "emergency_contact")]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    pub person_id: i32,
    pub tenant_id: i32,
    pub name: String,
    pub number: String,
    pub email: Option<String>,
    pub relation: String,
    #[sea_orm(
        belongs_to,
        from = "person_id",
        to = "id",
        on_update = "NoAction",
        on_delete = "Cascade"
    )]
    pub person: HasOne<super::person::Entity>,
    #[sea_orm(
        belongs_to,
        from = "tenant_id",
        to = "id",
        on_update = "NoAction",
        on_delete = "Cascade"
    )]
    pub tenant: HasOne<super::tenant::Entity>,
}

impl ActiveModelBehavior for ActiveModel {}

compact:

//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0

use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "user")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    #[sea_orm(unique)]
    pub email: String,
    pub password: String,
    pub force_password_reset: bool,
    pub created_at: TimeDateTimeWithTimeZone,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(has_many = "super::person::Entity")]
    Person,
    #[sea_orm(has_many = "super::session::Entity")]
    Session,
    #[sea_orm(has_many = "super::tenant_user_role::Entity")]
    TenantUserRole,
}

impl Related<super::person::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Person.def()
    }
}

impl Related<super::session::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Session.def()
    }
}

impl Related<super::tenant_user_role::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::TenantUserRole.def()
    }
}

impl Related<super::tenant::Entity> for Entity {
    fn to() -> RelationDef {
        super::tenant_user_role::Relation::Tenant.def()
    }
    fn via() -> Option<RelationDef> {
        Some(super::tenant_user_role::Relation::User.def().rev())
    }
}

impl ActiveModelBehavior for ActiveModel {}

expanded:

//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0

use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;

impl EntityName for Entity {
    fn table_name(&self) -> &'static str {
        "user"
    }
}

#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq, Serialize, Deserialize)]
pub struct Model {
    pub id: i32,
    pub email: String,
    pub password: String,
    pub force_password_reset: bool,
    pub created_at: TimeDateTimeWithTimeZone,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
    Id,
    Email,
    Password,
    ForcePasswordReset,
    CreatedAt,
}

#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
    Id,
}

impl PrimaryKeyTrait for PrimaryKey {
    type ValueType = i32;
    fn auto_increment() -> bool {
        true
    }
}

#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
    Person,
    Session,
    TenantUserRole,
}

impl ColumnTrait for Column {
    type EntityName = Entity;
    fn def(&self) -> ColumnDef {
        match self {
            Self::Id => ColumnType::Integer.def(),
            Self::Email => ColumnType::String(StringLen::None).def().unique(),
            Self::Password => ColumnType::String(StringLen::None).def(),
            Self::ForcePasswordReset => ColumnType::Boolean.def(),
            Self::CreatedAt => ColumnType::TimestampWithTimeZone.def(),
        }
    }
}

impl RelationTrait for Relation {
    fn def(&self) -> RelationDef {
        match self {
            Self::Person => Entity::has_many(super::person::Entity).into(),
            Self::Session => Entity::has_many(super::session::Entity).into(),
            Self::TenantUserRole => Entity::has_many(super::tenant_user_role::Entity).into(),
        }
    }
}

impl Related<super::person::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Person.def()
    }
}

impl Related<super::session::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Session.def()
    }
}

impl Related<super::tenant_user_role::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::TenantUserRole.def()
    }
}

impl Related<super::tenant::Entity> for Entity {
    fn to() -> RelationDef {
        super::tenant_user_role::Relation::Tenant.def()
    }
    fn via() -> Option<RelationDef> {
        Some(super::tenant_user_role::Relation::User.def().rev())
    }
}

impl ActiveModelBehavior for ActiveModel {}

Reproduces How Often

100%

Workarounds

Continue to use dense entity format

Versions

sea-orm-cli 2.0.0-rc.32

latest available cargo-update

├── sea-orm-migration v2.0.0-rc.32
│ ├── sea-orm v2.0.0-rc.32
│ │ ├── sea-orm-macros v2.0.0-rc.32 (proc-macro)
│ │ │ ├── sea-bae v0.2.1 (proc-macro)
│ │ ├── sea-query v1.0.0-rc.31
│ │ │ ├── sea-query-derive v1.0.0-rc.12 (proc-macro)
│ │ ├── sea-query-sqlx v0.8.0-rc.14
│ │ │ ├── sea-query v1.0.0-rc.31 ()
│ │ ├── sea-schema v0.17.0-rc.17
│ │ │ ├── sea-query v1.0.0-rc.31 (
)
│ │ │ ├── sea-query-sqlx v0.8.0-rc.14 ()
│ │ │ ├── sea-schema-derive v0.3.0 (proc-macro)
│ ├── sea-orm-cli v2.0.0-rc.32
│ ├── sea-schema v0.17.0-rc.17 (
)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions