高级概念

作用域

本节涉及关联作用域. 有关关联作用域与关联模型作用域的定义, 请参阅 作用域.

关联作用域允许你在关联上放置作用域(getcreate的一组默认属性). 作用域可以放在关联模型(关联的目标)上,也可以放在n:m关系的中间表上.

1:n

假设我们有模型 Comment,Post 和 Image. Comment可以通过 commentableIdcommentable 与Image或Post相关联 - 我们称 Post 和 Image 为 `Commentable’

  1. class Post extends Model {}
  2. Post.init({
  3. title: Sequelize.STRING,
  4. text: Sequelize.STRING
  5. }, { sequelize, modelName: 'post' });
  6. class Image extends Model {}
  7. Image.init({
  8. title: Sequelize.STRING,
  9. link: Sequelize.STRING
  10. }, { sequelize, modelName: 'image' });
  11. class Comment extends Model {
  12. getItem(options) {
  13. return this[
  14. 'get' +
  15. this.get('commentable')
  16. [0]
  17. .toUpperCase() +
  18. this.get('commentable').substr(1)
  19. ](options);
  20. }
  21. }
  22. Comment.init({
  23. title: Sequelize.STRING,
  24. commentable: Sequelize.STRING,
  25. commentableId: Sequelize.INTEGER
  26. }, { sequelize, modelName: 'comment' });
  27. Post.hasMany(Comment, {
  28. foreignKey: 'commentableId',
  29. constraints: false,
  30. scope: {
  31. commentable: 'post'
  32. }
  33. });
  34. Comment.belongsTo(Post, {
  35. foreignKey: 'commentableId',
  36. constraints: false,
  37. as: 'post'
  38. });
  39. Image.hasMany(Comment, {
  40. foreignKey: 'commentableId',
  41. constraints: false,
  42. scope: {
  43. commentable: 'image'
  44. }
  45. });
  46. Comment.belongsTo(Image, {
  47. foreignKey: 'commentableId',
  48. constraints: false,
  49. as: 'image'
  50. });

constraints: false, 禁用引用约束 - 因为 commentableId 列引用了几个表,我们不能为它添加REFERENCES 约束. 请注意,Image -> Comment和Post -> Comment关系定义了一个作用域,分别是 commentable: 'image'commentable: 'post'. 使用关联函数时,将自动应用此作用域:

  1. image.getComments()
  2. // SELECT "id", "title", "commentable", "commentableId", "createdAt", "updatedAt" FROM "comments" AS
  3. // "comment" WHERE "comment"."commentable" = 'image' AND "comment"."commentableId" = 1;
  4. image.createComment({
  5. title: 'Awesome!'
  6. })
  7. // INSERT INTO "comments" ("id","title","commentable","commentableId","createdAt","updatedAt") VALUES
  8. // (DEFAULT,'Awesome!','image',1,'2018-04-17 05:36:40.454 +00:00','2018-04-17 05:36:40.454 +00:00')
  9. // RETURNING *;
  10. image.addComment(comment);
  11. // UPDATE "comments" SET "commentableId"=1,"commentable"='image',"updatedAt"='2018-04-17 05:38:43.948
  12. // +00:00' WHERE "id" IN (1)

Comment 上的 getItem 实用函数完成了图片 - 它只是将commentable字符串转换为对getImagegetPost的调用,提供了 comment 是属于post还是image的抽象. 你可以将普通选项对象作为参数传递给getItem(options) 以指定 where 条件或包含的任何位置.

n:m

继续使用多态模型的概念,考虑一个标签表 - 一个项目可以有多个标签,标签可以与多个项目相关联.

为简洁起见,该示例仅显示Post模型,但实际上Tag将与其他几个模型相关.

  1. class ItemTag extends Model {}
  2. ItemTag.init({
  3. id: {
  4. type: Sequelize.INTEGER,
  5. primaryKey: true,
  6. autoIncrement: true
  7. },
  8. tagId: {
  9. type: Sequelize.INTEGER,
  10. unique: 'item_tag_taggable'
  11. },
  12. taggable: {
  13. type: Sequelize.STRING,
  14. unique: 'item_tag_taggable'
  15. },
  16. taggableId: {
  17. type: Sequelize.INTEGER,
  18. unique: 'item_tag_taggable',
  19. references: null
  20. }
  21. }, { sequelize, modelName: 'item_tag' });
  22. class Tag extends Model {}
  23. Tag.init({
  24. name: Sequelize.STRING,
  25. status: Sequelize.STRING
  26. }, { sequelize, modelName: 'tag' });
  27. Post.belongsToMany(Tag, {
  28. through: {
  29. model: ItemTag,
  30. unique: false,
  31. scope: {
  32. taggable: 'post'
  33. }
  34. },
  35. foreignKey: 'taggableId',
  36. constraints: false
  37. });
  38. Tag.belongsToMany(Post, {
  39. through: {
  40. model: ItemTag,
  41. unique: false
  42. },
  43. foreignKey: 'tagId',
  44. constraints: false
  45. });

请注意,作用域列(taggable)现在位于中间模型(ItemTag)上.

我们还可以定义一个更具限制性的关联,例如,通过应用中间模型(ItemTag)和目标模型(Tag)的作用域来获取post的所有待定tag:

  1. Post.belongsToMany(Tag, {
  2. through: {
  3. model: ItemTag,
  4. unique: false,
  5. scope: {
  6. taggable: 'post'
  7. }
  8. },
  9. scope: {
  10. status: 'pending'
  11. },
  12. as: 'pendingTags',
  13. foreignKey: 'taggableId',
  14. constraints: false
  15. });
  16. post.getPendingTags();
  1. SELECT
  2. "tag"."id",
  3. "tag"."name",
  4. "tag"."status",
  5. "tag"."createdAt",
  6. "tag"."updatedAt",
  7. "item_tag"."id" AS "item_tag.id",
  8. "item_tag"."tagId" AS "item_tag.tagId",
  9. "item_tag"."taggable" AS "item_tag.taggable",
  10. "item_tag"."taggableId" AS "item_tag.taggableId",
  11. "item_tag"."createdAt" AS "item_tag.createdAt",
  12. "item_tag"."updatedAt" AS "item_tag.updatedAt"
  13. FROM
  14. "tags" AS "tag"
  15. INNER JOIN "item_tags" AS "item_tag" ON "tag"."id" = "item_tag"."tagId"
  16. AND "item_tag"."taggableId" = 1
  17. AND "item_tag"."taggable" = 'post'
  18. WHERE
  19. ("tag"."status" = 'pending');

constraints: false 禁用 taggableId 列的引用约束. 因为列是多态的,所以我们不能说它 REFERENCES 了一个特定的表.

用关联创建

如果所有元素都是新的,则可以在一个步骤中创建具有嵌套关联的实例.

BelongsTo / HasMany / HasOne 关联

考虑以下模型:

  1. class Product extends Model {}
  2. Product.init({
  3. title: Sequelize.STRING
  4. }, { sequelize, modelName: 'product' });
  5. class User extends Model {}
  6. User.init({
  7. firstName: Sequelize.STRING,
  8. lastName: Sequelize.STRING
  9. }, { sequelize, modelName: 'user' });
  10. class Address extends Model {}
  11. Address.init({
  12. type: Sequelize.STRING,
  13. line1: Sequelize.STRING,
  14. line2: Sequelize.STRING,
  15. city: Sequelize.STRING,
  16. state: Sequelize.STRING,
  17. zip: Sequelize.STRING,
  18. }, { sequelize, modelName: 'address' });
  19. Product.User = Product.belongsTo(User);
  20. User.Addresses = User.hasMany(Address);
  21. // 也能用于 `hasOne`

可以通过以下方式在一个步骤中创建一个新的Product, User和一个或多个Address:

  1. return Product.create({
  2. title: 'Chair',
  3. user: {
  4. firstName: 'Mick',
  5. lastName: 'Broadstone',
  6. addresses: [{
  7. type: 'home',
  8. line1: '100 Main St.',
  9. city: 'Austin',
  10. state: 'TX',
  11. zip: '78704'
  12. }]
  13. }
  14. }, {
  15. include: [{
  16. association: Product.User,
  17. include: [ User.Addresses ]
  18. }]
  19. });

这里,我们的用户模型称为user,带小写u - 这意味着对象中的属性也应该是user. 如果给sequelize.define指定的名称为User,对象中的键也应为User. 对于addresses也是同样的,除了它是一个 hasMany 关联的复数.

用别名创建 BelongsTo 关联

可以将前面的示例扩展为支持关联别名.

  1. const Creator = Product.belongsTo(User, { as: 'creator' });
  2. return Product.create({
  3. title: 'Chair',
  4. creator: {
  5. firstName: 'Matt',
  6. lastName: 'Hansen'
  7. }
  8. }, {
  9. include: [ Creator ]
  10. });

HasMany / BelongsToMany 关联

我们来介绍将产品与许多标签相关联的功能. 设置模型可能如下所示:

  1. class Tag extends Model {}
  2. Tag.init({
  3. name: Sequelize.STRING
  4. }, { sequelize, modelName: 'tag' });
  5. Product.hasMany(Tag);
  6. // 也能用于 `belongsToMany`.

现在,我们可以通过以下方式创建具有多个标签的产品:

  1. Product.create({
  2. id: 1,
  3. title: 'Chair',
  4. tags: [
  5. { name: 'Alpha'},
  6. { name: 'Beta'}
  7. ]
  8. }, {
  9. include: [ Tag ]
  10. })

然后,我们可以修改此示例以支持别名:

  1. const Categories = Product.hasMany(Tag, { as: 'categories' });
  2. Product.create({
  3. id: 1,
  4. title: 'Chair',
  5. categories: [
  6. { id: 1, name: 'Alpha' },
  7. { id: 2, name: 'Beta' }
  8. ]
  9. }, {
  10. include: [{
  11. association: Categories,
  12. as: 'categories'
  13. }]
  14. })