不要怂,就是干,撸起袖子干!

Commit 0750a939 by Mick Hansen

merge skipped fields refactor

2 parents 7433307b 6c9ade1b
...@@ -4,7 +4,7 @@ MySQL, MariaDB, PostgresSQL, and SQLite Object Relational Mapper (ORM) for [node ...@@ -4,7 +4,7 @@ MySQL, MariaDB, PostgresSQL, and SQLite Object Relational Mapper (ORM) for [node
### Install ### Install
To install 1.x.x (currently 1.7.x) - which has a stable API and is backwards compatible: To install 1.x.x (currently 1.7.x) - which has a stable API and is mostly backwards compatible:
`npm install sequelize` `npm install sequelize`
...@@ -26,7 +26,7 @@ To install 2.x.x branch - which has a unstable API and will break backwards comp ...@@ -26,7 +26,7 @@ To install 2.x.x branch - which has a unstable API and will break backwards comp
### 2.0.0 ### ### 2.0.0 ###
There is a parallel "branch" of the project, released as `2.0.0-alphaX` in NPM. All those releases are based on the master There is a parallel "branch" of the project, released as `2.0.0-alphaX` in NPM. All those releases are based on the master
and will get all the changes of the master. However, `2.0.0` will contain backwards compatibility breaking changes. Check the and will get all the changes of the master. However, `2.0.0` will contain major backwards compatibility breaking changes. Check the
changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/2.0.0/changelog.md changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/2.0.0/changelog.md
##### 2.0.0 API should be considered unstable ##### 2.0.0 API should be considered unstable
......
...@@ -8,6 +8,9 @@ Notice: All 1.7.x changed are present in 2.0.x aswell ...@@ -8,6 +8,9 @@ Notice: All 1.7.x changed are present in 2.0.x aswell
- fixes a few bugs with transactions in regards to associations - fixes a few bugs with transactions in regards to associations
- add error handling for transaction creation - add error handling for transaction creation
- `sequelize --undo` will now actually undo migrations. Its basically an alias for `sequelize --migrate --undo`. [#1059](https://github.com/sequelize/sequelize/pull/1059) - `sequelize --undo` will now actually undo migrations. Its basically an alias for `sequelize --migrate --undo`. [#1059](https://github.com/sequelize/sequelize/pull/1059)
- fix bug where `{where: {ne: null}}` would result in `!= NULL` instead of `IS NOT NULL` [#1231](https://github.com/sequelize/sequelize/pull/1059)
- fixes a bug with validation skipping using the `fields` options. [#1233](https://github.com/sequelize/sequelize/pull/1233)
- fixes a bug with postgres and setters [#1234](https://github.com/sequelize/sequelize/issues/123)
#### Backwards compatability changes #### Backwards compatability changes
- Hooks are no longer passing value hashes. Instead, they are now passing instances of the model. - Hooks are no longer passing value hashes. Instead, they are now passing instances of the model.
......
...@@ -875,9 +875,13 @@ module.exports = (function() { ...@@ -875,9 +875,13 @@ module.exports = (function() {
options.fields = newFields || options.fields options.fields = newFields || options.fields
if (options.validate === true) { if (options.validate === true) {
if (options.fields.length) {
var skippedFields = Utils._.difference(Object.keys(self.attributes), options.fields);
}
if (options.hooks === true) { if (options.hooks === true) {
var iterate = function(i) { var iterate = function(i) {
daos[i].hookValidate({skip: options.fields}).complete(function (err) { daos[i].hookValidate({skip: skippedFields}).complete(function (err) {
if (!!err) { if (!!err) {
errors.push({record: v, errors: err}) errors.push({record: v, errors: err})
} }
...@@ -890,13 +894,13 @@ module.exports = (function() { ...@@ -890,13 +894,13 @@ module.exports = (function() {
iterate(i) iterate(i)
}) })
} }
} else { } else
var afterDaos = Utils._.after(daos.length, function() { var afterDaos = Utils._.after(daos.length, function() {
next(errors.length > 0 ? errors : null) next(errors.length > 0 ? errors : null)
}) })
daos.forEach(function(v) { daos.forEach(function(v) {
v.validate({skip: options.fields}).success(function(err) { v.validate({skip: skippedFields}).success(function(err) {
if (!!err) { if (!!err) {
errors.push({record: v, errors: err}) errors.push({record: v, errors: err})
} }
...@@ -1149,12 +1153,22 @@ module.exports = (function() { ...@@ -1149,12 +1153,22 @@ module.exports = (function() {
} }
if (options.validate === true) { if (options.validate === true) {
<<<<<<< HEAD
var build = self.build(attrValueHash) var build = self.build(attrValueHash)
, attrKeys = Object.keys(attrValueHash) , attrKeys = Object.keys(attrValueHash)
build.hookValidate({ build.hookValidate({
skip: Object.keys(build.dataValues).filter(function(val) { return attrKeys.indexOf(val) !== -1 }) skip: Object.keys(build.dataValues).filter(function(val) { return attrKeys.indexOf(val) !== -1 })
}).error(function(err) { }).error(function(err) {
=======
var build = self.build(attrValueHash)
// We want to skip validations for all other fields
var updatedFields = Object.keys(attrValueHash)
var skippedFields = Utils._.difference(Object.keys(self.attributes), updatedFields)
build.hookValidate({skip: skippedFields}).error(function(err) {
>>>>>>> master
emitter.emit('error', err) emitter.emit('error', err)
}).success(function(attributes) { }).success(function(attributes) {
if (!!attributes && !!attributes.dataValues) { if (!!attributes && !!attributes.dataValues) {
......
...@@ -93,7 +93,7 @@ var validateAttributes = function() { ...@@ -93,7 +93,7 @@ var validateAttributes = function() {
Utils._.each(this.model.rawAttributes, function(rawAttribute, field) { Utils._.each(this.model.rawAttributes, function(rawAttribute, field) {
var value = self.model.dataValues[field] || undefined var value = self.model.dataValues[field] || undefined
, hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined))) , hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined)))
, isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) === -1 , isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) !== -1
if (self.model.validators.hasOwnProperty(field) && !hasAllowedNull && !isSkipped) { if (self.model.validators.hasOwnProperty(field) && !hasAllowedNull && !isSkipped) {
errors = Utils._.merge(errors, validateAttribute.call(self, value, field)) errors = Utils._.merge(errors, validateAttribute.call(self, value, field))
......
...@@ -139,7 +139,7 @@ module.exports = (function() { ...@@ -139,7 +139,7 @@ module.exports = (function() {
if (!!this.callee.daoFactory && !!this.callee.daoFactory.rawAttributes && !!this.callee.daoFactory.rawAttributes[key] && !!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) { if (!!this.callee.daoFactory && !!this.callee.daoFactory.rawAttributes && !!this.callee.daoFactory.rawAttributes[key] && !!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) {
record = hstore.parse(record) record = hstore.parse(record)
} }
this.callee[key] = record this.callee.dataValues[key] = record
} }
} }
} }
...@@ -152,7 +152,7 @@ module.exports = (function() { ...@@ -152,7 +152,7 @@ module.exports = (function() {
if (!!this.callee.daoFactory && !!this.callee.daoFactory.rawAttributes && !!this.callee.daoFactory.rawAttributes[key] && !!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) { if (!!this.callee.daoFactory && !!this.callee.daoFactory.rawAttributes && !!this.callee.daoFactory.rawAttributes[key] && !!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) {
record = hstore.parse(record) record = hstore.parse(record)
} }
this.callee[key] = record this.callee.dataValues[key] = record
} }
} }
} }
......
...@@ -299,7 +299,7 @@ var Utils = module.exports = { ...@@ -299,7 +299,7 @@ var Utils = module.exports = {
case 'eq': case 'eq':
return '=' return '='
case 'ne': case 'ne':
return val ? '!=' : 'IS NOT' return val === null ? 'IS NOT' : '!='
case 'between': case 'between':
case '..': case '..':
return 'BETWEEN' return 'BETWEEN'
...@@ -451,7 +451,7 @@ var Utils = module.exports = { ...@@ -451,7 +451,7 @@ var Utils = module.exports = {
for (var key in hash) { for (var key in hash) {
if (key instanceof Utils.literal) { if (key instanceof Utils.literal) {
_hash[key] = hash[key] _hash[key] = hash[key]
} else if (key.indexOf('.') === -1) { } else if (key.indexOf('.') === -1) {
_hash[tableName + '.' + key] = hash[key] _hash[tableName + '.' + key] = hash[key]
} else { } else {
......
...@@ -597,7 +597,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -597,7 +597,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank creates (with timestamps: false)', function (done) { it('should allow blank creates (with timestamps: false)', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false}) var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) { Worker.sync().done(function(err) {
Worker.create({}, {fields: []}).done(function (err, worker) { Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
expect(worker).to.be.ok expect(worker).to.be.ok
...@@ -608,7 +608,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -608,7 +608,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow truly blank creates', function (done) { it('should allow truly blank creates', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false}) var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) { Worker.sync().done(function(err) {
Worker.create({}, {fields: []}).done(function (err, worker) { Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
expect(worker).to.be.ok expect(worker).to.be.ok
...@@ -897,7 +897,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -897,7 +897,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank arrays (return immediatly)', function (done) { it('should allow blank arrays (return immediatly)', function (done) {
var Worker = this.sequelize.define('Worker', {}) var Worker = this.sequelize.define('Worker', {})
Worker.sync().done(function(err) { Worker.sync().done(function(err) {
Worker.bulkCreate([]).done(function (err, workers) { Worker.bulkCreate([]).done(function (err, workers) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
expect(workers).to.be.ok expect(workers).to.be.ok
...@@ -909,7 +909,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -909,7 +909,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank creates (with timestamps: false)', function (done) { it('should allow blank creates (with timestamps: false)', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false}) var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) { Worker.sync().done(function(err) {
Worker.bulkCreate([{}, {}]).done(function (err, workers) { Worker.bulkCreate([{}, {}]).done(function (err, workers) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
expect(workers).to.be.ok expect(workers).to.be.ok
...@@ -920,7 +920,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -920,7 +920,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow autoincremented attributes to be set', function (done) { it('should allow autoincremented attributes to be set', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false}) var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) { Worker.sync().done(function(err) {
Worker.bulkCreate([ Worker.bulkCreate([
{id: 5}, {id: 5},
{id: 10} {id: 10}
...@@ -954,4 +954,4 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -954,4 +954,4 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
}) })
}) })
\ No newline at end of file
...@@ -650,5 +650,24 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -650,5 +650,24 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
expect(errors.field[0]).to.equal("Unexpected value or invalid argument") expect(errors.field[0]).to.equal("Unexpected value or invalid argument")
}) })
}) })
it('skips validations for the given fields', function() {
var values = ['value1', 'value2']
var Bar = this.sequelize.define('Bar' + config.rand(), {
field: {
type: Sequelize.ENUM,
values: values,
validate: {
isIn: [values]
}
}
})
var failingBar = Bar.build({ field: 'value3' })
, errors = failingBar.validate({ skip: ['field'] })
expect(errors).to.be.null
})
}) })
}) })
...@@ -193,6 +193,40 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -193,6 +193,40 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
}) })
expect(product.toJSON()).to.deep.equal({withTaxes: 1250, price: 1000, id: null}) expect(product.toJSON()).to.deep.equal({withTaxes: 1250, price: 1000, id: null})
}) })
it('should work with save', function (done) {
var Contact = this.sequelize.define('Contact', {
first: { type: Sequelize.STRING },
last: { type: Sequelize.STRING },
tags: {
type: Sequelize.STRING,
get: function(field) {
var val = this.getDataValue(field);
return JSON.parse(val);
},
set: function(val, field) {
this.setDataValue(field, JSON.stringify(val));
}
}
});
this.sequelize.sync().done(function () {
var contact = Contact.build({
first: 'My',
last: 'Name',
tags: ['yes','no']
});
expect(contact.get('tags')).to.deep.equal(['yes', 'no'])
contact.save().done(function(err, me) {
expect(err).not.to.be.ok
var idToTest = me.id;
expect(me.get('tags')).to.deep.equal(['yes', 'no'])
done();
});
});
})
}) })
describe('changed', function () { describe('changed', function () {
......
...@@ -273,6 +273,16 @@ if (Support.dialectIsMySQL()) { ...@@ -273,6 +273,16 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}], arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field`=X'53657175656c697a65';", expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field`=X'53657175656c697a65';",
context: QueryGenerator context: QueryGenerator
}, {
title: 'use != if ne !== null',
arguments: ['myTable', {where: {field: {ne: 0}}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field` != 0;",
context: QueryGenerator
}, {
title: 'use IS NOT if ne === null',
arguments: ['myTable', {where: {field: {ne: null}}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field` IS NOT NULL;",
context: QueryGenerator
} }
], ],
......
...@@ -268,8 +268,8 @@ if (dialect.match(/^postgres/)) { ...@@ -268,8 +268,8 @@ if (dialect.match(/^postgres/)) {
}, { }, {
title: 'functions can take functions as arguments', title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) { arguments: ['myTable', function (sequelize) {
return { return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']] order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
} }
}], }],
expectation: 'SELECT * FROM "myTable" ORDER BY f1(f2("id")) DESC;', expectation: 'SELECT * FROM "myTable" ORDER BY f1(f2("id")) DESC;',
...@@ -280,7 +280,7 @@ if (dialect.match(/^postgres/)) { ...@@ -280,7 +280,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', function (sequelize) { arguments: ['myTable', function (sequelize) {
return { return {
order: [ order: [
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'], [sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC'] [sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
] ]
} }
...@@ -410,6 +410,16 @@ if (dialect.match(/^postgres/)) { ...@@ -410,6 +410,16 @@ if (dialect.match(/^postgres/)) {
arguments: ['mySchema.myTable', {where: {name: "foo';DROP TABLE mySchema.myTable;"}}], arguments: ['mySchema.myTable', {where: {name: "foo';DROP TABLE mySchema.myTable;"}}],
expectation: "SELECT * FROM mySchema.myTable WHERE mySchema.myTable.name='foo'';DROP TABLE mySchema.myTable;';", expectation: "SELECT * FROM mySchema.myTable WHERE mySchema.myTable.name='foo'';DROP TABLE mySchema.myTable;';",
context: {options: {quoteIdentifiers: false}} context: {options: {quoteIdentifiers: false}}
}, {
title: 'use != if ne !== null',
arguments: ['myTable', {where: {field: {ne: 0}}}],
expectation: "SELECT * FROM myTable WHERE myTable.field != 0;",
context: {options: {quoteIdentifiers: false}}
}, {
title: 'use IS NOT if ne === null',
arguments: ['myTable', {where: {field: {ne: null}}}],
expectation: "SELECT * FROM myTable WHERE myTable.field IS NOT NULL;",
context: {options: {quoteIdentifiers: false}}
} }
], ],
......
...@@ -165,8 +165,8 @@ if (dialect === 'sqlite') { ...@@ -165,8 +165,8 @@ if (dialect === 'sqlite') {
}, { }, {
title: 'functions can take functions as arguments', title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) { arguments: ['myTable', function (sequelize) {
return { return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']] order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
} }
}], }],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(`id`)) DESC;", expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(`id`)) DESC;",
...@@ -177,7 +177,7 @@ if (dialect === 'sqlite') { ...@@ -177,7 +177,7 @@ if (dialect === 'sqlite') {
arguments: ['myTable', function (sequelize) { arguments: ['myTable', function (sequelize) {
return { return {
order: [ order: [
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'], [sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC'] [sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
] ]
} }
...@@ -260,6 +260,16 @@ if (dialect === 'sqlite') { ...@@ -260,6 +260,16 @@ if (dialect === 'sqlite') {
arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}], arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field`=X'53657175656c697a65';", expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field`=X'53657175656c697a65';",
context: QueryGenerator context: QueryGenerator
}, {
title: 'use != if ne !== null',
arguments: ['myTable', {where: {field: {ne: 0}}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field` != 0;",
context: QueryGenerator
}, {
title: 'use IS NOT if ne === null',
arguments: ['myTable', {where: {field: {ne: null}}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field` IS NOT NULL;",
context: QueryGenerator
} }
], ],
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!