sequelize-typescript icon indicating copy to clipboard operation
sequelize-typescript copied to clipboard

Can't define scopes in separate variables/files to use in the @DefaultScope and @Scope decorators

Open Brobin opened this issue 3 years ago • 3 comments

Versions

  • sequelize: 6.4.0
  • sequelize-typescript: 2.0.0
  • typescript: 4.1.3

I'm submitting a ... [x] bug report [x] feature request

Actual behavior: I am unable to use the decorators to define scopes for one of my models. It has quite a few relations and nested includes, so I have delcared them in a separate file

// ModelAScopes.ts
const defaultScope = {
  include: [
    {
      model: ModelB,
      as: 'modelBs'
    }
  ]
}

// ModelA.ts
@DefaultScope(() => defaultScope)
@Table({ tableName: 'model_a' })
export defulat lass ModelA extends Model {
  @PrimaryKey
  @Column(DataType.STRING(36))
  public uuid!: string;

  @HasMany(() => ModelB)
  modelBs!: ModelB[];
}

// ModelB.ts
@Table({ tableName: 'model_b' })
export default class ModelBp extends Model {
  @PrimaryKey
  @AutoIncrement
  @Column(DataType.BIGINT)
  public id!: number;

  @ForeignKey(() => ModelA)
  @Column({
    type: DataType.STRING(36),
    field: 'model_a_uuid',
    onDelete: 'cascade',
  })
  public modelAUUID!: string;

  @BelongsTo(() => ModelA)
  public quote!: ModelA;
}

I get the following error when running code that touches this model

Include unexpected. Element has to be either a Model, an Association or an object.

Using the exact same scope definitions, the following way of adding the scopes to the model works.


sequelize.addModels([...,ModelA, ModelB]);

ModelA.addScope('defaultScope', defaultScope, { override: true });

Expected behavior: I expect the behavior to be the same regardless of where I create the scopes.

It would be nice to be able to create the scope definitions elsewhere and then pass them into the decorators. For example, I am combining a couple different scopes to avoid duplicate code, like this


const defaultScope = { include: [ModelB };
const apiScope= { attributes: { exclude: 'privateField' }, ...defaultScope }

Brobin avatar Jan 26 '21 19:01 Brobin

I think I figured it out.

Each hook must be a static method. Multiple hooks can be attached to a single method, and you can define multiple methods for a given hook.

It appears that by defining the hooks in a separate file, or even in a variable does not work since it doesn't register ModelB as a model

Brobin avatar Jan 26 '21 20:01 Brobin

If it's still relevant, I figured out a way to make it work. I had the same issue with hooks. The key is to export a function that returns the scope definition rather that the object itself. So, this works:

// ModelAScopes.ts
const getDefaultScope = () => ({
  include: [
    {
      model: ModelB,
      as: 'modelBs'
    }
  ]
})

// ModelA.ts
@DefaultScope(() => getDefaultScope())
@Table({ tableName: 'model_a' })

I think it has to do with how node resolves circular dependencies or something...

Papooch avatar Jun 22 '21 10:06 Papooch

@Papooch your solution works 👏

lan-nguyen91 avatar Dec 25 '22 16:12 lan-nguyen91