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

  • activefalse
  • 我们应该能够设置头像 – 它不应该通过validation

案例#2

  • activetrue
  • 我们应该能够设置头像 – 它应该通过validation

思路

  1. 使用自定义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领域。

  1. 使用前“validation”钩子
 schema.pre('validate', function (next) { // this will never be called }); 

这个钩子不适用于update方法。

  1. 使用前“更新”钩子
 schema.pre('update', function (next) { console.log(this.active); // undefined }); 

这对我们不起作用,因为它没有模型字段的访问权限。

  1. 使用后“更新”钩子
 schema.post('update', function (next) { console.log(this.active); // false }); 

这个工作,但在validation方面不是很好的select,因为只有当模型已经保存时才调用函数。

那么有没有一种方法来validation模型基于几个字段(都保存在数据库和新的)保存之前,而使用model.update()方法?

总结如下:

  1. 初始用户对象
 { active: false, avatar: null } 
  1. 更新
 User.update({ _id: id }, { $set: { avatar: 'user1.png' } }); 
  1. validation应该有访问权限
 { active: false, avatar: 'user1.png' } 
  1. 如果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两个选项runValidatorscontext 。 所以你的更新查询变成:

 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议,将runValidatorscontext选项传递给update() )。

但是,这将需要每个更新的额外查询,这是不理想的。

另外,你也可以考虑使用类似的东西(如果不能更新非活动用户的约束比实际validation更重要):

 user.update({ _id : id, active : true }, { ... }, ...);