Mongoose自定义validation更新的几个字段
首先, 这没有帮助。
比方说,我们有一个用户模型:
const schema = new mongoose.Schema({ active: { type: Boolean }, avatar: { type: String } }); const User = mongoose.model('User', schema);
当我们更新它(设置一个化身):
// This should pass validation User.update({ _id: id }, { $set: { avatar: 'user1.png' } });
我们希望根据当前(或更改)的active
属性值进行validation。
情况1
-
active
是false
- 我们不应该能够设置头像 – 它不应该通过validation
案例#2
-
active
是true
- 我们应该能够设置头像 – 它应该通过validation
思路
- 使用自定义validation器
const schema = new mongoose.Schema({ active: { type: Boolean }, avatar: { type: String, validate: [validateAvatar, 'User is not active'] } }); function validateAvatar (value) { console.log(value); // user.avatar console.log(this.active); // undefined }
所以这不起作用,因为我们没有访问active
领域。
- 使用前“validation”钩子
schema.pre('validate', function (next) { // this will never be called });
这个钩子不适用于update
方法。
- 使用前“更新”钩子
schema.pre('update', function (next) { console.log(this.active); // undefined });
这对我们不起作用,因为它没有模型字段的访问权限。
- 使用后“更新”钩子
schema.post('update', function (next) { console.log(this.active); // false });
这个工作,但在validation方面不是很好的select,因为只有当模型已经保存时才调用函数。
题
那么有没有一种方法来validation模型基于几个字段(都保存在数据库和新的)保存之前,而使用model.update()
方法?
总结如下:
- 初始用户对象
{ active: false, avatar: null }
- 更新
User.update({ _id: id }, { $set: { avatar: 'user1.png' } });
- validation应该有访问权限
{ active: false, avatar: 'user1.png' }
- 如果validation失败,则不应将更改传递给数据库
由于使用update()
限制,我决定用这种方法解决这个问题:
- 使用自定义validation器(问题中提到的想法#1)
- 不要使用
update()
所以,而不是
User.update({ _id: id }, { $set: { avatar: 'user1.png' } });
我用
User.findOne({ _id: id }) .then((user) => { user.avatar = 'user1.png'; user.save(); });
在这种情况下,自定义validation器按预期工作。
PS我select这个答案对我来说是正确的,但是我会给予最相关的答案。
您可以使用mongoose文档中指定的上下文选项来执行此操作。
上下文选项
上下文选项允许您将更新validation器中的值设置为基础查询。
所以在你的代码中,你可以像这样在path上定义你的validator
:
function validateAvatar (value) { // When running update validators with the `context` option set to // 'query', `this` refers to the query object. return this.getUpdate().$set.active; } schema.path('avatar').validate(validateAvatar, 'User is not active');
而更新时,你需要input两个选项runValidators
和context
。 所以你的更新查询变成:
var opts = { runValidators: true, context: 'query' }; user.update({ _id: id }, { $set: { avatar: 'user1.png' }, opts });
你有没有尝试给出一个默认值,所以它不会在MongoDB中未定义。
const schema = new mongoose.Schema({ active: { type: Boolean, 'default': false }, avatar: { type: String, trim: true, 'default': '', validate: [validateAvatar, 'User is not active'] }}); function validateAvatar (value) { console.log(value); // user.avatar console.log(this.active); // undefined }
创build时,您是以这种方式设置用户
var User = mongoose.model('User'); var user_1 = new User({ active: false, avatar: ''}); user_1.save(function (err) { if (err) { return res.status(400).send({message: 'err'}); } res.json(user_1); });
你可以尝试预先“保存”钩子。 我以前用过它,可以在“this”中获得价值。
schema.pre('save', function (next) { console.log(this.active); });
希望与你一起工作!
你必须使用一个asynchronous定制的validation器 :
const schema = new mongoose.Schema({ active: { type: Boolean }, avatar: { type : String, validate : { validator : validateAvatar, message : 'User is not active' } } }); function validateAvatar(v, cb) { this.model.findOne({ _id : this.getQuery()._id }).then(user => { if (user && ! user.active) { return cb(false); } else { cb(); } }); }
(并按照Naeem的回答build议,将runValidators
和context
选项传递给update()
)。
但是,这将需要每个更新的额外查询,这是不理想的。
另外,你也可以考虑使用类似的东西(如果不能更新非活动用户的约束比实际validation更重要):
user.update({ _id : id, active : true }, { ... }, ...);