获取器、设置器和虚拟值
Sequelize 允许你为模型的属性定义自定义 getter 和 setter。
¥Sequelize allows you to define custom getters and setters for the attributes of your models.
Sequelize 还允许你指定所谓的虚拟属性,这些属性是 Sequelize 模型上的属性,实际上并不存在于底层 SQL 表中,而是由 Sequelize 自动填充。例如,它们对于创建自定义属性非常有用,这也可以简化你的代码。
¥Sequelize also allows you to specify the so-called virtual attributes, which are attributes on the Sequelize Model that doesn't really exist in the underlying SQL table, but instead are populated automatically by Sequelize. They are very useful to create custom attributes which also could simplify your code, for example.
获取器
¥Getters
getter 是为模型定义中的一列定义的 get()
函数:
¥A getter is a get()
function defined for one column in the model definition:
const User = sequelize.define('user', {
// Let's say we wanted to see every username in uppercase, even
// though they are not necessarily uppercase in the database itself
username: {
type: DataTypes.STRING,
get() {
const rawValue = this.getDataValue('username');
return rawValue ? rawValue.toUpperCase() : null;
},
},
});
这个 getter 就像标准的 JavaScript getter 一样,在读取字段值时自动调用:
¥This getter, just like a standard JavaScript getter, is called automatically when the field value is read:
const user = User.build({ username: 'SuperUser123' });
console.log(user.username); // 'SUPERUSER123'
console.log(user.getDataValue('username')); // 'SuperUser123'
需要注意的是,虽然上面记录了 SUPERUSER123
,但是真正存储在数据库中的值仍然是 SuperUser123
。我们使用 this.getDataValue('username')
来获取这个值,并将其转换为大写。
¥Note that, although SUPERUSER123
was logged above, the value truly stored in the database is still SuperUser123
. We used this.getDataValue('username')
to obtain this value, and converted it to uppercase.
如果我们尝试在 getter 中使用 this.username
,我们就会陷入无限循环!这就是 Sequelize 提供 getDataValue
方法的原因。
¥Had we tried to use this.username
in the getter instead, we would have gotten an infinite loop! This is why Sequelize provides the getDataValue
method.
设置器
¥Setters
setter 是为模型定义中的一列定义的 set()
函数。它接收正在设置的值:
¥A setter is a set()
function defined for one column in the model definition. It receives the value being set:
const User = sequelize.define('user', {
username: DataTypes.STRING,
password: {
type: DataTypes.STRING,
set(value) {
// Storing passwords in plaintext in the database is terrible.
// Hashing the value with an appropriate cryptographic hash function is better.
this.setDataValue('password', hash(value));
},
},
});
const user = User.build({
username: 'someone',
password: 'NotSo§tr0ngP4$SW0RD!',
});
console.log(user.password); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
console.log(user.getDataValue('password')); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
观察 Sequelize 在将数据发送到数据库之前自动调用 setter。数据库看到的唯一数据是已经散列的值。
¥Observe that Sequelize called the setter automatically, before even sending data to the database. The only data the database ever saw was the already hashed value.
如果我们想在计算中涉及模型实例中的另一个字段,这是可能的,而且非常简单!
¥If we wanted to involve another field from our model instance in the computation, that is possible and very easy!
const User = sequelize.define('user', {
username: DataTypes.STRING,
password: {
type: DataTypes.STRING,
set(value) {
// Storing passwords in plaintext in the database is terrible.
// Hashing the value with an appropriate cryptographic hash function is better.
// Using the username as a salt is better.
this.setDataValue('password', hash(this.username + value));
},
},
});
注意:上面涉及密码处理的示例虽然比简单地以明文形式存储密码要好得多,但远非完美的安全性。正确处理密码很困难,这里的所有内容只是为了展示 Sequelize 功能的示 例。我们建议聘请网络安全专家和/或阅读 OWASP 文件和/或访问 信息安全堆栈交换。
¥Note: The above examples involving password handling, although much better than simply storing the password in plaintext, are far from perfect security. Handling passwords properly is hard, everything here is just for the sake of an example to show Sequelize functionality. We suggest involving a cybersecurity expert and/or reading OWASP documents and/or visiting the InfoSec StackExchange.
组合获取器和设置器
¥Combining getters and setters
getter 和 setter 可以在同一字段中定义。
¥Getters and setters can be both defined in the same field.
举个例子,假设我们正在建模 Post
,其 content
是无限长度的文本。为了提高内存使用率,假设我们要存储内容的 gzip 压缩版本。
¥For the sake of an example, let's say we are modeling a Post
, whose content
is a text of unlimited length. To improve memory usage, let's say we want to store a gzipped version of the content.
注意:在这些情况下,现代数据库应该自动进行一些压缩。请注意,这只是为了举例。
¥Note: modern databases should do some compression automatically in these cases. Please note that this is just for the sake of an example.
const { gzipSync, gunzipSync } = require('zlib');
const Post = sequelize.define('post', {
content: {
type: DataTypes.TEXT,
get() {
const storedValue = this.getDataValue('content');
const gzippedBuffer = Buffer.from(storedValue, 'base64');
const unzippedBuffer = gunzipSync(gzippedBuffer);
return unzippedBuffer.toString();
},
set(value) {
const gzippedBuffer = gzipSync(value);
this.setDataValue('content', gzippedBuffer.toString('base64'));
},
},
});