在 Sails 中,模型定义的顶级属性称为**模型设置**。这包括从属性定义到模型将使用的数据库设置,以及其他一些选项。
此页面的大部分内容都致力于完整介绍 Sails 支持的模型设置。但在我们开始之前,让我们先看看如何在 Sails 应用中实际应用这些设置。
模型设置允许您自定义 Sails 应用中模型的行为。可以通过在模型定义中设置顶级属性来基于每个模型指定模型设置,或者在sails.config.models
中作为应用范围的默认值指定。
要修改应用中所有模型共享的默认模型设置,请编辑config/models.js
。
例如,当您生成一个新应用时,Sails 会自动在您的 config/models.js
文件中包含三个不同的默认属性:id
、createdAt
和 updatedAt
。假设对于所有模型,您都希望使用稍微不同的自定义 id
属性。为此,您只需在 config/models.js
定义中覆盖 attributes: { id: {...} }
即可。
要进一步自定义特定模型的这些设置,您可以在该模型的定义文件中(例如 api/models/User.js
)将其指定为顶级属性。这将覆盖具有相同名称的默认模型设置。
例如,如果您将 fetchRecordsOnUpdate: true
添加到其中一个模型定义(api/models/UploadedFile.js
)中,则该模型现在将返回已更新的记录。但是,您的其他模型将不受影响:它们将继续使用默认设置(除非您已更改它,否则为 fetchRecordsOnUpdate: false
)。
在日常开发中,您最常交互的模型设置是 attributes
。属性几乎在每个模型定义中都使用,并且 config/models.js
中包含一些默认属性。为了便于将来参考,这里有一些额外的提示
tableName
,则应始终基于每个模型进行指定。(应用范围的表名毫无意义!)datastore
——例如,如果您的默认数据存储是 PostgreSQL,但您有一个 CachedBloodworkReport
模型希望存储在 Redis 中。migrate
和 schema
设置指定为应用范围的默认值,切勿基于每个模型指定。现在您已经了解了模型设置是什么以及如何配置它们,让我们一起浏览并查看每个设置。
模型的属性定义集。
attributes: { /* ... */ }
类型 | 示例 | 默认值 |
---|---|---|
见下文。 | {} |
大多数情况下,您将在各个模型定义(在 api/models/
中)中定义属性,但您也可以在 config/models.js
中指定**默认属性**。这允许您在一个地方定义一组全局属性,然后依靠 Sails 隐式地并将它们提供给所有模型,而无需重复自身。默认属性也可以通过在相关模型定义中定义具有相同名称的替换属性来基于每个模型覆盖。
attributes: {
id: { type: 'number', autoIncrement: true },
createdAt: { type: 'number', autoCreatedAt: true },
updatedAt: { type: 'number', autoUpdatedAt: true },
}
有关模型属性的完整介绍,包括如何在 Sails 应用中定义和使用它们,请参见概念 > ORM > 属性。
一个函数,允许您自定义模型记录序列化为 JSON 的方式。
customToJSON: function() { /*...*/ }
类型 | 示例 | 默认值 |
---|---|---|
见下文。 | n/a |
将 customToJSON
设置添加到模型会更改模型记录的字符串化方式。换句话说,它允许您注入自定义逻辑,该逻辑在将这些记录中的任何一个传递给 JSON.stringify()
时运行。这最常用于实现故障保护,确保敏感数据(如用户密码)不会意外包含在响应中(因为res.send()
和 actions2 可能会在发送前将数据字符串化)。
customToJSON
函数不带任何参数,但可以通过 this
变量访问记录。这允许您省略敏感数据并返回已清理的结果,这是 JSON.stringify()
在生成 JSON 字符串时实际使用的结果。例如
customToJSON: function() {
// Return a shallow copy of this record with the password and ssn removed.
return _.omit(this, ['password', 'ssn'])
}
customToJSON 函数的设计不支持异步功能。这使得核心中的同步部分保持同步,并为整个系统提供更好的稳定性。
请注意,
customToJSON
中可用的this
变量是对实际记录对象的直接引用,因此请注意不要修改它。换句话说,避免编写类似delete this.password
的代码。相反,使用_.omit()
或_.pick()
等方法获取记录的副本。或者只构造一个新字典并返回它(例如return { foo: this.foo }
)。
模型将存储和检索其记录作为行(/MongoDB 文档)的 SQL 表(/MongoDB 集合)的名称。
tableName: 'some_preexisting_table'
类型 | 示例 | 默认值 |
---|---|---|
'some_preexisting_table' |
与模型的标识相同。 |
tableName 设置使您能够自定义特定模型应使用的底层物理模型的名称。换句话说,它允许您控制模型在数据库中存储和检索记录的位置,而不会影响控制器操作/辅助函数中的代码。
默认情况下,Sails 使用模型的标识来确定其表名
await User.find();
// => SELECT * FROM user;
这是一个推荐的约定,在大多数情况下不需要更改。但是,如果您与使用其他平台(如 Python 或 C#)编写的现有应用程序共享遗留数据库,或者您的团队更喜欢数据库表的不同命名约定,那么自定义此映射可能很有用。
回到上面的示例,如果您修改了 api/models/User.js
中的模型定义,并将 tableName
设置为 'foo_bar'
,那么您将看到略微不同的结果
await User.find();
// => SELECT * FROM foo_bar;
tableName
中有什么?在 MySQL 和 PostgreSQL 等数据库中,该设置指的是文字“表”。在 MongoDB 中,它指的是“集合”。这实际上只是关于熟悉程度:我们称之为“表”的东西,无论用什么其他词语都可以查询。
Sails 每次加载应用时将运行的**自动迁移策略**。
migrate: 'alter'
类型 | 示例 | 默认值 |
---|---|---|
'alter' |
系统将提示您。 注意:在生产环境中,这始终为 'safe' 。 |
migrate
设置控制应用的自动迁移策略。简而言之,这告诉 Sails 您是否希望它尝试自动重建数据库中的表/集合/集等。
在开发应用的过程中,您几乎总是需要对数据库结构进行至少一两个**重大更改**。究竟什么构成“重大更改”取决于您使用的数据库:例如,假设您向其中一个模型定义添加了一个新属性。如果该模型配置为使用 MongoDB,那么这没什么大不了的;您可以继续开发,就好像什么也没发生一样。但是,如果该模型配置为使用 MySQL,则需要执行一个额外的步骤:必须向相应的表中添加一个列(否则,.create()
等模型方法将停止工作)。因此,对于使用 MySQL 的模型,添加属性是对数据库模式的重大更改。
即使所有模型都使用 MongoDB,仍然有一些需要注意的重大模式更改。例如,如果您向其中一个属性添加了
unique: true
,则必须在 MongoDB 中创建一个唯一索引。
在 Sails 中,在进行数据库迁移时,有两种不同的操作模式
sails run
手动执行所有数据库迁移。每当您需要对生产数据库应用重大更改时,都应使用手动数据库迁移。否则,当您在笔记本电脑上进行开发或运行自动化测试时,自动迁移可以为您节省大量时间。
当你在开发环境中启动 Sails 应用(例如,在一个全新的 Sails 应用中运行 sails lift
)时,配置的自动迁移策略将会执行。如果你使用的是 migrate: 'safe'
,那么不会发生任何额外的事情,但是如果你使用的是 drop
或 alter
,Sails 会将开发数据库中的所有记录加载到内存中,然后删除并重新创建数据的物理层表示(即表/集合/集等)。这允许你在模型定义中做出的任何破坏性更改(例如,删除唯一性约束)自动应用到你的开发数据库。最后,如果你使用的是 alter
,Sails 随后会尝试使用之前保存的记录重新填充新生成的表/集合/集。
自动迁移策略 | 描述 |
---|---|
safe |
永远不要自动迁移我的数据库。我会手动执行。 |
alter |
自动迁移列/字段,但尝试保留我现有的数据(实验性) |
drop |
每次我启动 Sails 时都清除/删除所有数据并重建模型 |
请记住,当使用
alter
或drop
策略时,自上次启动应用以来对数据库进行的任何手动更改都可能会丢失。这包括自定义索引、外键约束、列顺序和注释等。通常,由自动迁移创建的表无法保证与物理数据库列的任何细节一致,除了设置列名、类型(包括指定的字符集/编码)和唯一性之外。
Sails 中的 drop
和 alter
自动迁移策略作为开发期间和运行自动化测试时的便利功能而存在。**它们并非设计用于处理你关心的数据。**请务必不要在生产数据集上使用 drop
或 alter
。实际上,作为一项帮助你避免无意中执行此操作的保险措施,无论你配置了什么,只要你在 生产环境中启动应用,Sails 始终使用 migrate: 'safe'
。
在许多情况下,托管提供商在检测到 Node.js 应用时会自动将 NODE_ENV
环境变量设置为“production”。即便如此,也请不要只依赖于此保险措施,并采取通常的预防措施来确保用户数据的安全。任何时候将 Sails(或任何其他工具或框架)连接到具有预先存在的生产数据的数据库时,**请进行试运行**,尤其是在第一次连接时。生产数据是敏感的、有价值的,并且在许多情况下是不可替代的。客户、用户及其律师不会对数据被清空感到满意。
最佳实践是,确保永远不要使用生产数据库凭据启动或 部署 应用,除非你 100% 确定正在生产环境中运行。在组织范围内解决此问题的常用方法是,根本**不要**将生产数据库凭据推送到源代码存储库,而是依靠 环境变量 来存储所有敏感凭据。(如果你的应用受监管要求约束,或者大量人员可以访问你的代码库,那么这尤其是一个好主意。)
如果你正在处理相对大量的开发/测试数据,则 alter
自动迁移策略可能需要很长时间才能在启动时完成。如果你注意到类似 npm test
、sails console
或 sails lift
的命令似乎挂起,请考虑减小开发数据集的大小。(请记住:Sails 自动迁移应该只在你的本地笔记本电脑/台式机上使用,并且只用于小型开发数据集。)
模型是否期望记录符合特定的一组属性。
schema: true
类型 | 示例 | 默认值 |
---|---|---|
true |
取决于适配器。 |
schema
设置允许你在“无模式”或“有模式”模式之间切换模型。更具体地说,它控制着 .create()
和 .update()
等方法的行为。通常情况下,只要你使用的适配器支持,你就可以在记录中存储任意数据。但是,如果你启用了 schema:true
,则实际上只会存储与模型的 attributes
对应的属性。
此设置仅与使用无模式数据库(如 MongoDB)的模型相关。当连接到关系数据库(如 MySQL 或 PostgreSQL)时,模型始终有效地为
schema:true
,因为底层数据库只能在预先设置的表和列中存储数据。
模型将用于查找记录、创建记录等 数据存储配置 的名称。
datastore: 'legacyECommerceDb'
类型 | 示例 | 默认值 |
---|---|---|
'legacyECommerceDb' |
'default' |
这允许你指示此模型将在其中获取和保存数据的数据库。除非另有指定,否则应用中的每个模型都使用名为“default”的内置数据存储,它包含在每个新 Sails 应用中。这使得配置应用的主数据库变得很容易,同时仍然允许你覆盖任何特定模型的 datastore
设置。
有关配置应用的数据存储的更多信息,请参阅 参考 > 配置 > 数据存储。
解密数据时要使用的一组密钥。除非另有配置,否则始终使用 default
数据加密密钥(或“DEK”)进行加密。
dataEncryptionKeys: {
default: 'tVdQbq2JptoPp4oXGT94kKqF72iV0VKY/cnp7SjL7Ik='
}
除非你的用例需要密钥轮换,否则
default
密钥是唯一需要的。除了default
之外的任何其他数据加密密钥都只是为了允许解密使用它们加密的旧数据。
要停用数据加密密钥,你需要为其提供一个新的密钥 ID(例如 2028
),然后创建一个新的 default
密钥供任何新的加密使用。例如,如果你在 2028 年发布了一个 Sails 应用并且你的密钥每年轮换一次,那么下一年你的 dataEncryptionKeys
可能如下所示
dataEncryptionKeys: {
default: 'DZ7MslaooGub3pS/0O734yeyPTAeZtd0Lrgeswwlt0s=',
'2028': 'C5QAkA46HD9pK0m7293V2CzEVlJeSUXgwmxBAQVj+xU='
}
在次年(2030 年 1 月)更换默认密钥后,你可能会有
dataEncryptionKeys: {
default: 'tVdQbq2JptoPp4oXGT94kKqF72iV0VKY/cnp7SjL7Ik=',
'2029': 'DZ7MslaooGub3pS/0O734yeyPTAeZtd0Lrgeswwlt0s=',
'2028': 'C5QAkA46HD9pK0m7293V2CzEVlJeSUXgwmxBAQVj+xU='
}
是否始终像你在使用此模型调用 .destroy()
时设置了 cascade: true
一样。
cascadeOnDestroy: true
类型 | 示例 | 默认值 |
---|---|---|
true |
false |
默认情况下出于性能原因禁用此功能。你可以使用此模型设置启用它,或者使用 .meta({cascade: true})
在每个查询的基础上启用它。
此功能仅供
sails-mongo
适配器 使用。
如果设置为 true
,则模型不会使用自动生成的 MongoDB ObjectID 对象作为其主键。这允许你使用 sails-mongo
适配器创建主键为任意字符串或数字的模型,而不仅仅是长长的 UUID 格式的东西。请注意,将其设置为 true
意味着你必须在每次调用 .create()
或 .createEach()
时提供 id
的值。
类型 | 示例 | 默认值 |
---|---|---|
true |
false |
默认情况下出于性能原因禁用此功能。你可以使用此模型设置启用它,或者使用 .meta({dontUseObjectIds: true})
在每个查询的基础上启用它。
以下低级设置出于完整性考虑而包含在内,但在实践中,很少(如果有的话)需要更改它们。
模型主键属性的名称。
你永远不需要更改此设置。相反,如果你需要使用自定义主键,请在“id”属性上设置自定义
columnName
。
primaryKey: 'id'
类型 | 示例 | 默认值 |
---|---|---|
'id' |
'id' |
按照惯例,这是“id”,一个默认属性,在 Sails v1.0 及更高版本生成的新的应用的 config/models.js
文件中自动为你包含。更改模型主键的最佳方法是简单地自定义该默认属性的 columnName
。
例如,假设你有一个需要与预先存在的 MySQL 数据库中的表集成的 User 模型。该表可能有一个名为“id”以外的其他名称(如“email_address”)的列作为其主键。要使你的模型尊重该主键,你需要在模型定义中指定 id
属性的覆盖;如下所示
id: {
type: 'string',
columnName: 'email_address',
required: true
}
然后,在你的应用代码中,你将能够按主键查找用户,同时自动为你处理所有生成的 SQL 查询中到 email_address
的映射
await User.find({ id: req.param('emailAddress' });
撇开所有注意事项不谈,假设你是 MongoDB 的狂热用户。在你的新 Sails 应用中,你将首先在
config/models.js
中的默认“id”属性上设置columnName: '_id'
。然后你可以像往常一样使用 Sails 和 Waterline,一切都会正常工作。但是,如果你希望为了熟悉起见而更改“id”属性本身的实际名称会怎样?这样,当你调用代码中的内置模型方法时,你将使用类似
.destroy({ _id: 'ba8319abd-13810-ab31815' })
的语法,而不是通常的“id”。这就是此模型设置可能变得有用的地方。你只需编辑
config/models.js
,使其包含primaryKey: '_id'
,然后将默认的“id”属性重命名为“_id”。但是有一些 重新考虑 此操作路线的好理由。
模型的小写唯一标识符。
模型的
identity
是只读的。它是自动派生的,永远不应该手动设置。
Something.identity;
类型 | 示例 |
---|---|
'purchase' |
在 Sails 中,模型的 identity
是通过将文件名小写并剥离文件扩展名自动推断出来的。例如,api/models/Purchase.js
的标识符将是 purchase
。它可以通过 sails.models.purchase
访问,如果启用了蓝图路由,则可以通过 GET /purchase
和 PATCH /purchase/1
等请求访问它。
assert(Purchase.identity === 'purchase');
assert(sails.models.purchase.identity === 'purchase');
assert(Purchase === sails.models.purchase);
模型的唯一全局标识符,它也决定了其对应的全局变量的名称(如果相关)。
模型的
globalId
是只读的。它是自动派生的,永远不应该手动设置。
Something.globalId;
类型 | 示例 |
---|---|
'Purchase'
|
模型的 globalId
的主要目的是确定 Sails 自动为其公开的全局变量的名称,除非已禁用模型的全局化。在 Sails 中,模型的 globalId
是根据其文件名自动推断的。例如,api/models/Purchase.js
的 globalId
将是 Purchase
。
assert(Purchase.globalId === 'Purchase');
assert(sails.models.purchase.globalId === 'Purchase');
if (sails.config.globals.models) {
assert(sails.models.purchase === Purchase);
}
else {
assert(typeof Purchase === 'undefined');
}