sequelize / sequelize-typescript

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

Two manytomany association eagerLoad Problem #319

Closed chaoxihailing closed 6 years ago

chaoxihailing commented 6 years ago

I have two ManyToMany model CourseSkill and CourseSkillUser,and I want to these model associate to get User but it happen error CourseSkillUser is not associated to Skill.

these model define like below:

class Course extends Model<Course> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@BelongsToMany(() => Skill, () => CourseSkill)
public skills: Skill[];
}

class User extends Model<User> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@BelongsToMany(() => CourseSkill, () => CourseSkillUser)
public courseSkills: CourseSkill[];
}

class Skill extends Model<Skill> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@BelongsToMany(() => Course, () => CourseSkill)
 public courses: Course[];
 }

class CourseSkill extends Model<CourseSkill> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@ForeignKey(() => Course)
@Column
public courseId: number;

@ForeignKey(() => Skill)
@Column
public skillId: number;

@BelongsToMany(() => User, () => CourseSkillUser)
public users: User[];
}

class CourseSkillUser extends Model<CourseSkillUser> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@ForeignKey(() => CourseSkill)
@Column
public courseSkillId: number;

@ForeignKey(() => User)
@Column
public userId: number;
}

query method

let result = await Course.findOne({
include: [
    {
        model: Skill,
        include: [{
        model: CourseSkillUser,
        include: [{model: User}]
     }],
    }
]});

error image

RobinBuschmann commented 6 years ago

This is because Skill - which is the "parent" model in this query - doesn't know about an association to CourseSkillUser. You didn't define one in Skill. The problem with that is, you can not define an include for a through model, which would be necessary to get the associated CourseSkillUser entries. A through model is implicitly used to resolve a many to many relation. So you need to define an explicit relation between these models (CourseSkillUser and CourseSkill) to get what you want be resolved.

See https://stackoverflow.com/questions/38726793/sequelize-include-model-of-through-attribute and here: http://sequelize.readthedocs.io/en/latest/docs/models-usage/index.html#eager-loading

When you look at the setup of the associations in the sequelize docs example, you will notice that there is no belongsToMany relation with a through model used.

If you don't get it working with the help of these examples, feel free to come back - I can help you then.

chaoxihailing commented 6 years ago

I see these information, Does it mean I have to not use belongsToMany relation with a through model used? I have to remove model CourseSkill, and modify CourseSkillUser association model CourseSkill, User.

model define

class Course extends Model<Course> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@BelongsToMany(() => Skill, () => CourseSkillUser)
public skills: Skill[];
}

class User extends Model<User> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@BelongsToMany(() => Skill, () => CourseSkillUser)
public skills: Skill[];

@BelongsToMany(() => Course, () => CourseSkillUser)
public course: Course[];
}

class Skill extends Model<Skill> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@BelongsToMany(() => Course, () => CourseSkillUser)
public courses: Course[];
}

class CourseSkillUser extends Model<CourseSkillUser> {
@PrimaryKey
@AutoIncrement
@Column
public id: number;

@ForeignKey(() => Skill)
@Column
public skillId: number;

@ForeignKey(() => Course)
@Column
public courseId: number;

@ForeignKey(() => User)
@Column
public userId: number;
}
RobinBuschmann commented 6 years ago

@chaoxihailing Sry for the late response. Yes, to get this working like you want to, you need to use HasMany and BelongsTo.

class Course extends Model<Course> {
  @HasMany(() => CourseSkillUser)
   courseSkillUser: CourseSkillUser[];
}

class CourseSkillUser extends Model<CourseSkillUser> {
  @ForeignKey(() => Skill)
  @Column
  skillId: number;

  @BelongsTo(() => Skill)
  skill: Skill;

  @ForeignKey(() => Course)
  @Column
  courseId: number;

  @BelongsTo(() => Course)
  course: Course;

  @ForeignKey(() => User)
  @Column
  userId: number;

  @BelongsTo(() => User)
  user: User;
}

And now you should be able to request the data like:

Course.findOne({
  include: [
    {
      model: CourseSkillUser,
      include: [User, Skill]
    }
  ]
});

Does this solve your other question (https://github.com/RobinBuschmann/sequelize-typescript/issues/334) too?

chaoxihailing commented 6 years ago

thanks your useful response, it does resolve my question.