2013-06-02 9 views
14

Jak dokonać walidacji przed zapisaniem edytowanych danych w mangalu?Jak sprawdzić, czy dane już istnieją w bazie danych podczas aktualizacji (Mongoose And Express)?

Na przykład, jeśli sample.name już istnieje w bazie danych, użytkownik otrzyma jakiegoś błędu, coś takiego, oto mój kod poniżej

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

    }); 
}); 

Odpowiedz

36

Zazwyczaj można użyć mongoose validation ale skoro potrzebujesz Wynik asynchroniczny (zapytanie db o istniejące nazwy) i weryfikatory nie obsługują obietnic (z tego co wiem), musisz utworzyć własną funkcję i przekazać wywołanie zwrotne. Oto przykład:

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: Lepszym sposobem

Wstępnie hak wydaje się być bardziej naturalne miejsce do zatrzymania się zapisać:

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

UPDATE 2: asynchroniczny niestandardowe weryfikatory

Wygląda jak mongoose obsługuje asynchronizatory niestandardowe teraz s O to byłoby chyba naturalne rozwiązanie:

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

Nie dzwonisz „next()”, jeśli użytkownik nie istnieje, myślę, że należy wywołać next() z błędem –

+3

dlaczego nie tylko przy użyciu 'unique' param na pole nazwy? – udidu

+0

jak użytkownik będzie miał metodę save() na nim? –

4

Innym sposobem, aby kontynuować przykład @nfreeze używany jest ta metoda sprawdzania poprawności:

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

Co to jest ścieżka? Czy mógłbyś to rozwinąć? –

+0

To działało dla mnie i wygląda na najbardziej elegancki sposób, ale generuje błąd usunięcia, tzn. DeprecationWarning: Implicit async Custom Values ​​(niestandardowe walidatory, które pobierają 2 argumenty) są przestarzałe w mongoose> = 4.9.0 . – Marko

+0

http://mongoosejs.com/docs/validation.html#async-custom-validators Nawet jeśli nie chcesz używać asynchronicznych weryfikatorów, bądź ostrożny, ponieważ mangusta 4 zakłada, że ​​wszystkie funkcje przyjmują 2 argumenty są asynchroniczne, podobnie jak funkcja validator.isEmail. To zachowanie jest uważane za przestarzałe od wersji 4.9.0 i można je wyłączyć, określając właściwość isAsync: false na niestandardowym walidatorze. – Marko

0

Jeśli szukasz za pomocą unikalnego indeksu, a następnie za pomocą UserModel.count może być w rzeczywistości lepszy niż UserModel.findOne z powodu zwrócenia całego dokumentu (tj. Robienia odczytu) zamiast zwracania tylko int.

2

Oto inny sposób osiągnięcia tego w mniejszej ilości kodu.

UPDATE 3: Asynchroniczny model klasy statyka

podobne do opcji 2, to pozwala na tworzenie funkcji związanych bezpośrednio z schematu, ale z tej samej nazwie pliku przy użyciu modelu.

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

Połącz z plikiem

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

Oprócz już zamieszczonych przykładów, tutaj jest inne podejście używając Express asynchronicznej-wrap i funkcje asynchroniczne (ES2017).

Router

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, 
} 

Resources

async function

await

express-async-wrap

Powiązane problemy