1. 程式人生 > >Sequelize-nodejs-7-Associations

Sequelize-nodejs-7-Associations

Associations關聯性

This section describes the various association types in sequelize. When calling a method such as User.hasOne(Project), we say that the User model (the model that the function is being invoked on) is the source and the Project model (the model being passed as an argument) is the target

.

這個部分描述了sequelize中的多種關聯型別。當呼叫例如User.hasOne(Project)這個方法時,我們說模型(函式正呼叫的)是來源,Project模型(作為引數傳遞的模型)是目標

 

 

One-To-One associations

One-To-One associations are associations between exactly two models connected by a single foreign key.

一對一關聯是兩個通過一個外來鍵連線的模型之間的關聯性

 

BelongsTo

BelongsTo associations are associations where the foreign key for the one-to-one relation exists on the source model

.

BelongsTo關聯是存在於來源模型一對一關係中的外來鍵的關聯性

A simple example would be a Player being part of a Team with the foreign key on the player.

一個簡單的例子,Player是帶有player外來鍵的Team的一部分

const Player = this.sequelize.define('player', {/* attributes */});
const Team  = this.sequelize.define('team', {/*
attributes */}); Player.belongsTo(Team); // Will add a teamId attribute to Player to hold the primary key value for Team
Player中將會新增一個teamId屬性(這就是外來鍵),儲存Team的主鍵值
 

Foreign keys(寫在來源模型中)

By default the foreign key for a belongsTo relation will be generated from the target model name and the target primary key name.

預設belongsTo關係的外來鍵將被目標模型生成並以目標主鍵命名

The default casing is camelCase however if the source model is configured with underscored: true the foreignKey will be snake_case.

命名格式預設為camelCase拼寫法(即類似myForeignKeys形式)。如果來源模型配置為underscored: true,那麼外來鍵將使用snake_case拼寫法(即類似my_foreign_keys

const User = this.sequelize.define('user', {/* attributes */})
const Company  = this.sequelize.define('company', {/* attributes */});

User.belongsTo(Company); // Will add companyId to user,外來鍵名命名為companyIdcamelCase

const User = this.sequelize.define('user', {/* attributes */}, {underscored: true})
const Company  = this.sequelize.define('company', {
  uuid: {
    type: Sequelize.UUID,
    primaryKey: true
  }
});

User.belongsTo(Company); // Will add company_uuid to user,外來鍵名命名為company_uuidsnake_case

In cases where as has been defined it will be used in place of the target model name.

如果使用了as,將使用其替代目標模型的名字來命名外來鍵

const User = this.sequelize.define('user', {/* attributes */})
const UserRole  = this.sequelize.define('userRole', {/* attributes */});

User.belongsTo(UserRole, {as: 'role'}); // Adds roleId to user rather than userRoleId

In all cases the default foreign key can be overwritten with the foreignKey option. When the foreign key option is used, Sequelize will use it as-is:

在所有情況下,預設外來鍵可以被foreignKey選項值複寫。當foreignKey選項值被使用時,Sequelize將使用其作為外來鍵名

const User = this.sequelize.define('user', {/* attributes */})
const Company  = this.sequelize.define('company', {/* attributes */});

User.belongsTo(Company, {foreignKey: 'fk_company'}); // Adds fk_company to User

 

Target keys

The target key is the column on the target model that the foreign key column on the source model points to. By default the target key for a belongsTo relation will be the target model's primary key. To define a custom column, use the targetKey option.

目標鍵是目標模型中來源模型指向的外來鍵列對應的列。預設目標鍵的belongsTo關係將是目標模型的主鍵。為了定義自定義列,使用的是targetKey選項

const User = this.sequelize.define('user', {/* attributes */})
const Company  = this.sequelize.define('company', {/* attributes */});

User.belongsTo(Company, {foreignKey: 'fk_companyname', targetKey: 'name'}); // Adds fk_companyname to User

說明來源模型User的外來鍵名字是fk_companyname,它指向的是目標模型Companyname

 

HasOne

HasOne associations are associations where the foreign key for the one-to-one relation exists on the target model.

HasOne關聯性是存在在目標模型中一對一關係的外來鍵的關聯性

const User = sequelize.define('user', {/* ... */})
const Project = sequelize.define('project', {/* ... */})

// One-way associations
Project.hasOne(User)

/*
  在這個例子中,hasOne將會新增屬性projectId到User模型中
  而且Project.prototype將會得到getUser和setUser方法,並根據傳遞的第一個引數去定義。
  如果下劃線型別可用,新增的屬性名將使用project_id替代projectId

  外來鍵將被放在users表中

  你也可以定義外來鍵名,比如,如果你已經有一個存在的資料庫並想要執行它:
*/

Project.hasOne(User, { foreignKey: 'initiator_id' })//這就是自己定義了外來鍵名

/*
  for因為Sequelize將使用模型名(定義的第一個變數)給訪問器方法,還是有傳遞一個特殊的選項給hasOne的可能的:
*/

Project.hasOne(User, { as: 'Initiator' })
//然後你就可以呼叫Project.getInitiator和Project.setInitiator方法來獲得外來鍵和改變外來鍵

// 或者是定義一些自我引用
const Person = sequelize.define('person', { /* ... */})

Person.hasOne(Person, {as: 'Father'})
// 將會新增FatherId屬性給Person

// also possible:
Person.hasOne(Person, {as: 'Father', foreignKey: 'DadId'})
//將會新增DadId屬性給Person

//因為設定as,所以還能夠呼叫一下的兩個方法去得到外來鍵和改變外來鍵
Person.setFather
Person.getFather

// 如果你需要連線一個表兩次,你可以連線兩次同一個表
Team.hasOne(Game, {as: 'HomeTeam', foreignKey : 'homeTeamId'});
Team.hasOne(Game, {as: 'AwayTeam', foreignKey : 'awayTeamId'});

Game.belongsTo(Team);

Even though it is called a HasOne association, for most 1:1 relations you usually want the BelongsTo association since BelongsTo will add the foreignKey on the source where hasOne will add on the target.

即使被稱作HasOne關聯性,對於更多的一對一關係你通常想要的是BelongsTo關聯性。兩者不同在於,BelongsTo要新增外來鍵到來源,hasOne新增外來鍵到目標。(因為兩者的來源和目標是相反的)

 

Difference between HasOne and BelongsTo

In Sequelize 1:1 relationship can be set using HasOne and BelongsTo. They are suitable for different scenarios. Lets study this difference using an example.

Sequelize中,一對一關係可以使用HasOne和BelongsTo來進行設定。他們對於不同的方案都是合適的。通過使用例子來學習他們之間的不同:

Suppose we have two tables to link Player and Team. Lets define their models.

假設你有兩個表PlayerTeam,先定義他們的模型:

const Player = this.sequelize.define('player', {/* attributes */})
const Team  = this.sequelize.define('team', {/* attributes */});

When we link two models in Sequelize we can refer them as pairs of source and target models. Like this

在Sequelize中,當我們連線兩個模型時,我們可以將其參考為來源和目標模型,就像下面:

Having Player as the source and Team as the target

Player是來源模型,Team是目標模型

Player.belongsTo(Team);
//Or
Player.hasOne(Team);

Having Team as the source and Player as the target

或者Team是來源模型,Player是目標模型

Team.belongsTo(Player);
//Or
Team.hasOne(Player);

HasOne and BelongsTo insert the association key in different models from each other. HasOne inserts the association key in target model whereas BelongsTo inserts the association key in the source model.

HasOne和BelongsTo將關聯鍵插入與對方不同的模型。HasOne將關聯鍵插入目標模型,然而BelongsTo插入來源模型

Here is an example demonstrating use cases of BelongsTo and HasOne.舉例說明

const Player = this.sequelize.define('player', {/* attributes */})
const Coach  = this.sequelize.define('coach', {/* attributes */})
const Team  = this.sequelize.define('team', {/* attributes */});

Suppose our Player model has information about its team as teamId column. Information about each Team's Coach is stored in the Team model as coachId column. These both scenarios requires different kind of 1:1 relation because foreign key relation is present on different models each time.

假設Player模型有著關於他的team的訊息,即teamId列。關於每一個Team的Coach的訊息儲存在Team模型中,即coachId。這些場景需要不同型別的一對一關係,因為每一次外來鍵關係會出現在不同的模型中。

When information about association is present in source model we can use belongsTo. In this case Player is suitable for belongsTo because it has teamId column.

當關於關聯性的訊息出現在來源模型時,我們能使用belongsTo。在這種情況下,Player很適合belongsTo,因為它有著teamId列:

Player.belongsTo(Team)  // `teamId` will be added on Player / Source model

When information about association is present in target model we can use hasOne. In this case Coach is suitable for hasOne because Team model store information about its Coach as coachId field.

當關於關聯性的訊息出現在目標模型時,我們能使用hasOne。在這種情況下,Coach很適合hasOne,因為Team儲存關於Coach的資訊在coachId列中

Coach.hasOne(Team)  // `coachId` will be added on Team / Target model

 

 

 

One-To-Many associations (hasMany)

One-To-Many associations are connecting one source with multiple targets. The targets however are again connected to exactly one specific source.

一對多關聯性將連線一個來源與多個目標模型。可是目標只與特定的一個來源連線

const User = sequelize.define('user', {/* ... */})
const Project = sequelize.define('project', {/* ... */})

// OK. Now things get more complicated (not really visible to the user :)).
// First let's define a hasMany association
Project.hasMany(User, {as: 'Workers'})

This will add the attribute projectId or project_id to User. Instances of Project will get the accessors getWorkers and setWorkers. 

這將會新增屬性projectIdproject_idUser中。Project例項將會得到訪問器getWorkerssetWorkers(因為設定as)

Sometimes you may need to associate records on different columns, you may use sourceKey option:

有時你可能需要在不同的列中關聯記錄,那你需要sourceKey選項:

const City = sequelize.define('city', { countryCode: Sequelize.STRING });
const Country = sequelize.define('country', { isoCode: Sequelize.STRING });

// Here we can connect countries and cities base on country code
Country.hasMany(City, {foreignKey: 'countryCode', sourceKey: 'isoCode'});
City.belongsTo(Country, {foreignKey: 'countryCode', targetKey: 'isoCode'});
sourceKey即說明目標模型City的外來鍵對應的是來源模型Country的isoCode

So far we dealt with a one-way association. But we want more! Let's define it the other way around by creating a many to many association in the next section.

到目前為止,我們解決了一對的關聯關係。但我們需要更多。在下面將通過建立多對多的關聯關係來以另一種方式定義它。

 

 

 

Belongs-To-Many associations

Belongs-To-Many associations are used to connect sources with multiple targets. Furthermore the targets can also have connections to multiple sources.

所屬對多關係用於連線來源和多個目標模型。與一對多不同在與目標能夠連線多個來源

Project.belongsToMany(User, {through: 'UserProject'});
User.belongsToMany(Project, {through: 'UserProject'});

This will create a new model called UserProject with the equivalent foreign keys projectId and userId. Whether the attributes are camelcase or not depends on the two models joined by the table (in this case User and Project).

這將會建立一個新的同等帶有外來鍵projectIduserId的UserProject模型。這個屬性是否是camelcase拼寫法將取決於通過UserProject這個表連線的兩個模型

Defining through is required. Sequelize would previously attempt to autogenerate names but that would not always lead to the most logical setups.

通過required進行定義。Sequelize以前企圖自動生成名字,但這樣不能保證總能導致最多的邏輯設定

This will add methods getUsers, setUsers, addUser,addUsers to Project, and getProjects, setProjects, addProject, and addProjects to User.

這將會新增方法getUsers, setUsers, addUser,addUsers給Project,並新增getProjects, setProjects, addProject, and addProjects方法給User

Sometimes you may want to rename your models when using them in associations. Let's define users as workers and projects as tasks by using the alias (as) option. We will also manually define the foreign keys to use:

有時當你在關聯時使用時,你可能想要重新命名你的模型。讓我們通過使用別名 (as)選項去將users定義成workers,projects定義成tasks。我們將相互定義外來鍵來使用:

User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId' })
Project.belongsToMany(User, { as: 'Workers', through: 'worker_tasks', foreignKey: 'projectId' })

foreignKey will allow you to set source model key in the through relation. otherKey will allow you to set target model key in the through relation.

foreignKey將允許你設定來源模型鍵到through關係中。otherKey是用於設定目標模型鍵到through關係中的

User.belongsToMany(Project, { as: 'Tasks', through: 'worker_tasks', foreignKey: 'userId', otherKey: 'projectId'})

Of course you can also define self references with belongsToMany:

當然,你也可以設定自我引用:

Person.belongsToMany(Person, { as: 'Children', through: 'PersonChildren' })
// This will create the table PersonChildren which stores the ids of the objects.

If you want additional attributes in your join table, you can define a model for the join table in sequelize, before you define the association, and then tell sequelize that it should use that model for joining, instead of creating a new one:

如果你想要在你的連線表中新增額外屬性的話,你可以在sequelize中為你的連線表定義模型。在你定義關聯之前,告訴sequelize它需要使用這個模型進行連線而不是建立一個新的連線表:

const User = sequelize.define('user', {})
const Project = sequelize.define('project', {})
const UserProjects = sequelize.define('userProjects', {
    status: DataTypes.STRING
})

User.belongsToMany(Project, { through: UserProjects })
Project.belongsToMany(User, { through: UserProjects })

有上面的例子可知,定義了連線表userProjects,並在建立連線belongsToMany時宣告其為through

To add a new project to a user and set its status, you pass extra options.through to the setter, which contains the attributes for the join table

為了新增新的project到user並設定其狀態,你可以傳遞options.through給設定者,裡面包含了連線表的屬性

user.addProject(project, { through: { status: 'started' }})

By default the code above will add projectId and userId to the UserProjects table, and remove any previously defined primary key attribute - the table will be uniquely identified by the combination of the keys of the two tables, and there is no reason to have other PK columns. To enforce a primary key on the UserProjects model you can add it manually.

預設上面的程式碼將會新增projectId 和userId到UserProjects表中,並移除任何以前定義的主鍵屬性-表將通過兩個表的連線唯一標明,這裡沒有理由還有別的PK列。為了加強UserProjects模型中的主鍵,你可以手動新增它:

const UserProjects = sequelize.define('userProjects', {
  id: {
    type: Sequelize.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  status: DataTypes.STRING
})

With Belongs-To-Many you can query based on through relation and select specific attributes. For example using findAll with through

在Belongs-To-Many中你可以基於through關係來查詢和選擇具體的屬性。比如使用帶著throug的findAll:

User.findAll({
  include: [{
    model: Project,
    through: {
      attributes: ['createdAt', 'startedAt', 'finishedAt'],
      where: {completed: true}
    }
  }]
});

Belongs-To-Many creates a unique key when primary key is not present on through model. This unique key name can be overridden using uniqueKey option.

在through模型中,當主鍵不存在時,Belongs-To-Many將建立了一個唯一的鍵。使用uniqueKey選項可以複寫這個唯一鍵名

Project.belongsToMany(User, { through: UserProjects, uniqueKey: 'my_custom_unique' })

 

 

 

Scopes作用域

This section concerns association scopes. For a definition of association scopes vs. scopes on associated models, see Scopes.

這部分是關於關聯作用域

Association scopes allow you to place a scope (a set of default attributes for get and create) on the association. Scopes can be placed both on the associated model (the target of the association), and on the through table for n:m relations.

關聯作用域允許你在關聯行中定義作用域(對於getcreate的預設屬性集)。作用域能夠在關聯模型(關聯的目標)和n:m關係的through表中定義

1:m

Assume we have tables Comment, Post, and Image. A comment can be associated to either an image or a post via commentable_id and commentable - we say that Post and Image are Commentable

假設我們有表Comment、Post和Image。comment能夠通過commentable_idcommentable
ImagePost相關聯- Post和 Image是Commentable

const Comment = this.sequelize.define('comment', {
  title: Sequelize.STRING,
  commentable: Sequelize.STRING,
  commentable_id: Sequelize.INTEGER
});

Comment.prototype.getItem = function(options) {
  return this['get' + this.get('commentable').substr(0, 1).toUpperCase() + this.get('commentable').substr(1)](options);
};

Post.hasMany(this.Comment, {
  foreignKey: 'commentable_id',
  constraints: false,
  scope: {
    commentable: 'post'
  }
});
Comment.belongsTo(this.Post, {
  foreignKey: 'commentable_id',
  constraints: false,
  as: 'post'
});

Image.hasMany(this.Comment, {
  foreignKey: 'commentable_id',
  constraints: false,
  scope: {
    commentable: 'image'
  }
});
Comment.belongsTo(this.Image, {
  foreignKey: 'commentable_id',
  constraints: false,
  as: 'image'
});

constraints: false, disables references constraints - since the commentable_id column references several tables, we cannot add a REFERENCES constraint to it. Note that the Image -> Comment and Post -> Comment relations define a scope, commentable: 'image' and commentable: 'post' respectively. This scope is automatically applied when using the association functions:

constraints: false阻止引用限制-當commentable_id列引用多個表,我們將不能夠新增REFERENCES限制給它。注意, Image -> Comment 和 Post -> Comment的關係定義了作用域,分別為commentable: 'image' commentable: 'post'

image.getComments()
SELECT * FROM comments WHERE commentable_id = 42 AND commentable = 'image';

image.createComment({
  title: 'Awesome!'
})
INSERT INTO comments (title, commentable_id, commentable) VALUES ('Awesome!', 42, 'image');

image.addComment(comment);
UPDATE comments SET commentable_id = 42, commentable = 'image'

The getItem utility function on Comment completes the picture - it simply converts the commentable string into a call to either getImage or getPost, providing an abstraction over whether a comment belongs to a post or an image. You can pass a normal options object as a parameter to getItem(options) to specify any where conditions or includes.

Comment中的getItem效用函式完成了圖片-只要抽象完成,無論comment屬於post還是image,它都簡單地將commentable字串轉成getImage或getPost的呼叫。

你可以傳遞一個正常選項物件作為變數給