sequelize 获取器, 设置器 与 虚拟字段——迹忆客-ag捕鱼王app官网
sequelize 允许你为模型的属性定义自定义获取器和设置器。
sequelize 还允许你指定所谓的 虚拟属性,它们是 sequelize 模型上的属性,这些属性在基础 sql 表中实际上并不存在,而是由 sequelize 自动填充。 它们对于创建自定义属性非常有用, 这也可以简化您的代码。
获取器
获取器是为模型定义中的一列定义的 get() 函数:
const user = sequelize.define('user', {
// 假设我们想要以大写形式查看每个用户名,
// 即使它们在数据库本身中不一定是大写的
username: {
type: datatypes.string,
get() {
const rawvalue = this.getdatavalue('username');
return rawvalue ? rawvalue.touppercase() : null;
}
}
});
就像标准 javascript 获取器一样,在读取字段值时会自动调用此获取器:
const user = user.build({ username: 'superuser123' });
console.log(user.username); // 'superuser123'
console.log(user.getdatavalue('username')); // 'superuser123'
注意,尽管上面记录为 superuser123,但是真正存储在数据库中的值仍然是 superuser123。 我们使用了 this.getdatavalue('username')
来获得该值,并将其转换为大写。
如果我们尝试在获取器中使用 this.username
,我们将陷入无限循环! 这就是为什么 sequelize 提供 getdatavalue 方法的原因。
设置器
设置器是为模型定义中的一列定义的 set() 函数。 它接收要设置的值:
const user = sequelize.define('user', {
username: datatypes.string,
password: {
type: datatypes.string,
set(value) {
// 在数据库中以明文形式存储密码是很糟糕的.
// 使用适当的哈希函数来加密哈希值更好.
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 在将数据发送到数据库之前自动调用了设置器。 数据库得到的唯一数据是已经散列过的值。
如果我们想将模型实例中的另一个字段包含在计算中,那也是可以的,而且非常容易!
const user = sequelize.define('user', {
username: datatypes.string,
password: {
type: datatypes.string,
set(value) {
// 在数据库中以明文形式存储密码是很糟糕的.
// 使用适当的哈希函数来加密哈希值更好.
// 使用用户名作为盐更好.
this.setdatavalue('password', hash(this.username value));
}
}
});
**
注意
**: 上面涉及密码处理的示例尽管比单纯以明文形式存储密码要好得多,但远非完美的安全性。 正确处理密码很困难,这里的所有内容只是为了举例说明 sequelize 功能。
组合获取器和设置器
获取器和设置器都可以在同一字段中定义.
举个例子,假设我们正在建一个 post 模型,其 content 是无限长度的文本。 假设要提高内存使用率,我们要存储内容的压缩版本。
注意
:在这种情况下,现代数据库应会自动进行一些压缩. 这只是为了举例.
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'));
}
}
});
通过上述设置,每当我们尝试与 post 模型的 content 字段进行交互时,sequelize 都会自动处理自定义的获取器和设置器。 例如:
const post = await post.create({ content: 'hello everyone!' });
console.log(post.content); // 'hello everyone!'
// 一切都在幕后进行,所以我们甚至都可以忘记内容实际上是
// 作为 gzip 压缩的 base64 字符串存储的!
// 但是,如果我们真的很好奇,我们可以获取 'raw' 数据...
console.log(post.getdatavalue('content'));
// output: 'h4siaaaaaaaacvnizcnjv0gtsy2qzm9lvqqauuk9jq8aaaa='
虚拟字段
虚拟字段是 sequelize 在后台填充的字段,但实际上它们不存在于数据库中。
例如,假设我们有一个 user 的 firstname 和 lastname 属性。
同样,这仅是为了示例。
如果有一种简单的方法能直接获取 全名 那会非常好! 我们可以将 getters 的概念与 sequelize 针对这种情况提供的特殊数据类型结合使用:datatypes.virtual
:
const { datatypes } = require("sequelize");
const user = sequelize.define('user', {
firstname: datatypes.text,
lastname: datatypes.text,
fullname: {
type: datatypes.virtual,
get() {
return `${this.firstname} ${this.lastname}`;
},
set(value) {
throw new error('不要尝试设置 `fullname` 的值!');
}
}
});
virtual 字段不会导致数据表也存在此列。 换句话说,上面的模型虽然没有 fullname 列。 但是它似乎存在着!
const user = await user.create({ firstname: 'john', lastname: 'doe' });
console.log(user.fullname); // 'john doe'
在 sequelize v7 中已弃用:gettermethods 和 settermethods
sequelize 在模型定义中还提供了 gettermethods
和 settermethods
参数,以指定看起来像但与虚拟属性不完全相同的事物。 不鼓励使用此方法,并且将来可能会不建议使用(建议直接使用虚拟属性)。
示例:
const { sequelize, datatypes } = require('sequelize');
const sequelize = new sequelize('sqlite::memory:');
const user = sequelize.define('user', {
firstname: datatypes.string,
lastname: datatypes.string
}, {
gettermethods: {
fullname() {
return this.firstname ' ' this.lastname;
}
},
settermethods: {
fullname(value) {
// 注意:这仅用于演示.
// 查阅: https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/
const names = value.split(' ');
const firstname = names[0];
const lastname = names.slice(1).join(' ');
this.setdatavalue('firstname', firstname);
this.setdatavalue('lastname', lastname);
}
}
});
(async () => {
await sequelize.sync();
let user = await user.create({ firstname: 'john', lastname: 'doe' });
console.log(user.fullname); // 'john doe'
user.fullname = 'someone else';
await user.save();
user = await user.findone();
console.log(user.firstname); // 'someone'
console.log(user.lastname); // 'else'
})();