命名策略
underscored
選項
Sequelize 為模型提供 underscored
選項。當設為 true
時,此選項會將所有屬性的 field
選項設定為其名稱的 snake_case 版本。這也適用於由關聯自動產生的外鍵和其他自動產生的欄位。範例
const User = sequelize.define(
'user',
{ username: Sequelize.STRING },
{
underscored: true,
},
);
const Task = sequelize.define(
'task',
{ title: Sequelize.STRING },
{
underscored: true,
},
);
User.hasMany(Task);
Task.belongsTo(User);
上面我們有 User 和 Task 模型,兩者都使用 underscored
選項。我們還有它們之間的一對多關係。此外,回想一下,由於 timestamps
預設為 true,我們應該預期 createdAt
和 updatedAt
欄位也會被自動建立。
如果沒有 underscored
選項,Sequelize 會自動定義
- 每個模型的
createdAt
屬性,指向每個表格中名為createdAt
的欄位 - 每個模型的
updatedAt
屬性,指向每個表格中名為updatedAt
的欄位 Task
模型中的userId
屬性,指向 task 表格中名為userId
的欄位
啟用 underscored
選項後,Sequelize 將改為定義
- 每個模型的
createdAt
屬性,指向每個表格中名為created_at
的欄位 - 每個模型的
updatedAt
屬性,指向每個表格中名為updated_at
的欄位 Task
模型中的userId
屬性,指向 task 表格中名為user_id
的欄位
請注意,在這兩種情況下,欄位在 JavaScript 端仍然是 camelCase;此選項僅更改這些欄位如何對應到資料庫本身。每個屬性的 field
選項都設定為其 snake_case 版本,但屬性本身仍然是 camelCase。
這樣,在上述程式碼上呼叫 sync()
將產生以下結果
CREATE TABLE IF NOT EXISTS "users" (
"id" SERIAL,
"username" VARCHAR(255),
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "tasks" (
"id" SERIAL,
"title" VARCHAR(255),
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"user_id" INTEGER REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
PRIMARY KEY ("id")
);
單數 vs. 複數
乍看之下,可能會混淆在 Sequelize 中應該使用名稱的單數形式還是複數形式。本節旨在釐清這一點。
回想一下,Sequelize 在底層使用一個名為 inflection 的程式庫,以便正確計算不規則複數(例如 person -> people
)。但是,如果您使用其他語言,您可能需要直接定義名稱的單數和複數形式;sequelize 允許您使用一些選項來執行此操作。
定義模型時
模型應該使用單數字詞形式定義。範例
sequelize.define('foo', { name: DataTypes.STRING });
上面,模型名稱是 foo
(單數),而相應的表格名稱是 foos
,因為 Sequelize 會自動取得表格名稱的複數形式。
在模型中定義參考鍵時
sequelize.define('foo', {
name: DataTypes.STRING,
barId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'bars',
key: 'id',
},
onDelete: 'CASCADE',
},
});
在上面的範例中,我們手動定義一個參考另一個模型的鍵。這並不常見,但如果您必須這樣做,您應該在那裡使用表格名稱。這是因為參考是根據被參考的表格名稱建立的。在上面的範例中,使用了複數形式(bars
),假設 bar
模型是在預設設定下建立的(使其底層表格自動複數化)。
從預先載入中檢索資料時
當您在查詢中執行 include
時,包含的資料將會根據以下規則新增至傳回物件的額外欄位
- 當從單一關聯(
hasOne
或belongsTo
)包含某些內容時 - 欄位名稱將是模型名稱的單數版本; - 當從多重關聯(
hasMany
或belongsToMany
)包含某些內容時 - 欄位名稱將是模型的複數形式。
簡而言之,欄位名稱將在每種情況下採用最合乎邏輯的形式。
範例
// Assuming Foo.hasMany(Bar)
const foo = Foo.findOne({ include: Bar });
// foo.bars will be an array
// foo.bar will not exist since it doens't make sense
// Assuming Foo.hasOne(Bar)
const foo = Foo.findOne({ include: Bar });
// foo.bar will be an object (possibly null if there is no associated model)
// foo.bars will not exist since it doens't make sense
// And so on.
在定義別名時覆寫單數和複數
當為關聯定義別名時,您可以傳遞一個物件來指定單數和複數形式,而不是僅使用 { as: 'myAlias' }
Project.belongsToMany(User, {
as: {
singular: 'líder',
plural: 'líderes',
},
});
如果您知道模型在關聯中總是使用相同的別名,您可以直接向模型本身提供單數和複數形式
const User = sequelize.define(
'user',
{
/* ... */
},
{
name: {
singular: 'líder',
plural: 'líderes',
},
},
);
Project.belongsToMany(User);
新增到 user 實例的 mixin 將使用正確的形式。例如,Sequelize 將提供 project.getLíder()
,而不是 project.addUser()
。此外,Sequelize 將提供 project.setLíderes()
,而不是 project.setUsers()
。
注意:回想一下,使用 as
來更改關聯的名稱也會更改外鍵的名稱。因此,建議在這種情況下也直接指定所涉及的外鍵。
// Example of possible mistake
Invoice.belongsTo(Subscription, { as: 'TheSubscription' });
Subscription.hasMany(Invoice);
上面的第一個呼叫將在 Invoice
上建立一個名為 theSubscriptionId
的外鍵。但是,第二個呼叫也會在 Invoice
上建立一個外鍵(因為我們知道,hasMany
呼叫將外鍵放置在目標模型中) - 但是,它將被命名為 subscriptionId
。這樣您將同時擁有 subscriptionId
和 theSubscriptionId
欄位。
最好的方法是為外鍵選擇一個名稱,並將其明確地放置在兩個呼叫中。例如,如果選擇了 subscription_id
// Fixed example
Invoice.belongsTo(Subscription, {
as: 'TheSubscription',
foreignKey: 'subscription_id',
});
Subscription.hasMany(Invoice, { foreignKey: 'subscription_id' });