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

Commit ad18baff by Sanketh Katta

Map raw fields back to attribute names when using `mapToModel`.

1 parent 3cb60167
......@@ -5,6 +5,7 @@
- [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] 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
- [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 = {
query += selectFromTmp;
}
} else if (this._dialect.supports.returnValues && options.returning) {
// ensure that the return output is properly mapped to model fields.
options.mapToModel = true;
query += ' RETURNING *';
}
}
......
......@@ -548,7 +548,19 @@ AbstractQuery.prototype.isUpdateQuery = function() {
AbstractQuery.prototype.handleSelectQuery = function(results) {
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
if (this.options.raw) {
result = results.map(function(result) {
......@@ -598,7 +610,6 @@ AbstractQuery.prototype.handleSelectQuery = function(results) {
if (this.options.plain) {
result = (result.length === 0) ? null : result[0];
}
return result;
};
......
......@@ -870,6 +870,13 @@ Model.prototype.refreshAttributes = function() {
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;
......
......@@ -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 {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 {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}
*
......@@ -659,6 +661,11 @@ Sequelize.prototype.query = function(sql, options) {
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 (sql.values !== undefined) {
if (options.replacements !== undefined) {
......
......@@ -2,12 +2,20 @@
/* jshint -W030 */
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('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: {
type: DataTypes.INTEGER,
allowNull: false,
......@@ -17,20 +25,44 @@ describe(Support.getTestDialectTeaser('Model'), function() {
type: DataTypes.STRING
}
});
return Account.sync({force: true});
});
it('should only update the passed fields', function () {
return Account.create({
ownerId: 2
}).then(function (account) {
return Account.update({
name: Math.random().toString()
}, {
where: {
id: account.get('id')
}
});
});
});
return Account.sync({force: true}).then(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: Math.random().toString()
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');
});
});
});
});
}
});
});
\ No newline at end of file
});
......@@ -225,11 +225,16 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
beforeEach(function() {
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') + ', ' +
qq('updatedAt') + ") VALUES ('john', '2012-01-01 10:10:10', '2012-01-01 10:10:10')";
this.insertQuery = 'INSERT INTO ' + qq(this.User.tableName) + ' (username, email_address, ' +
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 });
});
......@@ -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() {
var self = this;
expect(function() {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!