Hey,
Not sure if this is working as intended or not, but I noticed that if I instantiate a model then immediately save it, the validation rules on the individual properties are not run.
I created a small script below to demonstrate what I mean. If you run this with mocha, the first test on the user model will fail because err is null after the save, when I was expecting the validation to fail because user.email == undefined.
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
should = require('should'),
UserSchema,
User;
mongoose.connect('mongodb://localhost/test');
function validatePresenceOf (value) {
if(typeof value === 'string' || typeof value === 'number') {
value = value.toString().trim();
}
return !!(value && value.length);
}
UserSchema = new Schema({
email : {type: String,
validate: [validatePresenceOf, "email required"],
index: {unique: true}
},
hashed_password : String,
salt : String
});
User = mongoose.model('User', UserSchema);
describe('validatePresenceOf', function () {
it("should return false for undefined", function () {
validatePresenceOf(undefined).should.be.false;
});
it("should return false for empty string", function () {
validatePresenceOf('').should.be.false;
})
})
describe("User model", function () {
describe("validation", function () {
afterEach(function () {
User.find().remove();
});
it(" does not validate if user.email is not set first", function (done) {
var user = new User();
// expecting validation to fail because validatePresenceOf returns
// false, but err is null
user.save(function (err) {
should.exist(err);
err.toString().should.equal('ValidationError: Validator ' +
'"email required" failed for path email');
done();
});
});
it("is fine if I set user.email", function (done) {
var user = new User();
user.email = '';
// validation works fine if I set user.email before saving
user.save(function (err) {
should.exist(err);
err.toString().should.equal('ValidationError: Validator ' +
'"email required" failed for path email');
done();
});
});
});
});
if a path is required use the required
validator.
new Schema({
email : {type: String,
validate: [validatePresenceOf, "email required"],
required: true,
index: {unique: true}
},
hashed_password : String,
salt : String
});
Ah, awesome thanks for the help!
On May 30, 2012 11:26 AM, "Aaron Heckmann" <
[email protected]>
wrote:
if a path is required use the
required
validator.new Schema({ email : {type: String, validate: [validatePresenceOf, "email required"], required: true, index: {unique: true} }, hashed_password : String, salt : String }); --- Reply to this email directly or view it on GitHub: https://github.com/LearnBoost/mongoose/issues/941#issuecomment-6012750
this is a bit old but how do you perform conditional required based on another field?
I added this custom validator:
validate: [
function validator(val) {
return this.type === 'other' && val === '';
}, '{PATH} is required'
]
but if property is not existent this is never run... any workaround?
I have exactly the same problem as @cyberval
UPD:
In the meantime I came up with this ugly hack (real code snippet):
schema.path('frequency').validators.push([function(val){
// frequency is not required for singular sessions
if(!this.multiSession) return true;
return !!val;
}, mongoose.Error.messages.general.required, 'required']);
schema.path('frequency').isRequired = true;
Seems to work. But yeah, having custom conditional required validation would be nice.
:+1:
Just spent the better part of an hours thinking I was doing something wrong. Thanks for the hack @chopachom
The trick I used for conditional 'required' validation was this to default to null and use a custom validator. This will force the validator to be called even if the field is not set.
...
default: null,
validate: [customValidator...]
There should not need to be a trick to make it work.
@jonstorer agreed. I have the same issue as @cyberval. We should open a new issue.
has this been solved ?
@cyberval yeah, required can now take a function. See #2247
I'm not sure it is working. If the property is defined then yes the function will be executed but if the property is not defined then the required function won't be triggered and required is set to true somewhere
@cyberval passing a function to required
is supported, but running validators on undefined fields is not supported and currently not planned, see #2449 #2446
Here's another way to do it. Add the validator into some pre validate middleware:
schema.pre('validate', function(next) {
//3, or whatever value you're checking for
if (this.status === 3) {
return next();
}
if (this.myConditionalField) {
return next();
}
var error = new mongoose.Error.ValidationError(this);
error.errors.myConditionalField = new mongoose.Error.ValidatorError('myConditionalField', 'myConditionalField is required for status passed.', 'notvalid', this.myConditionalField);
return next(error);
});
use default: null
worked for me.
Most helpful comment
There should not need to be a trick to make it work.