简体   繁体   English

在更新时对几个字段进行Mongoose自定义验证

[英]Mongoose custom validation of several fields on update

First of all, this doesn't help. 首先, 没有帮助。

Let's say, we have a User model: 比方说,我们有一个用户模型:

const schema = new mongoose.Schema({
    active: { type: Boolean },
    avatar: { type: String }
});

const User = mongoose.model('User', schema);

When we update it (set an avatar): 当我们更新它(设置一个头像):

// This should pass validation
User.update({ _id: id }, { $set: { avatar: 'user1.png' } });

We want to validate it based on current (or changed) active attribute value. 我们希望根据当前(或更改的) active属性值对其进行验证。

Case #1 情况1

  • active is false activefalse
  • we should not be able to set avatar - it should not pass the validation 我们应该设置头像 - 它不应该通过验证

Case #2 案例#2

  • active is true activetrue
  • we should be able to set avatar - it should pass the validation 我们应该能够设置头像 - 它应该通过验证

Ideas 思路

  1. Use a custom validator 使用自定义验证器
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
}

So this will not work as we don't have an access to active field. 所以这不起作用,因为我们没有访问active字段。

  1. Use pre "validate" hook 使用预“验证”钩子
schema.pre('validate', function (next) {
    // this will never be called
});

This hook doesn't work with update method. 此挂钩不适用于update方法。

  1. Use pre "update" hook 使用pre“update”挂钩
schema.pre('update', function (next) {
    console.log(this.active); // undefined
});

This will not work for us as it doesn't have an access to model fields. 这对我们不起作用,因为它无法访问模型字段。

  1. Use post "update" hook 使用post“update”hook
schema.post('update', function (next) {
    console.log(this.active); // false
});

This one works, but in terms of validation is not quite good choice, as the function is being called only when model was already saved. 这个可行,但在验证方面不是很好的选择,因为只有在模型已经保存时才调用该函数。

Question

So is there a way to validate the model based on several fields (both saved in DB and new ones) before saving it, while using model.update() method? 那么有没有一种方法可以在保存之前基于几个字段(保存在DB和新的字段中)验证模型,同时使用model.update()方法?

As a summary: 作为总结:

  1. Initial user object 初始用户对象
{ active: false, avatar: null }
  1. Update 更新
User.update({ _id: id }, { $set: { avatar: 'user1.png' } });
  1. Validation should have an access to 验证应该有权访问
{ active: false, avatar: 'user1.png' }
  1. If validation fails, changes should not be passed to DB 如果验证失败,则不应将更改传递给DB

Due to limitation of working with update() I've decided to solve the problem this way: 由于使用update()限制,我决定以这种方式解决问题:

  • Use custom validators (idea #1 mentioned in the question) 使用自定义验证器(问题中提到的想法#1)
  • Don't use update() 不要使用update()

So instead of 而不是

User.update({ _id: id }, { $set: { avatar: 'user1.png' } });

I use 我用

User.findOne({ _id: id })
    .then((user) => {
        user.avatar = 'user1.png';
        user.save();
    });

In this case custom validators work as expected. 在这种情况下,自定义验证程序按预期工作。

PS I choose this answer as a correct one for me, but I will give bounty to the most relevant answer. PS我选择这个答案对我来说是正确的,但我会给予最相关答案的赏金。

You can do this with the context option specified in the mongoose documentation . 您可以使用mongoose文档中指定的context选项执行此操作。

The context option 上下文选项

The context option lets you set the value of this in update validators to the underlying query. context选项允许您将更新验证程序中的this值设置为基础查询。


So in your code you can define your validator on the path like this: 因此,在您的代码中,您可以在路径上定义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'); 

And while updating you need to enter two options runValidators and context . 在更新时,您需要输入两个选项runValidatorscontext So your update query becomes: 所以你的更新查询变为:

 var opts = { runValidators: true, context: 'query' }; user.update({ _id: id }, { $set: { avatar: 'user1.png' }, opts }); 

Did you try giving active a default value so it would not be undefined in mongodb. 您是否尝试激活默认值,以便在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
}

When creating do you set the user in this way 在创建时,您是否以这种方式设置用户

  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);                
        });

You can try with pre "save" hook. 您可以尝试预先“保存”钩子。 I used it before and can get the value in "this". 我之前使用过它,可以在“this”中获得值。

schema.pre('save', function (next) {
    console.log(this.active);
});

Hope this work with you too ! 希望这也和你合作!

You have to use an asynchronous custom validator for that: 您必须使用异步自定义验证器

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();
    }
  });
}

(and pass the runValidators and context options to update() , as suggested in the answer from Naeem). (并将runValidatorscontext选项传递给update() ,如Naeem的答案所示)。

However, this will require an extra query for each update, which isn't ideal. 但是,这将需要对每个更新进行额外查询,这并不理想。

As an alternative, you could also consider using something like this (if the constraint of not being able to update inactive users is more important than actually validating for it): 作为替代方案,您还可以考虑使用类似的东西(如果无法更新非活动用户的约束比实际验证它更重要):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM