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

Commit b31b6628 by Julian Meyer Committed by Sushant

Added feature to alter tables on sync. Fixes #537 (#7230)

* Added feature to alter tables on sync. Fixes #537

* Added an entry to future in changelog

* Added integration test for syncing with alter

* Fixed tests and model with sqlite and psql

* Updated docs and updated some code style

* Updated test.sync.js to use let instead of var

* Changed all vars to lets and replaced anonymous functions with arrow functions

* Added back some anonymous functions to use scope and fixed more coding style

* Updated code style again - removed binds and nested then's

* Updated changelog; renamed and improved tests; Added option to comment

* Changed debug vars to consts

* Switched changes to const

* Added some missing semi-colons
1 parent b53bae57
# Future
- [ADDED] `options.alter` to sequelize.sync() to alter existing tables.[#537](https://github.com/sequelize/sequelize/issues/537)
- [ADDED] Ability to run transactions on a read-replica by marking transactions as read only [#7323](https://github.com/sequelize/sequelize/issues/7323)
- [FIXED] Show a reasonable message when using renameColumn with a missing column [#6606](https://github.com/sequelize/sequelize/issues/6606)
- [PERFORMANCE] more efficient array handing for certain large queries [#7175](https://github.com/sequelize/sequelize/pull/7175)
......
......@@ -789,6 +789,7 @@ Sync all defined models to the DB.
| [options.schema='public'] | String | The schema that the tables should be created in. This can be overriden for each table in sequelize.define |
| [options.searchPath=DEFAULT] | String | An optional parameter to specify the schema search_path (Postgres only) |
| [options.hooks=true] | Boolean | If hooks is true then beforeSync, afterSync, beforBulkSync, afterBulkSync hooks will be called |
| [options.alter=false] | Boolean | Alters tables to fit models. Not recommended for production use. Deletes data in columns that were removed or had their type changed in the model. |
***
......
......@@ -1111,6 +1111,27 @@ class Model {
}
})
.then(() => this.QueryInterface.createTable(this.getTableName(options), attributes, options, this))
.then(() => {
if (options.alter) {
return this.QueryInterface.describeTable(this.getTableName(options))
.then(columns => {
const changes = []; // array of promises to run
_.each(attributes, (columnDesc, columnName) => {
if (!columns[columnName]) {
changes.push(() => this.QueryInterface.addColumn(this.getTableName(options), columnName, attributes[columnName]));
}
});
_.each(columns, (columnDesc, columnName) => {
if (!attributes[columnName]) {
changes.push(() => this.QueryInterface.removeColumn(this.getTableName(options), columnName, options));
} else if (!attributes[columnName].primaryKey) {
changes.push(() => this.QueryInterface.changeColumn(this.getTableName(options), columnName, attributes[columnName]));
}
});
return changes.reduce((p, fn) => p.then(fn), Promise.resolve());
});
}
})
.then(() => this.QueryInterface.showIndex(this.getTableName(options), options))
.then(indexes => {
// Assign an auto-generated name to indexes which are not named by the user
......
......@@ -678,6 +678,7 @@ class Sequelize {
* @param {String} [options.schema='public'] The schema that the tables should be created in. This can be overriden for each table in sequelize.define
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.hooks=true] If hooks is true then beforeSync, afterSync, beforBulkSync, afterBulkSync hooks will be called
* @param {Boolean} [options.alter=false] Alters tables to fit models. Not recommended for production use. Deletes data in columns that were removed or had their type changed in the model.
* @return {Promise}
*/
sync(options) {
......
'use strict';
/* jshint -W030 */
/* jshint -W110 */
const chai = require('chai')
, Sequelize = require('../../../index')
, expect = chai.expect
, Support = require(__dirname + '/../support');
describe(Support.getTestDialectTeaser('Model'), function() {
describe('sync', function() {
beforeEach(function() {
this.testSync = this.sequelize.define('testSync', {
dummy: Sequelize.STRING
});
return this.testSync.drop();
});
it('should remove a column if it exists in the databases schema but not the model', function() {
const User = this.sequelize.define('testSync', {
name: Sequelize.STRING,
age: Sequelize.INTEGER
});
return this.sequelize.sync()
.then(() => {
this.sequelize.define('testSync', {
name: Sequelize.STRING
});
})
.then(() => this.sequelize.sync({alter: true}))
.then(() => User.describe())
.then(data => {
expect(data).to.not.have.ownProperty('age');
expect(data).to.have.ownProperty('name');
});
});
it('should add a column if it exists in the model but not the database', function() {
const testSync = this.sequelize.define('testSync', {
name: Sequelize.STRING
});
return this.sequelize.sync()
.then(() => this.sequelize.define('testSync', {
name: Sequelize.STRING,
age: Sequelize.INTEGER
}))
.then(() => this.sequelize.sync({alter: true}))
.then(() => testSync.describe())
.then(data => expect(data).to.have.ownProperty('age'));
});
it('should change a column if it exists in the model but is different in the database', function() {
const testSync = this.sequelize.define('testSync', {
name: Sequelize.STRING,
age: Sequelize.INTEGER
});
return this.sequelize.sync()
.then(() => this.sequelize.define('testSync', {
name: Sequelize.STRING,
age: Sequelize.STRING
}))
.then(() => this.sequelize.sync({alter: true}))
.then(() => testSync.describe())
.then(data => {
expect(data).to.have.ownProperty('age');
expect(data.age.type).to.have.string('CHAR'); // CHARACTER VARYING, VARCHAR(n)
});
});
it('should not alter table if data type does not change', function() {
const testSync = this.sequelize.define('testSync', {
name: Sequelize.STRING,
age: Sequelize.STRING
});
return this.sequelize.sync()
.then(() => testSync.create({name: 'test', age: '1'}))
.then(() => this.sequelize.sync({alter: true}))
.then(() => testSync.findOne())
.then(data => {
expect(data.dataValues.name).to.eql('test');
expect(data.dataValues.age).to.eql('1');
});
});
});
});
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!