include.test.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. 'use strict';
  2. const chai = require('chai');
  3. const expect = chai.expect;
  4. const Support = require('../../support');
  5. const { DataTypes } = require('@sequelize/core');
  6. describe(Support.getTestDialectTeaser('Model'), () => {
  7. describe('bulkCreate', () => {
  8. describe('include', () => {
  9. it('should bulkCreate data for BelongsTo relations', async function () {
  10. const Product = this.sequelize.define(
  11. 'Product',
  12. {
  13. title: DataTypes.STRING,
  14. },
  15. {
  16. hooks: {
  17. afterBulkCreate(products) {
  18. for (const product of products) {
  19. product.isIncludeCreatedOnAfterCreate = Boolean(product.user && product.user.id);
  20. }
  21. },
  22. },
  23. },
  24. );
  25. const User = this.sequelize.define(
  26. 'User',
  27. {
  28. first_name: DataTypes.STRING,
  29. last_name: DataTypes.STRING,
  30. },
  31. {
  32. hooks: {
  33. beforeBulkCreate(users, options) {
  34. for (const user of users) {
  35. user.createOptions = options;
  36. }
  37. },
  38. },
  39. },
  40. );
  41. Product.belongsTo(User);
  42. await this.sequelize.sync({ force: true });
  43. const savedProducts = await Product.bulkCreate(
  44. [
  45. {
  46. title: 'Chair',
  47. user: {
  48. first_name: 'Mick',
  49. last_name: 'Broadstone',
  50. },
  51. },
  52. {
  53. title: 'Table',
  54. user: {
  55. first_name: 'John',
  56. last_name: 'Johnson',
  57. },
  58. },
  59. ],
  60. {
  61. include: [
  62. {
  63. model: User,
  64. myOption: 'option',
  65. },
  66. ],
  67. },
  68. );
  69. expect(savedProducts[0].isIncludeCreatedOnAfterCreate).to.be.true;
  70. expect(savedProducts[0].user.createOptions.myOption).to.equal('option');
  71. expect(savedProducts[1].isIncludeCreatedOnAfterCreate).to.be.true;
  72. expect(savedProducts[1].user.createOptions.myOption).to.equal('option');
  73. const persistedProducts = await Promise.all([
  74. Product.findOne({
  75. where: { id: savedProducts[0].id },
  76. include: [User],
  77. }),
  78. Product.findOne({
  79. where: { id: savedProducts[1].id },
  80. include: [User],
  81. }),
  82. ]);
  83. expect(persistedProducts[0].user).to.be.ok;
  84. expect(persistedProducts[0].user.first_name).to.equal('Mick');
  85. expect(persistedProducts[0].user.last_name).to.equal('Broadstone');
  86. expect(persistedProducts[1].user).to.be.ok;
  87. expect(persistedProducts[1].user.first_name).to.equal('John');
  88. expect(persistedProducts[1].user.last_name).to.equal('Johnson');
  89. });
  90. it('should bulkCreate data for BelongsTo relations with no nullable FK', async function () {
  91. const Product = this.sequelize.define('Product', {
  92. title: DataTypes.STRING,
  93. });
  94. const User = this.sequelize.define('User', {
  95. first_name: DataTypes.STRING,
  96. });
  97. Product.belongsTo(User, {
  98. foreignKey: {
  99. allowNull: false,
  100. },
  101. });
  102. await this.sequelize.sync({ force: true });
  103. const savedProducts = await Product.bulkCreate(
  104. [
  105. {
  106. title: 'Chair',
  107. user: {
  108. first_name: 'Mick',
  109. },
  110. },
  111. {
  112. title: 'Table',
  113. user: {
  114. first_name: 'John',
  115. },
  116. },
  117. ],
  118. {
  119. include: [
  120. {
  121. model: User,
  122. },
  123. ],
  124. },
  125. );
  126. expect(savedProducts[0]).to.exist;
  127. expect(savedProducts[0].title).to.equal('Chair');
  128. expect(savedProducts[0].user).to.exist;
  129. expect(savedProducts[0].user.first_name).to.equal('Mick');
  130. expect(savedProducts[1]).to.exist;
  131. expect(savedProducts[1].title).to.equal('Table');
  132. expect(savedProducts[1].user).to.exist;
  133. expect(savedProducts[1].user.first_name).to.equal('John');
  134. });
  135. it('should bulkCreate data for BelongsTo relations with alias', async function () {
  136. const Product = this.sequelize.define('Product', {
  137. title: DataTypes.STRING,
  138. });
  139. const User = this.sequelize.define('User', {
  140. first_name: DataTypes.STRING,
  141. last_name: DataTypes.STRING,
  142. });
  143. const Creator = Product.belongsTo(User, { as: 'creator' });
  144. await this.sequelize.sync({ force: true });
  145. const savedProducts = await Product.bulkCreate(
  146. [
  147. {
  148. title: 'Chair',
  149. creator: {
  150. first_name: 'Matt',
  151. last_name: 'Hansen',
  152. },
  153. },
  154. {
  155. title: 'Table',
  156. creator: {
  157. first_name: 'John',
  158. last_name: 'Johnson',
  159. },
  160. },
  161. ],
  162. {
  163. include: [Creator],
  164. },
  165. );
  166. const persistedProducts = await Promise.all([
  167. Product.findOne({
  168. where: { id: savedProducts[0].id },
  169. include: [Creator],
  170. }),
  171. Product.findOne({
  172. where: { id: savedProducts[1].id },
  173. include: [Creator],
  174. }),
  175. ]);
  176. expect(persistedProducts[0].creator).to.be.ok;
  177. expect(persistedProducts[0].creator.first_name).to.equal('Matt');
  178. expect(persistedProducts[0].creator.last_name).to.equal('Hansen');
  179. expect(persistedProducts[1].creator).to.be.ok;
  180. expect(persistedProducts[1].creator.first_name).to.equal('John');
  181. expect(persistedProducts[1].creator.last_name).to.equal('Johnson');
  182. });
  183. it('should bulkCreate data for HasMany relations', async function () {
  184. const Product = this.sequelize.define(
  185. 'Product',
  186. {
  187. title: DataTypes.STRING,
  188. },
  189. {
  190. hooks: {
  191. afterBulkCreate(products) {
  192. for (const product of products) {
  193. product.areIncludesCreatedOnAfterCreate =
  194. product.tags &&
  195. product.tags.every(tag => {
  196. return Boolean(tag.id);
  197. });
  198. }
  199. },
  200. },
  201. },
  202. );
  203. const Tag = this.sequelize.define(
  204. 'Tag',
  205. {
  206. name: DataTypes.STRING,
  207. },
  208. {
  209. hooks: {
  210. afterBulkCreate(tags, options) {
  211. for (const tag of tags) {
  212. tag.createOptions = options;
  213. }
  214. },
  215. },
  216. },
  217. );
  218. Product.hasMany(Tag);
  219. await this.sequelize.sync({ force: true });
  220. const savedProducts = await Product.bulkCreate(
  221. [
  222. {
  223. id: 1,
  224. title: 'Chair',
  225. tags: [
  226. { id: 1, name: 'Alpha' },
  227. { id: 2, name: 'Beta' },
  228. ],
  229. },
  230. {
  231. id: 2,
  232. title: 'Table',
  233. tags: [
  234. { id: 3, name: 'Gamma' },
  235. { id: 4, name: 'Delta' },
  236. ],
  237. },
  238. ],
  239. {
  240. include: [
  241. {
  242. model: Tag,
  243. myOption: 'option',
  244. },
  245. ],
  246. },
  247. );
  248. expect(savedProducts[0].areIncludesCreatedOnAfterCreate).to.be.true;
  249. expect(savedProducts[0].tags[0].createOptions.myOption).to.equal('option');
  250. expect(savedProducts[0].tags[1].createOptions.myOption).to.equal('option');
  251. expect(savedProducts[1].areIncludesCreatedOnAfterCreate).to.be.true;
  252. expect(savedProducts[1].tags[0].createOptions.myOption).to.equal('option');
  253. expect(savedProducts[1].tags[1].createOptions.myOption).to.equal('option');
  254. const persistedProducts = await Promise.all([
  255. Product.findOne({
  256. where: { id: savedProducts[0].id },
  257. include: [Tag],
  258. }),
  259. Product.findOne({
  260. where: { id: savedProducts[1].id },
  261. include: [Tag],
  262. }),
  263. ]);
  264. expect(persistedProducts[0].tags).to.be.ok;
  265. expect(persistedProducts[0].tags.length).to.equal(2);
  266. expect(persistedProducts[1].tags).to.be.ok;
  267. expect(persistedProducts[1].tags.length).to.equal(2);
  268. });
  269. it('should bulkCreate data for HasMany relations with alias', async function () {
  270. const Product = this.sequelize.define('Product', {
  271. title: DataTypes.STRING,
  272. });
  273. const Tag = this.sequelize.define('Tag', {
  274. name: DataTypes.STRING,
  275. });
  276. const Categories = Product.hasMany(Tag, { as: 'categories' });
  277. await this.sequelize.sync({ force: true });
  278. const savedProducts = await Product.bulkCreate(
  279. [
  280. {
  281. id: 1,
  282. title: 'Chair',
  283. categories: [
  284. { id: 1, name: 'Alpha' },
  285. { id: 2, name: 'Beta' },
  286. ],
  287. },
  288. {
  289. id: 2,
  290. title: 'Table',
  291. categories: [
  292. { id: 3, name: 'Gamma' },
  293. { id: 4, name: 'Delta' },
  294. ],
  295. },
  296. ],
  297. {
  298. include: [Categories],
  299. },
  300. );
  301. const persistedProducts = await Promise.all([
  302. Product.findOne({
  303. where: { id: savedProducts[0].id },
  304. include: [Categories],
  305. }),
  306. Product.findOne({
  307. where: { id: savedProducts[1].id },
  308. include: [Categories],
  309. }),
  310. ]);
  311. expect(persistedProducts[0].categories).to.be.ok;
  312. expect(persistedProducts[0].categories.length).to.equal(2);
  313. expect(persistedProducts[1].categories).to.be.ok;
  314. expect(persistedProducts[1].categories.length).to.equal(2);
  315. });
  316. it('should bulkCreate data for HasOne relations', async function () {
  317. const User = this.sequelize.define('User', {
  318. username: DataTypes.STRING,
  319. });
  320. const Task = this.sequelize.define('Task', {
  321. title: DataTypes.STRING,
  322. });
  323. User.hasOne(Task);
  324. await this.sequelize.sync({ force: true });
  325. const savedUsers = await User.bulkCreate(
  326. [
  327. {
  328. username: 'Muzzy',
  329. task: {
  330. title: 'Eat Clocks',
  331. },
  332. },
  333. {
  334. username: 'Walker',
  335. task: {
  336. title: 'Walk',
  337. },
  338. },
  339. ],
  340. {
  341. include: [Task],
  342. },
  343. );
  344. const persistedUsers = await Promise.all([
  345. User.findOne({
  346. where: { id: savedUsers[0].id },
  347. include: [Task],
  348. }),
  349. User.findOne({
  350. where: { id: savedUsers[1].id },
  351. include: [Task],
  352. }),
  353. ]);
  354. expect(persistedUsers[0].task).to.be.ok;
  355. expect(persistedUsers[1].task).to.be.ok;
  356. });
  357. it('should bulkCreate data for HasOne relations with alias', async function () {
  358. const User = this.sequelize.define('User', {
  359. username: DataTypes.STRING,
  360. });
  361. const Task = this.sequelize.define('Task', {
  362. title: DataTypes.STRING,
  363. });
  364. const Job = User.hasOne(Task, { as: 'job' });
  365. await this.sequelize.sync({ force: true });
  366. const savedUsers = await User.bulkCreate(
  367. [
  368. {
  369. username: 'Muzzy',
  370. job: {
  371. title: 'Eat Clocks',
  372. },
  373. },
  374. {
  375. username: 'Walker',
  376. job: {
  377. title: 'Walk',
  378. },
  379. },
  380. ],
  381. {
  382. include: [Job],
  383. },
  384. );
  385. const persistedUsers = await Promise.all([
  386. User.findOne({
  387. where: { id: savedUsers[0].id },
  388. include: [Job],
  389. }),
  390. User.findOne({
  391. where: { id: savedUsers[1].id },
  392. include: [Job],
  393. }),
  394. ]);
  395. expect(persistedUsers[0].job).to.be.ok;
  396. expect(persistedUsers[1].job).to.be.ok;
  397. });
  398. it('should bulkCreate data for BelongsToMany relations', async function () {
  399. const User = this.sequelize.define(
  400. 'User',
  401. {
  402. username: DataTypes.STRING,
  403. },
  404. {
  405. hooks: {
  406. afterBulkCreate(users) {
  407. for (const user of users) {
  408. user.areIncludesCreatedOnAfterCreate =
  409. user.tasks &&
  410. user.tasks.every(task => {
  411. return Boolean(task.id);
  412. });
  413. }
  414. },
  415. },
  416. },
  417. );
  418. const Task = this.sequelize.define(
  419. 'Task',
  420. {
  421. title: DataTypes.STRING,
  422. active: DataTypes.BOOLEAN,
  423. },
  424. {
  425. hooks: {
  426. afterBulkCreate(tasks, options) {
  427. for (const task of tasks) {
  428. task.createOptions = options;
  429. }
  430. },
  431. },
  432. },
  433. );
  434. User.belongsToMany(Task, { through: 'user_task' });
  435. Task.belongsToMany(User, { through: 'user_task' });
  436. await this.sequelize.sync({ force: true });
  437. const savedUsers = await User.bulkCreate(
  438. [
  439. {
  440. username: 'John',
  441. tasks: [
  442. { title: 'Get rich', active: true },
  443. { title: 'Die trying', active: false },
  444. ],
  445. },
  446. {
  447. username: 'Jack',
  448. tasks: [
  449. { title: 'Prepare sandwich', active: true },
  450. { title: 'Each sandwich', active: false },
  451. ],
  452. },
  453. ],
  454. {
  455. include: [
  456. {
  457. model: Task,
  458. myOption: 'option',
  459. },
  460. ],
  461. },
  462. );
  463. expect(savedUsers[0].areIncludesCreatedOnAfterCreate).to.be.true;
  464. expect(savedUsers[0].tasks[0].createOptions.myOption).to.equal('option');
  465. expect(savedUsers[0].tasks[1].createOptions.myOption).to.equal('option');
  466. expect(savedUsers[1].areIncludesCreatedOnAfterCreate).to.be.true;
  467. expect(savedUsers[1].tasks[0].createOptions.myOption).to.equal('option');
  468. expect(savedUsers[1].tasks[1].createOptions.myOption).to.equal('option');
  469. const persistedUsers = await Promise.all([
  470. User.findOne({
  471. where: { id: savedUsers[0].id },
  472. include: [Task],
  473. }),
  474. User.findOne({
  475. where: { id: savedUsers[1].id },
  476. include: [Task],
  477. }),
  478. ]);
  479. expect(persistedUsers[0].tasks).to.be.ok;
  480. expect(persistedUsers[0].tasks.length).to.equal(2);
  481. expect(persistedUsers[1].tasks).to.be.ok;
  482. expect(persistedUsers[1].tasks.length).to.equal(2);
  483. });
  484. it('should bulkCreate data for polymorphic BelongsToMany relations', async function () {
  485. const Post = this.sequelize.define(
  486. 'Post',
  487. {
  488. title: DataTypes.STRING,
  489. },
  490. {
  491. tableName: 'posts',
  492. underscored: true,
  493. },
  494. );
  495. const Tag = this.sequelize.define(
  496. 'Tag',
  497. {
  498. name: DataTypes.STRING,
  499. },
  500. {
  501. tableName: 'tags',
  502. underscored: true,
  503. },
  504. );
  505. const ItemTag = this.sequelize.define(
  506. 'ItemTag',
  507. {
  508. tag_id: {
  509. type: DataTypes.INTEGER,
  510. references: {
  511. tableName: 'tags',
  512. key: 'id',
  513. },
  514. },
  515. taggable_id: {
  516. type: DataTypes.INTEGER,
  517. references: null,
  518. },
  519. taggable: {
  520. type: DataTypes.STRING,
  521. },
  522. },
  523. {
  524. tableName: 'item_tag',
  525. underscored: true,
  526. },
  527. );
  528. Post.belongsToMany(Tag, {
  529. as: 'tags',
  530. foreignKey: 'taggable_id',
  531. otherKey: 'tag_id',
  532. foreignKeyConstraints: false,
  533. inverse: {
  534. foreignKeyConstraints: false,
  535. as: 'posts',
  536. },
  537. through: {
  538. model: ItemTag,
  539. scope: {
  540. taggable: 'post',
  541. },
  542. },
  543. });
  544. await this.sequelize.sync({ force: true });
  545. const savedPosts = await Post.bulkCreate(
  546. [
  547. {
  548. title: 'Polymorphic Associations',
  549. tags: [
  550. {
  551. name: 'polymorphic',
  552. },
  553. {
  554. name: 'associations',
  555. },
  556. ],
  557. },
  558. {
  559. title: 'Second Polymorphic Associations',
  560. tags: [
  561. {
  562. name: 'second polymorphic',
  563. },
  564. {
  565. name: 'second associations',
  566. },
  567. ],
  568. },
  569. ],
  570. {
  571. include: [
  572. {
  573. model: Tag,
  574. as: 'tags',
  575. through: {
  576. model: ItemTag,
  577. },
  578. },
  579. ],
  580. },
  581. );
  582. // The saved post should include the two tags
  583. expect(savedPosts[0].tags.length).to.equal(2);
  584. expect(savedPosts[1].tags.length).to.equal(2);
  585. // The saved post should be able to retrieve the two tags
  586. // using the convenience accessor methods
  587. const savedTagGroups = await Promise.all([
  588. savedPosts[0].getTags(),
  589. savedPosts[1].getTags(),
  590. ]);
  591. // All nested tags should be returned
  592. expect(savedTagGroups[0].length).to.equal(2);
  593. expect(savedTagGroups[1].length).to.equal(2);
  594. const itemTags = await ItemTag.findAll();
  595. // Four "through" models should be created
  596. expect(itemTags.length).to.equal(4);
  597. // And their polymorphic field should be correctly set to 'post'
  598. expect(itemTags[0].taggable).to.equal('post');
  599. expect(itemTags[1].taggable).to.equal('post');
  600. expect(itemTags[2].taggable).to.equal('post');
  601. expect(itemTags[3].taggable).to.equal('post');
  602. });
  603. it('should bulkCreate data for BelongsToMany relations with alias', async function () {
  604. const User = this.sequelize.define('User', {
  605. username: DataTypes.STRING,
  606. });
  607. const Task = this.sequelize.define('Task', {
  608. title: DataTypes.STRING,
  609. active: DataTypes.BOOLEAN,
  610. });
  611. const Jobs = User.belongsToMany(Task, { through: 'user_job', as: 'jobs' });
  612. Task.belongsToMany(User, { through: 'user_job' });
  613. await this.sequelize.sync({ force: true });
  614. const savedUsers = await User.bulkCreate(
  615. [
  616. {
  617. username: 'John',
  618. jobs: [
  619. { title: 'Get rich', active: true },
  620. { title: 'Die trying', active: false },
  621. ],
  622. },
  623. {
  624. username: 'Jack',
  625. jobs: [
  626. { title: 'Prepare sandwich', active: true },
  627. { title: 'Eat sandwich', active: false },
  628. ],
  629. },
  630. ],
  631. {
  632. include: [Jobs],
  633. },
  634. );
  635. const persistedUsers = await Promise.all([
  636. User.findOne({
  637. where: { id: savedUsers[0].id },
  638. include: [Jobs],
  639. }),
  640. User.findOne({
  641. where: { id: savedUsers[1].id },
  642. include: [Jobs],
  643. }),
  644. ]);
  645. expect(persistedUsers[0].jobs).to.be.ok;
  646. expect(persistedUsers[0].jobs.length).to.equal(2);
  647. expect(persistedUsers[1].jobs).to.be.ok;
  648. expect(persistedUsers[1].jobs.length).to.equal(2);
  649. });
  650. });
  651. });
  652. });