命名策略
underscored
选项
Sequelize 为模型提供 underscored
选项。当设置为 true
时,此选项将设置所有属性的 field
选项为其名称的 蛇形命名法 版本。这也适用于关联自动生成的外部键和其他自动生成的字段。示例
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
属性,指向任务表中名为userId
的列
启用 underscored
选项后,Sequelize 将改为定义
- 每个模型的
createdAt
属性,指向每个表中名为created_at
的列 - 每个模型的
updatedAt
属性,指向每个表中名为updated_at
的列 Task
模型中的userId
属性,指向任务表中名为user_id
的列
请注意,在这两种情况下,字段在 JavaScript 端仍然是 驼峰命名法;此选项仅更改这些字段如何映射到数据库本身。每个属性的 field
选项都设置为其蛇形命名法版本,但属性本身仍然是驼峰命名法。
这样,在上述代码上调用 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")
);
单数与复数
乍一看,在 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);
添加到用户实例中的 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' });