sequelize / sequelize-typescript

Decorators and some other features for sequelize
MIT License
2.79k stars 282 forks source link

Sequelize `get({ plain: true })` or `toJSON()` doesn't work with associations #940

Open mopcweb opened 3 years ago

mopcweb commented 3 years ago

Hi! Thanks for your effort and this lib!

Issue

I've been using sequelize-typescript for some time and noticed, that associations are not added to dataValues of native Sequelize object when fetching any records. Because of this, after calling Sequelize.get({ plain: true }) or Sequelize.toJSON() - there is no association data in resulting object.

Versions

Issue type

Actual behavior

const result = SomeModel.findByPk(1, { include: [AnotherModel] });
console.log(result.get({ plain: true }).anotherModel // undefined

Expected behavior

console.log(result.get({ plain: true }).anotherModel // here should be an object for AnotherModel record.

Steps to reproduce


@Table({ tableName: 'First' })
export class First extends Model<IFirst> implements IFirst {
  @PrimaryKey
  @AutoIncrement
  @Column
  public id: number;

  @Column
  public title: string;
}

export class Second extends Model<ISecond> implements ISecond {
  @PrimaryKey
  @AutoIncrement
  @Column
  public id: number;

  @Column
  public title: string;

  @ForeignKey(() => First)
  @Column
  public firstId: number;

  @BelongsTo(() => First)
  public first: IFirst;
}

export interface IFirst { id: string; title: string }

export interface ISecond { id: string; title: string }

async function test(): Pormise<void> {
  const result = await Second.findByPk(1, { include: [First] });
  console.log(result.get({ plain: true }).first); // indefined
  console.log(result.toJSON().first); // indefined
}

a bit similar to https://github.com/RobinBuschmann/sequelize-typescript/issues/605

mopcweb commented 3 years ago

Found temporary solution - use raw: true and nest: true in query. raw: true - will return plain object instead of Model instance, but there won't be nested objects for associations. nest: true - will map properties of type: 'first.title': 'someValue' to first: { title: 'someValue' }.

Second.findByPk(1, { include: [First], raw: true, first: true });

image

mopcweb commented 3 years ago

are there any updates?(

lwileczek commented 1 year ago

I either use Second.findByPk(1, { include: [First], raw: true, first: true }); Like you mention above or I overwrite the toJSON function for my models to always call the associations.


@Table({ tableName: 'First' })
export class First extends Model<IFirst> implements IFirst {
  @PrimaryKey
  @AutoIncrement
  @Column
  public id: number;

  @Column
  public title: string;
}

export class Second extends Model<ISecond> implements ISecond {
  @PrimaryKey
  @AutoIncrement
  @Column
  public id: number;

  @Column
  public title: string;

  @ForeignKey(() => First)
  @Column
  public firstId: number;

  @BelongsTo(() => First)
  public first: IFirst;

  toJSON(): object {
    return { ...this.get(), first: this.first }
  }
}

export interface IFirst { id: string; title: string }

export interface ISecond { id: string; title: string }

async function test(): Pormise<void> {
  const result = await Second.findByPk(1, { include: [First] });
  console.log(result.toJSON().first);
}
mopcweb commented 1 year ago

after dozens of different "hacks" i get to next - ORMs in JS r so inmature :( if orm is smth necessary - better to use typeorm instead of sequelize. but still - much better - just query builder and raw sql queries - orms are no way for complex queries :(

lwileczek commented 1 year ago

you can run a raw query if you cannot get exactly what you want: https://sequelize.org/docs/v6/core-concepts/raw-queries/