猫鼬自定义验证几个字段在更新

Mongoose custom validation of several fields on update

本文关键字:几个 字段 更新 自定义 验证      更新时间:2023-09-26

首先,这没有帮助。

假设我们有一个用户模型:

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属性值验证它。

案例# 1

  • activefalse
  • 我们不应该能够设置头像-它不应该通过验证

案例# 2

  • activetrue
  • 我们应该能够设置头像-它应该通过验证
<

想法/h3>
  1. 使用自定义验证器
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字段的权限。

  • 使用预"validate"钩子
  • schema.pre('validate', function (next) {
        // this will never be called
    });
    

    这个钩子不能用update方法。

  • 使用预"update"钩子
  • schema.pre('update', function (next) {
        console.log(this.active); // undefined
    });
    

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

  • 使用post "update"钩子
  • schema.post('update', function (next) {
        console.log(this.active); // false
    });
    

    这个可以工作,但就验证而言不是很好的选择,因为只有当模型已经保存时才调用该函数。

    问题

    所以有一种方法来验证基于几个字段的模型(既保存在DB和新的)保存它之前,而使用model.update()方法?

    作为总结:

    1. 初始用户对象
    { active: false, avatar: null }
    
  • User.update({ _id: id }, { $set: { avatar: 'user1.png' } });
    
  • 验证应该有权访问
  • { active: false, avatar: 'user1.png' }
    
  • 如果验证失败,更改不应该传递给DB
  • 由于使用update()的限制,我决定这样解决问题:

    • 使用自定义验证器(问题中提到的想法#1)
    • 不要使用update()

    所以不用

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

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

    在本例中,自定义验证器按预期工作。

    注:我选择这个答案作为我的正确答案,但我将奖励最相关的答案。

    您可以使用mongoose文档中指定的上下文选项来完成此操作。

    上下文选项允许您在更新验证器中设置此值到底层查询。


    在你的代码中你可以像这样在路径上定义你的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');
    

    更新时需要输入两个选项runValidatorscontext。所以你的更新查询变成:

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

    您是否尝试给active一个默认值,以便它不会在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
    }
    

    当你创建用户时,你是否这样设置用户

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

    你可以尝试使用预"save"钩子。我以前使用过它,可以在"this"中获得值。

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

    希望这对你也有用!

    您必须使用异步自定义验证器:

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

    (并将runValidatorscontext选项传递给update(),正如Naeem的回答所建议的那样)。

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

    作为一种替代方案,您还可以考虑使用如下内容(如果无法更新非活动用户的约束比实际验证更重要):

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