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

You need to sign in or sign up before continuing.
Commit 95f21071 by Mick Hansen

Merge pull request #3386 from sequelize/feat-nested-create

Nested creation
2 parents 1152e8eb 4f566ef8
# Next
- [FEATURE] Highly experimental support for nested creation [#3386](https://github.com/sequelize/sequelize/pull/3386)
# 2.0.4
- [BUG] Fixed support for 2 x belongsToMany without foreignKey defined and association getter/adder [#3185](https://github.com/sequelize/sequelize/issues/3185)
- [BUG] No longer throws on `Model.hasHook()` if no hooks are defiend [#3181](https://github.com/sequelize/sequelize/issues/3181)
......
......@@ -2,6 +2,10 @@
var Utils = require('./utils')
, Mixin = require('./associations/mixin')
, BelongsTo = require('./associations/belongs-to')
, HasOne = require('./associations/has-one')
, HasMany = require('./associations/has-many')
, BelongsToMany = require('./associations/belongs-to-many')
, InstanceValidator = require('./instance-validator')
, DataTypes = require('./data-types')
, Promise = require("./promise")
......@@ -431,7 +435,7 @@ module.exports = (function() {
if (!isEmpty) {
childOptions = {
isNewRecord: false,
isNewRecord: this.isNewRecord,
isDirty: false,
include: include.include,
includeNames: include.includeNames,
......@@ -501,7 +505,8 @@ module.exports = (function() {
var self = this
, updatedAtAttr = this.Model._timestampAttributes.updatedAt
, createdAtAttr = this.Model._timestampAttributes.createdAt
, hook = self.isNewRecord ? 'Create' : 'Update';
, hook = self.isNewRecord ? 'Create' : 'Update'
, wasNewRecord = this.isNewRecord;
if (updatedAtAttr && options.fields.indexOf(updatedAtAttr) === -1) {
options.fields.push(updatedAtAttr);
......@@ -579,6 +584,25 @@ module.exports = (function() {
}
}).then(function() {
if (!options.fields.length) return this;
if (!this.isNewRecord) return this;
if (!this.options.include || !this.options.include.length) return this;
// Nested creation for BelongsTo relations
return Promise.map(this.options.include.filter(function (include) {
return include.association instanceof BelongsTo;
}), function (include) {
var instance = self.get(include.as);
if (!instance) return Promise.resolve();
return instance.save({
transaction: options.transaction
}).then(function () {
return self[include.association.accessors.set](instance, {save: false});
});
});
})
.then(function() {
if (!options.fields.length) return this;
var values = Utils.mapValueFieldNames(this.dataValues, options.fields, this.Model)
, query = null
......@@ -638,6 +662,36 @@ module.exports = (function() {
});
self.isNewRecord = false;
return result;
})
.tap(function(result) {
if (!wasNewRecord) return;
if (!self.options.include || !self.options.include.length) return;
// Nested creation for HasOne/HasMany/BelongsToMany relations
return Promise.map(self.options.include.filter(function (include) {
return !(include.association instanceof BelongsTo);
}), function (include) {
var instances = self.get(include.as);
if (!instances) return Promise.resolve();
if (!Array.isArray(instances)) instances = [instances];
if (!instances.length) return Promise.resolve();
// Instances will be updated in place so we can safely treat HasOne like a HasMany
return Promise.map(instances, function (instance) {
if (include.association instanceof BelongsToMany) {
return instance.save({transaction: options.transaction, logging: console.log}).then(function () {
var values = {};
values[include.association.foreignKey] = self.get(self.Model.primaryKeyAttribute, {raw: true});
values[include.association.otherKey] = instance.get(instance.Model.primaryKeyAttribute, {raw: true});
return include.association.throughModel.create(values, {transaction: options.transaction, logging: options.logging});
});
} else {
instance.set(include.association.identifier, self.get(self.Model.primaryKeyAttribute, {raw: true}));
return instance.save({transaction: options.transaction, logging: console.log});
}
});
});
});
});
});
......
......@@ -1321,8 +1321,6 @@ describe(Support.getTestDialectTeaser('Instance'), function() {
user.age = user.age + 1; // happy birthday joe
user.save().done(function(err) {
expect(err).not.to.be.ok;
expect(user.username).to.equal('joe');
expect(user.age).to.equal(2);
expect(user.Projects).to.exist;
......
'use strict';
var chai = require('chai')
, Sequelize = require('../../../../index')
, expect = chai.expect
, Support = require(__dirname + '/../../support')
, dialect = Support.getTestDialect()
, DataTypes = require(__dirname + '/../../../../lib/data-types')
, Promise = Sequelize.Promise;
chai.config.includeStack = true;
describe(Support.getTestDialectTeaser('Model'), function() {
describe('create', function() {
describe('include', function() {
it('should create data for BelongsTo relations', function() {
var Product = this.sequelize.define('Product', {
title: Sequelize.STRING
});
var User = this.sequelize.define('User', {
first_name: Sequelize.STRING,
last_name: Sequelize.STRING
});
Product.belongsTo(User);
return this.sequelize.sync({ force: true }).then(function() {
return Product.create({
title: 'Chair',
User: {
first_name: 'Mick',
last_name: 'Broadstone'
}
}, {
include: [ User ]
}).then(function(savedProduct) {
return Product.findOne({
where: { id: savedProduct.id },
include: [ User ]
}).then(function(persistedProduct) {
expect(persistedProduct.User).to.be.ok;
expect(persistedProduct.User.first_name).to.be.equal('Mick');
expect(persistedProduct.User.last_name).to.be.equal('Broadstone');
});
});
});
});
it('should create data for BelongsTo relations with alias', function() {
var Product = this.sequelize.define('Product', {
title: Sequelize.STRING
});
var User = this.sequelize.define('User', {
first_name: Sequelize.STRING,
last_name: Sequelize.STRING
});
var Creator = Product.belongsTo(User, {as: 'creator'});
return this.sequelize.sync({ force: true }).then(function() {
return Product.create({
title: 'Chair',
creator: {
first_name: 'Matt',
last_name: 'Hansen'
}
}, {
include: [ Creator ]
}).then(function(savedProduct) {
return Product.findOne({
where: { id: savedProduct.id },
include: [ Creator ]
}).then(function(persistedProduct) {
expect(persistedProduct.creator).to.be.ok;
expect(persistedProduct.creator.first_name).to.be.equal('Matt');
expect(persistedProduct.creator.last_name).to.be.equal('Hansen');
});
});
});
});
it('should create data for HasMany relations', function() {
var Product = this.sequelize.define('Product', {
title: Sequelize.STRING
});
var Tag = this.sequelize.define('Tag', {
name: Sequelize.STRING
});
Product.hasMany(Tag);
return this.sequelize.sync({ force: true }).then(function() {
return Product.create({
id: 1,
title: 'Chair',
Tags: [
{id: 1, name: 'Alpha'},
{id: 2, name: 'Beta'}
]
}, {
include: [ Tag ]
}).then(function(savedProduct) {
return Product.find({
where: { id: savedProduct.id },
include: [ Tag ]
}).then(function(persistedProduct) {
expect(persistedProduct.Tags).to.be.ok;
expect(persistedProduct.Tags.length).to.equal(2);
});
});
});
});
it('should create data for HasMany relations with alias', function() {
var Product = this.sequelize.define('Product', {
title: Sequelize.STRING
});
var Tag = this.sequelize.define('Tag', {
name: Sequelize.STRING
});
var Categories = Product.hasMany(Tag, {as: 'categories'});
return this.sequelize.sync({ force: true }).then(function() {
return Product.create({
id: 1,
title: 'Chair',
categories: [
{id: 1, name: 'Alpha'},
{id: 2, name: 'Beta'}
]
}, {
include: [ Categories ]
}).then(function(savedProduct) {
return Product.find({
where: { id: savedProduct.id },
include: [ Categories ]
}).then(function(persistedProduct) {
expect(persistedProduct.categories).to.be.ok;
expect(persistedProduct.categories.length).to.equal(2);
});
});
});
});
it('should create data for HasOne relations', function() {
var User = this.sequelize.define('User', {
username: Sequelize.STRING
});
var Task = this.sequelize.define('Task', {
title: Sequelize.STRING
});
User.hasOne(Task);
return this.sequelize.sync({ force: true }).then(function() {
return User.create({
username: 'Muzzy',
Task: {
title: 'Eat Clocks'
}
}, {
include: [ Task ]
}).then(function(savedUser) {
return User.find({
where: { id: savedUser.id },
include: [ Task ]
}).then(function(persistedUser) {
expect(persistedUser.Task).to.be.ok;
});
});
});
});
it('should create data for HasOne relations with alias', function() {
var User = this.sequelize.define('User', {
username: Sequelize.STRING
});
var Task = this.sequelize.define('Task', {
title: Sequelize.STRING
});
var Job = User.hasOne(Task, {as: 'job'});
return this.sequelize.sync({ force: true }).then(function() {
return User.create({
username: 'Muzzy',
job: {
title: 'Eat Clocks'
}
}, {
include: [ Job ]
}).then(function(savedUser) {
return User.find({
where: { id: savedUser.id },
include: [ Job ]
}).then(function(persistedUser) {
expect(persistedUser.job).to.be.ok;
});
});
});
});
it('should create data for BelongsToMany relations', function() {
var User = this.sequelize.define('User', {
username: DataTypes.STRING
});
var Task = this.sequelize.define('Task', {
title: DataTypes.STRING,
active: DataTypes.BOOLEAN
});
User.belongsToMany(Task, {through: 'user_task'});
Task.belongsToMany(User, {through: 'user_task'});
return this.sequelize.sync({ force: true }).then(function() {
return User.create({
username: 'John',
Tasks: [
{ title: 'Get rich', active: true },
{ title: 'Die trying', active: false }
]
}, {
include: [ Task ]
}).then(function(savedUser) {
return User.find({
where: { id: savedUser.id },
include: [ Task ]
}).then(function(persistedUser) {
expect(persistedUser.Tasks).to.be.ok;
expect(persistedUser.Tasks.length).to.equal(2);
});
});
});
});
it('should create data for BelongsToMany relations with alias', function() {
var User = this.sequelize.define('User', {
username: DataTypes.STRING
});
var Task = this.sequelize.define('Task', {
title: DataTypes.STRING,
active: DataTypes.BOOLEAN
});
var Jobs = User.belongsToMany(Task, {through: 'user_job', as: 'jobs'});
Task.belongsToMany(User, {through: 'user_job'});
return this.sequelize.sync({ force: true }).then(function() {
return User.create({
username: 'John',
jobs: [
{ title: 'Get rich', active: true },
{ title: 'Die trying', active: false }
]
}, {
include: [ Jobs ]
}).then(function(savedUser) {
return User.find({
where: { id: savedUser.id },
include: [ Jobs ]
}).then(function(persistedUser) {
expect(persistedUser.jobs).to.be.ok;
expect(persistedUser.jobs.length).to.equal(2);
});
});
});
});
});
});
});
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!