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

Commit c0bcf5ce by Mick Hansen

Merge pull request #3995 from sankethkatta/master

Map raw field names to attributes.
2 parents 3cb60167 ad18baff
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
- [FIXED] commit/rollback multiple times on same transaction [#4491](https://github.com/sequelize/sequelize/issues/4491) - [FIXED] commit/rollback multiple times on same transaction [#4491](https://github.com/sequelize/sequelize/issues/4491)
- [FIXED] memory leak / options mangle for scopes with include [#4470](https://github.com/sequelize/sequelize/issues/4470) - [FIXED] memory leak / options mangle for scopes with include [#4470](https://github.com/sequelize/sequelize/issues/4470)
- [FIXED] custom `targetKey` for belongsTo on a target with a primary key will now correctly create foreign key constraints [#4455](https://github.com/sequelize/sequelize/issues/4455) - [FIXED] custom `targetKey` for belongsTo on a target with a primary key will now correctly create foreign key constraints [#4455](https://github.com/sequelize/sequelize/issues/4455)
- [ADDED] Map raw fields back to attributes names when using `mapToModel` or `returning` [#3995](https://github.com/sequelize/sequelize/pull/3995)
# 3.8.0 # 3.8.0
- [ADDED] `version` on `Sequelize` returning the current npm/package.json version [#4459](https://github.com/sequelize/sequelize/pull/4459) - [ADDED] `version` on `Sequelize` returning the current npm/package.json version [#4459](https://github.com/sequelize/sequelize/pull/4459)
......
...@@ -389,6 +389,8 @@ var QueryGenerator = { ...@@ -389,6 +389,8 @@ var QueryGenerator = {
query += selectFromTmp; query += selectFromTmp;
} }
} else if (this._dialect.supports.returnValues && options.returning) { } else if (this._dialect.supports.returnValues && options.returning) {
// ensure that the return output is properly mapped to model fields.
options.mapToModel = true;
query += ' RETURNING *'; query += ' RETURNING *';
} }
} }
......
...@@ -548,7 +548,19 @@ AbstractQuery.prototype.isUpdateQuery = function() { ...@@ -548,7 +548,19 @@ AbstractQuery.prototype.isUpdateQuery = function() {
AbstractQuery.prototype.handleSelectQuery = function(results) { AbstractQuery.prototype.handleSelectQuery = function(results) {
var result = null; var result = null;
// Map raw fields to names if a mapping is provided
if (this.options.fieldMap) {
var fieldMap = this.options.fieldMap;
results = Utils._.map(results, function(result) {
return Utils._.reduce(fieldMap, function(result, name, field) {
if (result[field] !== undefined) {
result[name] = result[field];
delete result[field];
}
return result;
}, result);
});
}
// Raw queries // Raw queries
if (this.options.raw) { if (this.options.raw) {
result = results.map(function(result) { result = results.map(function(result) {
...@@ -598,7 +610,6 @@ AbstractQuery.prototype.handleSelectQuery = function(results) { ...@@ -598,7 +610,6 @@ AbstractQuery.prototype.handleSelectQuery = function(results) {
if (this.options.plain) { if (this.options.plain) {
result = (result.length === 0) ? null : result[0]; result = (result.length === 0) ? null : result[0];
} }
return result; return result;
}; };
......
...@@ -870,6 +870,13 @@ Model.prototype.refreshAttributes = function() { ...@@ -870,6 +870,13 @@ Model.prototype.refreshAttributes = function() {
delete definition.index; delete definition.index;
} }
}); });
// Create a map of field to attribute names
this.fieldAttributeMap = Utils._.reduce(this.fieldRawAttributesMap, function(map, value, key) {
if (key !== value.fieldName) {
map[key] = value.fieldName;
}
return map;
}, {});
this.uniqueKeys = this.options.uniqueKeys; this.uniqueKeys = this.options.uniqueKeys;
......
...@@ -640,6 +640,8 @@ Sequelize.prototype.import = function(path) { ...@@ -640,6 +640,8 @@ Sequelize.prototype.import = function(path) {
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql. * @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Instance} [options.instance] A sequelize instance used to build the return instance * @param {Instance} [options.instance] A sequelize instance used to build the return instance
* @param {Model} [options.model] A sequelize model used to build the returned model instances (used to be called callee) * @param {Model} [options.model] A sequelize model used to build the returned model instances (used to be called callee)
* @param {Object} [options.mapToModel=false] Map returned fields to model's fields if `options.model` or `options.instance` is present. Mapping will occur before building the model instance.
* @param {Object} [options.fieldMap] Map returned fields to arbitrary names for `SELECT` query type.
* *
* @return {Promise} * @return {Promise}
* *
...@@ -659,6 +661,11 @@ Sequelize.prototype.query = function(sql, options) { ...@@ -659,6 +661,11 @@ Sequelize.prototype.query = function(sql, options) {
options.model = options.instance.Model; options.model = options.instance.Model;
} }
// Map raw fields to model field names using the `fieldAttributeMap`
if (options.model && options.mapToModel && !Utils._.isEmpty(options.model.fieldAttributeMap)) {
options.fieldMap = options.model.fieldAttributeMap;
}
if (Utils._.isPlainObject(sql)) { if (Utils._.isPlainObject(sql)) {
if (sql.values !== undefined) { if (sql.values !== undefined) {
if (options.replacements !== undefined) { if (options.replacements !== undefined) {
......
...@@ -2,12 +2,20 @@ ...@@ -2,12 +2,20 @@
/* jshint -W030 */ /* jshint -W030 */
var Support = require(__dirname + '/../support') var Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + '/../../../lib/data-types'); , DataTypes = require(__dirname + '/../../../lib/data-types')
, chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, current = Support.sequelize
, _ = require('lodash');
describe(Support.getTestDialectTeaser('Model'), function() { describe(Support.getTestDialectTeaser('Model'), function() {
describe('update', function () { describe('update', function () {
it('should only update the passed fields', function () {
var Account = this.sequelize.define('Account', { var Account;
beforeEach(function() {
Account = this.sequelize.define('Account', {
ownerId: { ownerId: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
...@@ -17,8 +25,10 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -17,8 +25,10 @@ describe(Support.getTestDialectTeaser('Model'), function() {
type: DataTypes.STRING type: DataTypes.STRING
} }
}); });
return Account.sync({force: true});
});
return Account.sync({force: true}).then(function () { it('should only update the passed fields', function () {
return Account.create({ return Account.create({
ownerId: 2 ownerId: 2
}).then(function (account) { }).then(function (account) {
...@@ -31,6 +41,28 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -31,6 +41,28 @@ describe(Support.getTestDialectTeaser('Model'), function() {
}); });
}); });
}); });
if (_.get(current.dialect.supports, 'returnValues.returning')) {
it('should return the updated record', function () {
return Account.create({
ownerId: 2
}).then(function (account) {
return Account.update({
name: 'FooBar'
}, {
where: {
id: account.get('id')
},
returning: true
}).spread(function(count, accounts) {
var account = accounts[0];
expect(account.ownerId).to.be.equal(2);
expect(account.name).to.be.equal('FooBar');
}); });
}); });
});
}
});
}); });
...@@ -225,11 +225,16 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() { ...@@ -225,11 +225,16 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
beforeEach(function() { beforeEach(function() {
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
username: DataTypes.STRING username: DataTypes.STRING,
emailAddress: {
type: DataTypes.STRING,
field: 'email_address'
}
}); });
this.insertQuery = 'INSERT INTO ' + qq(this.User.tableName) + ' (username, ' + qq('createdAt') + ', ' + this.insertQuery = 'INSERT INTO ' + qq(this.User.tableName) + ' (username, email_address, ' +
qq('updatedAt') + ") VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')"; qq('createdAt') + ', ' + qq('updatedAt') +
") VALUES ('john', 'john@gmail.com', '2012-01-01 10:10:10', '2012-01-01 10:10:10')";
return this.User.sync({ force: true }); return this.User.sync({ force: true });
}); });
...@@ -314,6 +319,29 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() { ...@@ -314,6 +319,29 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
}); });
}); });
it('maps the field names to attributes based on the passed model', function() {
return this.sequelize.query(this.insertQuery).bind(this).then(function() {
return this.sequelize.query('SELECT * FROM ' + qq(this.User.tableName) + ';', {
model: this.User,
mapToModel: true
});
}).then(function(users) {
expect(users[0].emailAddress).to.be.equal('john@gmail.com');
});
});
it('arbitrarily map the field names', function() {
return this.sequelize.query(this.insertQuery).bind(this).then(function() {
return this.sequelize.query('SELECT * FROM ' + qq(this.User.tableName) + ';', {
type: 'SELECT',
fieldMap: {username: 'userName', email_address: 'email'}
});
}).then(function(users) {
expect(users[0].userName).to.be.equal('john');
expect(users[0].email).to.be.equal('john@gmail.com');
});
});
it('throw an exception if `values` and `options.replacements` are both passed', function() { it('throw an exception if `values` and `options.replacements` are both passed', function() {
var self = this; var self = this;
expect(function() { expect(function() {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!