validations: support multi-key unique constraint
Modify the "unique" validator to accept additional property names to narrow the space of rows searched for duplicates. Example: Consider `SiteUser` belongsTo `Site` via `siteId` foreign key. Inside every site, the user email must be unique. It is allowed to register the same email with multiple sites. SiteUser.validateUniquenessOf('email', { scopedTo: ['siteId'] });
This commit is contained in:
parent
1db35cc926
commit
2a74bdc4de
|
@ -197,9 +197,19 @@ Validatable.validateAsync = getConfigurator('custom', {async: true});
|
|||
* - Oracle
|
||||
* - MongoDB
|
||||
*
|
||||
* ```
|
||||
* // The login must be unique across all User instances.
|
||||
* User.validatesUniquenessOf('login');
|
||||
*
|
||||
* // Assuming SiteUser.belongsTo(Site)
|
||||
* // The login must be unique within each Site.
|
||||
* SiteUser.validateUniquenessOf('login', { scopedTo: ['siteId'] });
|
||||
* ```
|
||||
|
||||
* @param {String} propertyName Property name to validate.
|
||||
* @options {Object} Options
|
||||
* @property {RegExp} with Regular expression to validate format.
|
||||
* @property {Array.<String>} scopedTo List of properties defining the scope.
|
||||
* @property {String} message Optional error message if property is not valid. Default error message: "is not unique".
|
||||
*/
|
||||
Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true});
|
||||
|
@ -297,6 +307,15 @@ function validateCustom(attr, conf, err, done) {
|
|||
function validateUniqueness(attr, conf, err, done) {
|
||||
var cond = {where: {}};
|
||||
cond.where[attr] = this[attr];
|
||||
|
||||
if (conf && conf.scopedTo) {
|
||||
conf.scopedTo.forEach(function(k) {
|
||||
var val = this[k];
|
||||
if (val !== undefined)
|
||||
cond.where[k] = this[k];
|
||||
}, this);
|
||||
}
|
||||
|
||||
this.constructor.find(cond, function (error, found) {
|
||||
if (error) {
|
||||
return err();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// This test written in mocha+should.js
|
||||
var should = require('./init.js');
|
||||
var async = require('async');
|
||||
|
||||
var j = require('../'), db, User;
|
||||
var ValidationError = j.ValidationError;
|
||||
|
@ -172,6 +173,41 @@ describe('validations', function () {
|
|||
})).should.not.be.ok;
|
||||
});
|
||||
|
||||
it('should support multi-key constraint', function(done) {
|
||||
var EMAIL = 'user@xample.com';
|
||||
var SiteUser = db.define('SiteUser', {
|
||||
siteId: String,
|
||||
email: String
|
||||
});
|
||||
SiteUser.validatesUniquenessOf('email', { scopedTo: ['siteId'] });
|
||||
async.waterfall([
|
||||
function automigrate(next) {
|
||||
db.automigrate(next);
|
||||
},
|
||||
function createSite1User(next) {
|
||||
SiteUser.create(
|
||||
{ siteId: 1, email: EMAIL },
|
||||
next);
|
||||
},
|
||||
function createSite2User(user1, next) {
|
||||
SiteUser.create(
|
||||
{ siteId: 2, email: EMAIL },
|
||||
next);
|
||||
},
|
||||
function validateDuplicateUser(user2, next) {
|
||||
var user3 = new SiteUser({ siteId: 1, email: EMAIL });
|
||||
user3.isValid(function(valid) {
|
||||
valid.should.be.false;
|
||||
next();
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
if (err && err.name == 'ValidationError') {
|
||||
console.error('ValidationError:', err.details.messages);
|
||||
}
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('format', function () {
|
||||
|
|
Loading…
Reference in New Issue