SeaQL / SeaQL/sea-orm

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

Open
#2,962 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
9.9k
Forks
734
Avg merge
6h 36m
Merged PRs (30d)
8

Description

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 (
)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ./generate.sh and the sea-orm-cli entity-generation command, comparing the generated person.rs, session.rs, and user.rs output for dense, compact, and expanded formats. Run cargo check --features server after each generation; done means all three formats expose the APIs expected by the logic files and compile successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.