2013-06-02 17 views
14

如何在將編輯後的數據保存在貓鼬中之前進行驗證?如何在更新期間檢查數據庫中是否已存在數據(Mongoose和Express)

例如,如果sample.name在數據庫中已經存在,用戶將收到了一些錯誤,這樣的事情的,這是我下面

//Post: /sample/edit 
app.post(uri + '/edit', function (req, res, next) { 
    Sample.findById(req.param('sid'), function (err, sample) { 

    if (err) { 
     return next(new Error(err)); 
    } 

    if (!sample) { 
     return next(new Error('Invalid reference to sample information')); 
    } 

    // basic info 
    sample.name = req.body.supplier.name; 
    sample.tin = req.body.supplier.tin; 

    // contact info 
    sample.contact.email = req.body.supplier.contact.email; 
    sample.contact.mobile = req.body.supplier.contact.mobile; 
    sample.contact.landline = req.body.supplier.contact.landline; 
    sample.contact.fax = req.body.supplier.contact.fax; 

    // address info 
    sample.address.street = req.body.supplier.address.street; 
    sample.address.city = req.body.supplier.address.city; 
    sample.address.state = req.body.supplier.address.state; 
    sample.address.country = req.body.supplier.address.country; 
    sample.address.zip = req.body.supplier.address.zip; 

    sample.save(function (err) { 
     if (err) { 
     return next(new Error(err)); 
     } 

     res.redirect(uri + '/view/' + sample._id); 
    }); 

    }); 
}); 

回答

36

代碼通常可以使用mongoose validation但因爲你需要一個異步結果(現有名稱的數據庫查詢)和驗證器不支持承諾(從我可以告訴),您將需要創建自己的函數並傳遞迴調。這裏有一個例子:

var mongoose = require('mongoose'), 
    Schema = mongoose.Schema, 
    ObjectId = Schema.ObjectId; 

mongoose.connect('mongodb://localhost/testDB'); 

var UserSchema = new Schema({ 
    name: {type:String} 
}); 

var UserModel = mongoose.model('UserModel',UserSchema); 

function updateUser(user,cb){ 
    UserModel.find({name : user.name}, function (err, docs) { 
     if (docs.length){ 
      cb('Name exists already',null); 
     }else{ 
      user.save(function(err){ 
       cb(err,user); 
      }); 
     } 
    }); 
} 

UserModel.findById(req.param('sid'),function(err,existingUser){ 
    if (!err && existingUser){ 
     existingUser.name = 'Kevin'; 
     updateUser(existingUser,function(err2,user){ 
      if (err2 || !user){ 
       console.log('error updated user: ',err2); 
      }else{ 
       console.log('user updated: ',user); 
      } 

     }); 
    } 
}); 

UPDATE:一種更好的方式

預掛鉤似乎是一個更自然的地方停下來保存:

UserSchema.pre('save', function (next) { 
    var self = this; 
    UserModel.find({name : self.name}, function (err, docs) { 
     if (!docs.length){ 
      next(); 
     }else{     
      console.log('user exists: ',self.name); 
      next(new Error("User exists!")); 
     } 
    }); 
}) ; 

更新2:異步自定義驗證器

它看起來像貓鼬現在支持異步自定義驗證器現在s Ø這將可能是自然的解決方案:

var userSchema = new Schema({ 
     name: { 
     type: String, 
     validate: { 
      validator: function(v, cb) { 
      User.find({name: v}, function(err,docs){ 
       cb(docs.length == 0); 
      }); 
      }, 
      message: 'User already exists!' 
     } 
     } 
    }); 
+4

你不是叫「下一個()」,在該用戶是否存在,我想你應該爲什麼不直接使用在了'unique' PARAM調用next()有錯誤 –

+3

名稱字段? – udidu

+0

用戶將如何使用save()方法? –

4

另一種方式繼續使用的示例@nfreeze是這個驗證方法:

UserModel.schema.path('name').validate(function (value, res) { 
    UserModel.findOne({name: value}, 'id', function(err, user) { 
     if (err) return res(err); 
     if (user) return res(false); 
     res(true); 
    }); 
}, 'already exists'); 
+1

什麼是路徑?你能否詳細說明這個問題 –

+0

這對我有用,看起來像最優雅的方式,但它會拋出一個棄用錯誤,即DeprecationWarning:隱式異步自定義驗證器(帶有2個參數的自定義驗證器)在mongoose> = 4.9.0 。 – Marko

+0

http://mongoosejs.com/docs/validation.html#async-custom-validators 即使您不想使用異步驗證器,也要小心,因爲貓鼬4會假設所有帶有2個參數的函數是異步的,就像validator.isEmail函數一樣。此行爲從4.9.0開始被視爲棄用,您可以通過在自定義驗證器上指定isAsync:false來關閉它。 – Marko

0

如果你被一個唯一索引搜索,然後使用UserModel.count實際上可能比UserModel.findOne更好,因爲它返回整個文檔(即進行讀取)而不是隻返回一個int。

2

下面是以較少代碼完成此操作的另一種方法。

更新3:異步模型類的靜態

類似於選項2,這允許您創建直接鏈接到該模式,但利用該模型從同一個文件調用的函數。

model.js

userSchema.statics.updateUser = function(user, cb) { 
    UserModel.find({name : user.name}).exec(function(err, docs) { 
    if (docs.length){ 
     cb('Name exists already', null); 
    } else { 
     user.save(function(err) { 
     cb(err,user); 
     } 
    } 
    }); 
} 

從文件

var User = require('./path/to/model'); 

User.updateUser(user.name, function(err, user) { 
    if(err) { 
    var error = new Error('Already exists!'); 
    error.status = 401; 
    return next(error); 
    } 
}); 
1

除了已經公佈的實例調用,這裏是用快遞 - 異步纏繞和異步功能的另一種方法(ES2017)。

路由器

router.put('/:id/settings/profile', wrap(async function (request, response, next) { 
    const username = request.body.username 
    const email = request.body.email 
    const userWithEmail = await userService.findUserByEmail(email) 
    if (userWithEmail) { 
     return response.status(409).send({message: 'Email is already taken.'}) 
    } 
    const userWithUsername = await userService.findUserByUsername(username) 
    if (userWithUsername) { 
     return response.status(409).send({message: 'Username is already taken.'}) 
    } 
    const user = await userService.updateProfileSettings(userId, username, email) 
    return response.status(200).json({user: user}) 
})) 

UserService

async function updateProfileSettings (userId, username, email) { 
    try { 
     return User.findOneAndUpdate({'_id': userId}, { 
      $set: { 
       'username': username, 
       'auth.email': email 
      } 
     }, {new: true}) 
    } catch (error) { 
     throw new Error(`Unable to update user with id "${userId}".`) 
    } 
} 

async function findUserByEmail (email) { 
    try { 
     return User.findOne({'auth.email': email.toLowerCase()}) 
    } catch (error) { 
     throw new Error(`Unable to connect to the database.`) 
    } 
} 

async function findUserByUsername (username) { 
    try { 
     return User.findOne({'username': username}) 
    } catch (error) { 
     throw new Error(`Unable to connect to the database.`) 
    } 
} 

// other methods 

export default { 
    updateProfileSettings, 
    findUserByEmail, 
    findUserByUsername, 
} 

資源

async function

await

express-async-wrap

相關問題