include.test.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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('create', () => {
  8. describe('include', () => {
  9. it('should create data for BelongsTo relations', async function () {
  10. const Product = this.sequelize.define(
  11. 'Product',
  12. {
  13. title: DataTypes.STRING,
  14. },
  15. {
  16. hooks: {
  17. afterCreate(product) {
  18. product.isIncludeCreatedOnAfterCreate = Boolean(product.user && product.user.id);
  19. },
  20. },
  21. },
  22. );
  23. const User = this.sequelize.define(
  24. 'User',
  25. {
  26. first_name: DataTypes.STRING,
  27. last_name: DataTypes.STRING,
  28. },
  29. {
  30. hooks: {
  31. beforeCreate(user, options) {
  32. user.createOptions = options;
  33. },
  34. },
  35. },
  36. );
  37. Product.belongsTo(User);
  38. await this.sequelize.sync({ force: true });
  39. const savedProduct = await Product.create(
  40. {
  41. title: 'Chair',
  42. user: {
  43. first_name: 'Mick',
  44. last_name: 'Broadstone',
  45. },
  46. },
  47. {
  48. include: [
  49. {
  50. model: User,
  51. myOption: 'option',
  52. },
  53. ],
  54. },
  55. );
  56. expect(savedProduct.id).to.be.a('number');
  57. expect(savedProduct.isIncludeCreatedOnAfterCreate).to.be.true;
  58. expect(savedProduct.user.createOptions.myOption).to.equal('option');
  59. expect(savedProduct.user.createOptions.parentRecord).to.equal(savedProduct);
  60. const persistedProduct = await Product.findOne({
  61. where: { id: savedProduct.id },
  62. include: [User],
  63. });
  64. expect(persistedProduct.user).to.be.ok;
  65. expect(persistedProduct.user.first_name).to.equal('Mick');
  66. expect(persistedProduct.user.last_name).to.equal('Broadstone');
  67. });
  68. it('should create data for BelongsTo relations with no nullable FK', async function () {
  69. const Product = this.sequelize.define('Product', {
  70. title: DataTypes.STRING,
  71. });
  72. const User = this.sequelize.define('User', {
  73. first_name: DataTypes.STRING,
  74. });
  75. Product.belongsTo(User, {
  76. foreignKey: {
  77. allowNull: false,
  78. },
  79. });
  80. await this.sequelize.sync({ force: true });
  81. const savedProduct = await Product.create(
  82. {
  83. title: 'Chair',
  84. user: {
  85. first_name: 'Mick',
  86. },
  87. },
  88. {
  89. include: [
  90. {
  91. model: User,
  92. },
  93. ],
  94. },
  95. );
  96. expect(savedProduct.id).to.be.a('number');
  97. expect(savedProduct).to.exist;
  98. expect(savedProduct.title).to.equal('Chair');
  99. expect(savedProduct.user).to.exist;
  100. expect(savedProduct.user.first_name).to.equal('Mick');
  101. });
  102. it('should create data for BelongsTo relations with alias', async function () {
  103. const Product = this.sequelize.define('Product', {
  104. title: DataTypes.STRING,
  105. });
  106. const User = this.sequelize.define('User', {
  107. first_name: DataTypes.STRING,
  108. last_name: DataTypes.STRING,
  109. });
  110. const Creator = Product.belongsTo(User, { as: 'creator' });
  111. await this.sequelize.sync({ force: true });
  112. const savedProduct = await Product.create(
  113. {
  114. title: 'Chair',
  115. creator: {
  116. first_name: 'Matt',
  117. last_name: 'Hansen',
  118. },
  119. },
  120. {
  121. include: [Creator],
  122. },
  123. );
  124. const persistedProduct = await Product.findOne({
  125. where: { id: savedProduct.id },
  126. include: [Creator],
  127. });
  128. expect(savedProduct.id).to.be.a('number');
  129. expect(persistedProduct.creator).to.be.ok;
  130. expect(persistedProduct.creator.first_name).to.equal('Matt');
  131. expect(persistedProduct.creator.last_name).to.equal('Hansen');
  132. });
  133. it('should create data for HasMany relations', async function () {
  134. const Product = this.sequelize.define(
  135. 'Product',
  136. {
  137. title: DataTypes.STRING,
  138. },
  139. {
  140. hooks: {
  141. afterCreate(product) {
  142. product.areIncludesCreatedOnAfterCreate =
  143. product.tags &&
  144. product.tags.every(tag => {
  145. return Boolean(tag.id);
  146. });
  147. },
  148. },
  149. },
  150. );
  151. const Tag = this.sequelize.define(
  152. 'Tag',
  153. {
  154. name: DataTypes.STRING,
  155. },
  156. {
  157. hooks: {
  158. afterCreate(tag, options) {
  159. tag.createOptions = options;
  160. },
  161. },
  162. },
  163. );
  164. Product.hasMany(Tag);
  165. await this.sequelize.sync({ force: true });
  166. const savedProduct = await Product.create(
  167. {
  168. id: 1,
  169. title: 'Chair',
  170. tags: [
  171. { id: 1, name: 'Alpha' },
  172. { id: 2, name: 'Beta' },
  173. ],
  174. },
  175. {
  176. include: [
  177. {
  178. model: Tag,
  179. myOption: 'option',
  180. },
  181. ],
  182. },
  183. );
  184. expect(savedProduct.id).to.be.a('number');
  185. expect(savedProduct.areIncludesCreatedOnAfterCreate).to.be.true;
  186. expect(savedProduct.tags[0].createOptions.myOption).to.equal('option');
  187. expect(savedProduct.tags[0].createOptions.parentRecord).to.equal(savedProduct);
  188. expect(savedProduct.tags[1].createOptions.myOption).to.equal('option');
  189. expect(savedProduct.tags[1].createOptions.parentRecord).to.equal(savedProduct);
  190. const persistedProduct = await Product.findOne({
  191. where: { id: savedProduct.id },
  192. include: [Tag],
  193. });
  194. expect(persistedProduct.tags).to.be.ok;
  195. expect(persistedProduct.tags.length).to.equal(2);
  196. });
  197. it('should create data for HasMany relations with alias', async function () {
  198. const Product = this.sequelize.define('Product', {
  199. title: DataTypes.STRING,
  200. });
  201. const Tag = this.sequelize.define('Tag', {
  202. name: DataTypes.STRING,
  203. });
  204. const Categories = Product.hasMany(Tag, { as: 'categories' });
  205. await this.sequelize.sync({ force: true });
  206. const savedProduct = await Product.create(
  207. {
  208. id: 1,
  209. title: 'Chair',
  210. categories: [
  211. { id: 1, name: 'Alpha' },
  212. { id: 2, name: 'Beta' },
  213. ],
  214. },
  215. {
  216. include: [Categories],
  217. },
  218. );
  219. const persistedProduct = await Product.findOne({
  220. where: { id: savedProduct.id },
  221. include: [Categories],
  222. });
  223. expect(persistedProduct.categories).to.be.ok;
  224. expect(persistedProduct.categories.length).to.equal(2);
  225. });
  226. it('should create data for HasOne relations', async function () {
  227. const User = this.sequelize.define('User', {
  228. username: DataTypes.STRING,
  229. });
  230. const Task = this.sequelize.define('Task', {
  231. title: DataTypes.STRING,
  232. });
  233. User.hasOne(Task);
  234. await this.sequelize.sync({ force: true });
  235. const savedUser = await User.create(
  236. {
  237. username: 'Muzzy',
  238. task: {
  239. title: 'Eat Clocks',
  240. },
  241. },
  242. {
  243. include: [Task],
  244. },
  245. );
  246. const persistedUser = await User.findOne({
  247. where: { id: savedUser.id },
  248. include: [Task],
  249. });
  250. expect(persistedUser.task).to.be.ok;
  251. });
  252. it('should create data for HasOne relations with alias', async function () {
  253. const User = this.sequelize.define('User', {
  254. username: DataTypes.STRING,
  255. });
  256. const Task = this.sequelize.define('Task', {
  257. title: DataTypes.STRING,
  258. });
  259. const Job = User.hasOne(Task, { as: 'job' });
  260. await this.sequelize.sync({ force: true });
  261. const savedUser = await User.create(
  262. {
  263. username: 'Muzzy',
  264. job: {
  265. title: 'Eat Clocks',
  266. },
  267. },
  268. {
  269. include: [Job],
  270. },
  271. );
  272. const persistedUser = await User.findOne({
  273. where: { id: savedUser.id },
  274. include: [Job],
  275. });
  276. expect(persistedUser.job).to.be.ok;
  277. });
  278. it('should create data for BelongsToMany relations', async function () {
  279. const User = this.sequelize.define(
  280. 'User',
  281. {
  282. username: DataTypes.STRING,
  283. },
  284. {
  285. hooks: {
  286. afterCreate(user) {
  287. user.areIncludesCreatedOnAfterCreate =
  288. user.tasks &&
  289. user.tasks.every(task => {
  290. return Boolean(task.id);
  291. });
  292. },
  293. },
  294. },
  295. );
  296. const Task = this.sequelize.define(
  297. 'Task',
  298. {
  299. title: DataTypes.STRING,
  300. active: DataTypes.BOOLEAN,
  301. },
  302. {
  303. hooks: {
  304. afterCreate(task, options) {
  305. task.createOptions = options;
  306. },
  307. },
  308. },
  309. );
  310. User.belongsToMany(Task, { through: 'user_task' });
  311. Task.belongsToMany(User, { through: 'user_task' });
  312. await this.sequelize.sync({ force: true });
  313. const savedUser = await User.create(
  314. {
  315. username: 'John',
  316. tasks: [
  317. { title: 'Get rich', active: true },
  318. { title: 'Die trying', active: false },
  319. ],
  320. },
  321. {
  322. include: [
  323. {
  324. model: Task,
  325. myOption: 'option',
  326. },
  327. ],
  328. },
  329. );
  330. expect(savedUser.areIncludesCreatedOnAfterCreate).to.be.true;
  331. expect(savedUser.tasks[0].createOptions.myOption).to.equal('option');
  332. expect(savedUser.tasks[0].createOptions.parentRecord).to.equal(savedUser);
  333. expect(savedUser.tasks[1].createOptions.myOption).to.equal('option');
  334. expect(savedUser.tasks[1].createOptions.parentRecord).to.equal(savedUser);
  335. const persistedUser = await User.findOne({
  336. where: { id: savedUser.id },
  337. include: [Task],
  338. });
  339. expect(persistedUser.tasks).to.be.ok;
  340. expect(persistedUser.tasks.length).to.equal(2);
  341. });
  342. it('should create data for polymorphic BelongsToMany relations', async function () {
  343. const Post = this.sequelize.define(
  344. 'Post',
  345. {
  346. title: DataTypes.STRING,
  347. },
  348. {
  349. tableName: 'posts',
  350. underscored: true,
  351. },
  352. );
  353. const Tag = this.sequelize.define(
  354. 'Tag',
  355. {
  356. name: DataTypes.STRING,
  357. },
  358. {
  359. tableName: 'tags',
  360. underscored: true,
  361. },
  362. );
  363. const ItemTag = this.sequelize.define(
  364. 'ItemTag',
  365. {
  366. tag_id: {
  367. type: DataTypes.INTEGER,
  368. references: {
  369. table: 'tags',
  370. key: 'id',
  371. },
  372. },
  373. taggable_id: {
  374. type: DataTypes.INTEGER,
  375. references: null,
  376. },
  377. taggable: {
  378. type: DataTypes.STRING,
  379. },
  380. },
  381. {
  382. tableName: 'item_tag',
  383. underscored: true,
  384. },
  385. );
  386. Post.belongsToMany(Tag, {
  387. as: 'tags',
  388. foreignKey: 'taggable_id',
  389. otherKey: 'tag_id',
  390. foreignKeyConstraints: false,
  391. inverse: {
  392. as: 'posts',
  393. foreignKeyConstraints: false,
  394. },
  395. through: {
  396. model: ItemTag,
  397. scope: {
  398. taggable: 'post',
  399. },
  400. },
  401. });
  402. await this.sequelize.sync({ force: true });
  403. const savedPost = await Post.create(
  404. {
  405. title: 'Polymorphic Associations',
  406. tags: [
  407. {
  408. name: 'polymorphic',
  409. },
  410. {
  411. name: 'associations',
  412. },
  413. ],
  414. },
  415. {
  416. include: [
  417. {
  418. model: Tag,
  419. as: 'tags',
  420. through: {
  421. model: ItemTag,
  422. },
  423. },
  424. ],
  425. },
  426. );
  427. // The saved post should include the two tags
  428. expect(savedPost.tags.length).to.equal(2);
  429. // The saved post should be able to retrieve the two tags
  430. // using the convenience accessor methods
  431. const savedTags = await savedPost.getTags();
  432. // All nested tags should be returned
  433. expect(savedTags.length).to.equal(2);
  434. const itemTags = await ItemTag.findAll();
  435. // Two "through" models should be created
  436. expect(itemTags.length).to.equal(2);
  437. // And their polymorphic field should be correctly set to 'post'
  438. expect(itemTags[0].taggable).to.equal('post');
  439. expect(itemTags[1].taggable).to.equal('post');
  440. });
  441. it('should create data for BelongsToMany relations with alias', async function () {
  442. const User = this.sequelize.define('User', {
  443. username: DataTypes.STRING,
  444. });
  445. const Task = this.sequelize.define('Task', {
  446. title: DataTypes.STRING,
  447. active: DataTypes.BOOLEAN,
  448. });
  449. const Jobs = User.belongsToMany(Task, { through: 'user_job', as: 'jobs' });
  450. Task.belongsToMany(User, { through: 'user_job' });
  451. await this.sequelize.sync({ force: true });
  452. const savedUser = await User.create(
  453. {
  454. username: 'John',
  455. jobs: [
  456. { title: 'Get rich', active: true },
  457. { title: 'Die trying', active: false },
  458. ],
  459. },
  460. {
  461. include: [Jobs],
  462. },
  463. );
  464. const persistedUser = await User.findOne({
  465. where: { id: savedUser.id },
  466. include: [Jobs],
  467. });
  468. expect(persistedUser.jobs).to.be.ok;
  469. expect(persistedUser.jobs.length).to.equal(2);
  470. });
  471. });
  472. });
  473. });