assoc.exs (28816B)
1 defmodule Ecto.Integration.AssocTest do 2 use Ecto.Integration.Case, async: Application.compile_env(:ecto, :async_integration_tests, true) 3 4 alias Ecto.Integration.TestRepo 5 import Ecto.Query 6 7 alias Ecto.Integration.Custom 8 alias Ecto.Integration.Post 9 alias Ecto.Integration.User 10 alias Ecto.Integration.PostUser 11 alias Ecto.Integration.Comment 12 alias Ecto.Integration.Permalink 13 14 test "has_many assoc" do 15 p1 = TestRepo.insert!(%Post{title: "1"}) 16 p2 = TestRepo.insert!(%Post{title: "2"}) 17 18 %Comment{id: cid1} = TestRepo.insert!(%Comment{text: "1", post_id: p1.id}) 19 %Comment{id: cid2} = TestRepo.insert!(%Comment{text: "2", post_id: p1.id}) 20 %Comment{id: cid3} = TestRepo.insert!(%Comment{text: "3", post_id: p2.id}) 21 22 [c1, c2] = TestRepo.all Ecto.assoc(p1, :comments) 23 assert c1.id == cid1 24 assert c2.id == cid2 25 26 [c1, c2, c3] = TestRepo.all Ecto.assoc([p1, p2], :comments) 27 assert c1.id == cid1 28 assert c2.id == cid2 29 assert c3.id == cid3 30 end 31 32 test "has_one assoc" do 33 p1 = TestRepo.insert!(%Post{title: "1"}) 34 p2 = TestRepo.insert!(%Post{title: "2"}) 35 36 %Permalink{id: lid1} = TestRepo.insert!(%Permalink{url: "1", post_id: p1.id}) 37 %Permalink{} = TestRepo.insert!(%Permalink{url: "2"}) 38 %Permalink{id: lid3} = TestRepo.insert!(%Permalink{url: "3", post_id: p2.id}) 39 40 [l1, l3] = TestRepo.all Ecto.assoc([p1, p2], :permalink) 41 assert l1.id == lid1 42 assert l3.id == lid3 43 end 44 45 test "belongs_to assoc" do 46 %Post{id: pid1} = TestRepo.insert!(%Post{title: "1"}) 47 %Post{id: pid2} = TestRepo.insert!(%Post{title: "2"}) 48 49 l1 = TestRepo.insert!(%Permalink{url: "1", post_id: pid1}) 50 l2 = TestRepo.insert!(%Permalink{url: "2"}) 51 l3 = TestRepo.insert!(%Permalink{url: "3", post_id: pid2}) 52 53 assert [p1, p2] = TestRepo.all Ecto.assoc([l1, l2, l3], :post) 54 assert p1.id == pid1 55 assert p2.id == pid2 56 end 57 58 test "has_many through assoc" do 59 p1 = TestRepo.insert!(%Post{}) 60 p2 = TestRepo.insert!(%Post{}) 61 62 u1 = TestRepo.insert!(%User{name: "zzz"}) 63 u2 = TestRepo.insert!(%User{name: "aaa"}) 64 65 %Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id}) 66 %Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id}) 67 %Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u2.id}) 68 %Comment{} = TestRepo.insert!(%Comment{post_id: p2.id, author_id: u2.id}) 69 70 query = Ecto.assoc([p1, p2], :comments_authors) |> order_by([a], a.name) 71 assert [^u2, ^u1] = TestRepo.all(query) 72 73 # Dynamic through 74 query = Ecto.assoc([p1, p2], [:comments, :author]) |> order_by([a], a.name) 75 assert [^u2, ^u1] = TestRepo.all(query) 76 end 77 78 @tag :on_replace_nilify 79 test "has_many through-through assoc leading" do 80 p1 = TestRepo.insert!(%Post{}) 81 p2 = TestRepo.insert!(%Post{}) 82 83 u1 = TestRepo.insert!(%User{}) 84 u2 = TestRepo.insert!(%User{}) 85 86 pl1 = TestRepo.insert!(%Permalink{user_id: u1.id, url: "zzz"}) 87 pl2 = TestRepo.insert!(%Permalink{user_id: u2.id, url: "aaa"}) 88 89 %Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id}) 90 %Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id}) 91 %Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u2.id}) 92 %Comment{} = TestRepo.insert!(%Comment{post_id: p2.id, author_id: u2.id}) 93 94 query = Ecto.assoc([p1, p2], :comments_authors_permalinks) |> order_by([p], p.url) 95 assert [^pl2, ^pl1] = TestRepo.all(query) 96 97 # Dynamic through 98 query = Ecto.assoc([p1, p2], [:comments, :author, :permalink]) |> order_by([p], p.url) 99 assert [^pl2, ^pl1] = TestRepo.all(query) 100 end 101 102 test "has_many through-through assoc trailing" do 103 p1 = TestRepo.insert!(%Post{}) 104 u1 = TestRepo.insert!(%User{}) 105 pl1 = TestRepo.insert!(%Permalink{user_id: u1.id, post_id: p1.id}) 106 107 %Comment{} = TestRepo.insert!(%Comment{post_id: p1.id, author_id: u1.id}) 108 109 query = Ecto.assoc([pl1], :post_comments_authors) 110 assert [^u1] = TestRepo.all(query) 111 112 # Dynamic through 113 query = Ecto.assoc([pl1], [:post, :comments, :author]) 114 assert [^u1] = TestRepo.all(query) 115 end 116 117 test "has_many through has_many, many_to_many and has_many" do 118 user1 = %User{id: uid1} = TestRepo.insert!(%User{name: "Gabriel"}) 119 %User{id: uid2} = TestRepo.insert!(%User{name: "Isadora"}) 120 %User{id: uid3} = TestRepo.insert!(%User{name: "Joey Mush"}) 121 122 p1 = TestRepo.insert!(%Post{title: "p1", author_id: uid1}) 123 p2 = TestRepo.insert!(%Post{title: "p2", author_id: uid2}) 124 p3 = TestRepo.insert!(%Post{title: "p3", author_id: uid2}) 125 TestRepo.insert!(%Post{title: "p4", author_id: uid3}) 126 127 TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1], 128 [post_id: p1.id, user_id: uid2], 129 [post_id: p2.id, user_id: uid3]] 130 131 [pid1, pid2, pid3] = 132 Ecto.assoc(user1, :related_2nd_order_posts) 133 |> TestRepo.all() 134 |> Enum.map(fn %Post{id: id} -> id end) 135 |> Enum.sort() 136 137 assert p1.id == pid1 138 assert p2.id == pid2 139 assert p3.id == pid3 140 end 141 142 test "has_many through has_many, belongs_to and a nested has through" do 143 user1 = TestRepo.insert!(%User{name: "Gabriel"}) 144 user2 = TestRepo.insert!(%User{name: "Isadora"}) 145 user3 = TestRepo.insert!(%User{name: "Joey"}) 146 147 post1 = TestRepo.insert!(%Post{title: "p1"}) 148 post2 = TestRepo.insert!(%Post{title: "p2"}) 149 150 TestRepo.insert!(%Comment{author_id: user1.id, text: "c1", post_id: post1.id}) 151 TestRepo.insert!(%Comment{author_id: user2.id, text: "c2", post_id: post1.id}) 152 TestRepo.insert!(%Comment{author_id: user3.id, text: "c3", post_id: post2.id}) 153 154 [u1_id, u2_id] = 155 Ecto.assoc(user1, :co_commenters) 156 |> TestRepo.all() 157 |> Enum.map(fn %User{id: id} -> id end) 158 |> Enum.sort() 159 160 assert u1_id == user1.id 161 assert u2_id == user2.id 162 end 163 164 test "has_many through two many_to_many associations" do 165 user1 = %User{id: uid1} = TestRepo.insert!(%User{name: "Gabriel"}) 166 %User{id: uid2} = TestRepo.insert!(%User{name: "Isadora"}) 167 %User{id: uid3} = TestRepo.insert!(%User{name: "Joey Mush"}) 168 169 p1 = TestRepo.insert!(%Post{title: "p1", author_id: uid1}) 170 TestRepo.insert!(%Post{title: "p2", author_id: uid2}) 171 p3 = TestRepo.insert!(%Post{title: "p3", author_id: uid2}) 172 p4 = TestRepo.insert!(%Post{title: "p4", author_id: uid3}) 173 174 TestRepo.insert_all "posts_users", [[post_id: p3.id, user_id: uid1], 175 [post_id: p3.id, user_id: uid2], 176 [post_id: p1.id, user_id: uid3]] 177 178 TestRepo.insert!(%PostUser{post_id: p1.id, user_id: uid2}) 179 TestRepo.insert!(%PostUser{post_id: p3.id, user_id: uid1}) 180 TestRepo.insert!(%PostUser{post_id: p3.id, user_id: uid2}) 181 TestRepo.insert!(%PostUser{post_id: p4.id, user_id: uid3}) 182 183 [u1, u2] = 184 Ecto.assoc(user1, :users_through_schema_posts) 185 |> TestRepo.all() 186 |> Enum.map(fn %User{id: id} -> id end) 187 |> Enum.sort() 188 189 assert uid1 == u1 190 assert uid2 == u2 191 end 192 193 test "has_many through with where" do 194 post1 = TestRepo.insert!(%Post{title: "p1"}) 195 post2 = TestRepo.insert!(%Post{title: "p2"}) 196 post3 = TestRepo.insert!(%Post{title: "p3"}) 197 198 author = TestRepo.insert!(%User{name: "john"}) 199 200 TestRepo.insert!(%Comment{text: "1", lock_version: 1, post_id: post1.id, author_id: author.id}) 201 TestRepo.insert!(%Comment{text: "2", lock_version: 2, post_id: post2.id, author_id: author.id}) 202 TestRepo.insert!(%Comment{text: "3", lock_version: 2, post_id: post3.id, author_id: author.id}) 203 204 [p2, p3] = Ecto.assoc(author, :v2_comments_posts) |> TestRepo.all() |> Enum.sort_by(&(&1.id)) 205 assert p2.id == post2.id 206 assert p3.id == post3.id 207 end 208 209 test "many_to_many assoc" do 210 p1 = TestRepo.insert!(%Post{title: "1"}) 211 p2 = TestRepo.insert!(%Post{title: "2"}) 212 p3 = TestRepo.insert!(%Post{title: "3"}) 213 214 %User{id: uid1} = TestRepo.insert!(%User{name: "john"}) 215 %User{id: uid2} = TestRepo.insert!(%User{name: "mary"}) 216 217 TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: uid1], 218 [post_id: p1.id, user_id: uid2], 219 [post_id: p2.id, user_id: uid2]] 220 221 [u1, u2] = TestRepo.all Ecto.assoc([p1], :users) 222 assert u1.id == uid1 223 assert u2.id == uid2 224 225 [u2] = TestRepo.all Ecto.assoc([p2], :users) 226 assert u2.id == uid2 227 [] = TestRepo.all Ecto.assoc([p3], :users) 228 229 [u1, u2, u2] = TestRepo.all Ecto.assoc([p1, p2, p3], :users) 230 assert u1.id == uid1 231 assert u2.id == uid2 232 end 233 234 ## Changesets 235 236 test "has_one changeset assoc (on_replace: :delete)" do 237 # Insert new 238 changeset = 239 %Post{title: "1"} 240 |> Ecto.Changeset.change 241 |> Ecto.Changeset.put_assoc(:permalink, %Permalink{url: "1"}) 242 post = TestRepo.insert!(changeset) 243 assert post.permalink.id 244 assert post.permalink.post_id == post.id 245 assert post.permalink.url == "1" 246 post = TestRepo.get!(from(Post, preload: [:permalink]), post.id) 247 assert post.permalink.url == "1" 248 249 # Replace with new 250 changeset = 251 post 252 |> Ecto.Changeset.change 253 |> Ecto.Changeset.put_assoc(:permalink, %Permalink{url: "2"}) 254 post = TestRepo.update!(changeset) 255 assert post.permalink.id 256 assert post.permalink.post_id == post.id 257 assert post.permalink.url == "2" 258 post = TestRepo.get!(from(Post, preload: [:permalink]), post.id) 259 assert post.permalink.url == "2" 260 261 # Replacing with existing 262 existing = TestRepo.insert!(%Permalink{url: "3"}) 263 changeset = 264 post 265 |> Ecto.Changeset.change 266 |> Ecto.Changeset.put_assoc(:permalink, existing) 267 post = TestRepo.update!(changeset) 268 assert post.permalink.id 269 assert post.permalink.post_id == post.id 270 assert post.permalink.url == "3" 271 post = TestRepo.get!(from(Post, preload: [:permalink]), post.id) 272 assert post.permalink.url == "3" 273 274 # Replacing with nil (on_replace: :delete) 275 changeset = 276 post 277 |> Ecto.Changeset.change 278 |> Ecto.Changeset.put_assoc(:permalink, nil) 279 post = TestRepo.update!(changeset) 280 refute post.permalink 281 post = TestRepo.get!(from(Post, preload: [:permalink]), post.id) 282 refute post.permalink 283 284 assert [0] == TestRepo.all(from(p in Permalink, select: count(p.id))) 285 end 286 287 test "has_one changeset assoc (on_replace: :delete_if_exists)" do 288 permalink = TestRepo.insert!(%Permalink{url: "1"}) 289 post = TestRepo.insert!(%Post{title: "1", permalink: permalink, force_permalink: permalink}) 290 TestRepo.delete!(permalink) 291 292 assert_raise Ecto.StaleEntryError, fn -> 293 post 294 |> Ecto.Changeset.change() 295 |> Ecto.Changeset.put_assoc(:permalink, nil) 296 |> TestRepo.update!() 297 end 298 299 post = 300 post 301 |> Ecto.Changeset.change() 302 |> Ecto.Changeset.put_assoc(:force_permalink, nil) 303 |> TestRepo.update!() 304 305 assert post.force_permalink == nil 306 end 307 308 @tag :on_replace_nilify 309 test "has_one changeset assoc (on_replace: :nilify)" do 310 # Insert new 311 changeset = 312 %User{name: "1"} 313 |> Ecto.Changeset.change 314 |> Ecto.Changeset.put_assoc(:permalink, %Permalink{url: "1"}) 315 user = TestRepo.insert!(changeset) 316 assert user.permalink.id 317 assert user.permalink.user_id == user.id 318 assert user.permalink.url == "1" 319 user = TestRepo.get!(from(User, preload: [:permalink]), user.id) 320 assert user.permalink.url == "1" 321 322 # Replace with new 323 changeset = 324 user 325 |> Ecto.Changeset.change 326 |> Ecto.Changeset.put_assoc(:permalink, %Permalink{url: "2"}) 327 user = TestRepo.update!(changeset) 328 assert user.permalink.id 329 assert user.permalink.user_id == user.id 330 assert user.permalink.url == "2" 331 user = TestRepo.get!(from(User, preload: [:permalink]), user.id) 332 assert user.permalink.url == "2" 333 334 # Replacing with nil (on_replace: :nilify) 335 changeset = 336 user 337 |> Ecto.Changeset.change 338 |> Ecto.Changeset.put_assoc(:permalink, nil) 339 user = TestRepo.update!(changeset) 340 refute user.permalink 341 user = TestRepo.get!(from(User, preload: [:permalink]), user.id) 342 refute user.permalink 343 344 assert [2] == TestRepo.all(from(p in Permalink, select: count(p.id))) 345 end 346 347 @tag :on_replace_update 348 test "has_one changeset assoc (on_replace: :update)" do 349 # Insert new 350 changeset = 351 %Post{title: "1"} 352 |> Ecto.Changeset.change 353 |> Ecto.Changeset.put_assoc(:update_permalink, %Permalink{url: "1"}) 354 post = TestRepo.insert!(changeset) 355 assert post.update_permalink.id 356 assert post.update_permalink.post_id == post.id 357 assert post.update_permalink.url == "1" 358 post = TestRepo.get!(from(Post, preload: [:update_permalink]), post.id) 359 assert post.update_permalink.url == "1" 360 361 perma = post.update_permalink 362 363 # Put on update 364 changeset = 365 post 366 |> Ecto.Changeset.change() 367 |> Ecto.Changeset.put_assoc(:update_permalink, %{url: "2"}) 368 post = TestRepo.update!(changeset) 369 assert post.update_permalink.id == perma.id 370 assert post.update_permalink.post_id == post.id 371 assert post.update_permalink.url == "2" 372 post = TestRepo.get!(from(Post, preload: [:update_permalink]), post.id) 373 assert post.update_permalink.url == "2" 374 375 # Cast on update 376 changeset = 377 post 378 |> Ecto.Changeset.cast(%{update_permalink: %{url: "3"}}, []) 379 |> Ecto.Changeset.cast_assoc(:update_permalink) 380 post = TestRepo.update!(changeset) 381 assert post.update_permalink.id == perma.id 382 assert post.update_permalink.post_id == post.id 383 assert post.update_permalink.url == "3" 384 post = TestRepo.get!(from(Post, preload: [:update_permalink]), post.id) 385 assert post.update_permalink.url == "3" 386 387 # Replace with new struct 388 assert_raise RuntimeError, ~r"you are only allowed\sto update the existing entry", fn -> 389 post 390 |> Ecto.Changeset.change() 391 |> Ecto.Changeset.put_assoc(:update_permalink, %Permalink{url: "4"}) 392 end 393 394 # Replace with existing struct 395 assert_raise RuntimeError, ~r"you are only allowed\sto update the existing entry", fn -> 396 post 397 |> Ecto.Changeset.change() 398 |> Ecto.Changeset.put_assoc(:update_permalink, TestRepo.insert!(%Permalink{url: "5"})) 399 end 400 401 # Replacing with nil (on_replace: :update) 402 changeset = 403 post 404 |> Ecto.Changeset.change 405 |> Ecto.Changeset.put_assoc(:update_permalink, nil) 406 post = TestRepo.update!(changeset) 407 refute post.update_permalink 408 post = TestRepo.get!(from(Post, preload: [:update_permalink]), post.id) 409 refute post.update_permalink 410 411 assert [2] == TestRepo.all(from(p in Permalink, select: count(p.id))) 412 end 413 414 test "has_many changeset assoc (on_replace: :delete)" do 415 c1 = TestRepo.insert! %Comment{text: "1"} 416 c2 = %Comment{text: "2"} 417 418 # Inserting 419 changeset = 420 %Post{title: "1"} 421 |> Ecto.Changeset.change 422 |> Ecto.Changeset.put_assoc(:comments, [c2]) 423 post = TestRepo.insert!(changeset) 424 [c2] = post.comments 425 assert c2.id 426 assert c2.post_id == post.id 427 post = TestRepo.get!(from(Post, preload: [:comments]), post.id) 428 [c2] = post.comments 429 assert c2.text == "2" 430 431 # Updating 432 changeset = 433 post 434 |> Ecto.Changeset.change 435 |> Ecto.Changeset.put_assoc(:comments, [Ecto.Changeset.change(c1, text: "11"), 436 Ecto.Changeset.change(c2, text: "22")]) 437 post = TestRepo.update!(changeset) 438 [c1, _c2] = post.comments |> Enum.sort_by(&(&1.id)) 439 assert c1.id 440 assert c1.post_id == post.id 441 post = TestRepo.get!(from(Post, preload: [:comments]), post.id) 442 [c1, c2] = post.comments |> Enum.sort_by(&(&1.id)) 443 assert c1.text == "11" 444 assert c2.text == "22" 445 446 # Replacing (on_replace: :delete) 447 changeset = 448 post 449 |> Ecto.Changeset.change 450 |> Ecto.Changeset.put_assoc(:comments, []) 451 post = TestRepo.update!(changeset) 452 assert post.comments == [] 453 post = TestRepo.get!(from(Post, preload: [:comments]), post.id) 454 assert post.comments == [] 455 456 assert [0] == TestRepo.all(from(c in Comment, select: count(c.id))) 457 end 458 459 test "has_many changeset assoc (on_replace: :delete_if_exists)" do 460 comment = TestRepo.insert!(%Comment{text: "1"}) 461 post = TestRepo.insert!(%Post{title: "1", comments: [comment], force_comments: [comment]}) 462 463 TestRepo.delete!(comment) 464 465 assert_raise Ecto.StaleEntryError, fn -> 466 post 467 |> Ecto.Changeset.change() 468 |> Ecto.Changeset.put_assoc(:comments, []) 469 |> TestRepo.update!() 470 end 471 472 post = 473 post 474 |> Ecto.Changeset.change() 475 |> Ecto.Changeset.put_assoc(:force_comments, []) 476 |> TestRepo.update!() 477 478 assert post.force_comments == [] 479 end 480 481 test "has_many changeset assoc (on_replace: :nilify)" do 482 c1 = TestRepo.insert! %Comment{text: "1"} 483 c2 = %Comment{text: "2"} 484 485 # Inserting 486 changeset = 487 %User{name: "1"} 488 |> Ecto.Changeset.change 489 |> Ecto.Changeset.put_assoc(:comments, [c1, c2]) 490 user = TestRepo.insert!(changeset) 491 [c1, c2] = user.comments 492 assert c1.id 493 assert c1.author_id == user.id 494 assert c2.id 495 assert c2.author_id == user.id 496 user = TestRepo.get!(from(User, preload: [:comments]), user.id) 497 [c1, c2] = user.comments 498 assert c1.text == "1" 499 assert c2.text == "2" 500 501 # Replacing (on_replace: :nilify) 502 changeset = 503 user 504 |> Ecto.Changeset.change 505 |> Ecto.Changeset.put_assoc(:comments, []) 506 user = TestRepo.update!(changeset) 507 assert user.comments == [] 508 user = TestRepo.get!(from(User, preload: [:comments]), user.id) 509 assert user.comments == [] 510 511 assert [2] == TestRepo.all(from(c in Comment, select: count(c.id))) 512 end 513 514 test "many_to_many changeset assoc" do 515 u1 = TestRepo.insert! %User{name: "1"} 516 u2 = %User{name: "2"} 517 518 # Inserting 519 changeset = 520 %Post{title: "1"} 521 |> Ecto.Changeset.change 522 |> Ecto.Changeset.put_assoc(:users, [u2]) 523 post = TestRepo.insert!(changeset) 524 [u2] = post.users 525 assert u2.id 526 post = TestRepo.get!(from(Post, preload: [:users]), post.id) 527 [u2] = post.users 528 assert u2.name == "2" 529 530 assert [1] == TestRepo.all(from(j in "posts_users", select: count(j.post_id))) 531 532 # Updating 533 changeset = 534 post 535 |> Ecto.Changeset.change 536 |> Ecto.Changeset.put_assoc(:users, [Ecto.Changeset.change(u1, name: "11"), 537 Ecto.Changeset.change(u2, name: "22")]) 538 post = TestRepo.update!(changeset) 539 [u1, _u2] = post.users |> Enum.sort_by(&(&1.id)) 540 assert u1.id 541 post = TestRepo.get!(from(Post, preload: [:users]), post.id) 542 [u1, u2] = post.users |> Enum.sort_by(&(&1.id)) 543 assert u1.name == "11" 544 assert u2.name == "22" 545 546 assert [2] == TestRepo.all(from(j in "posts_users", select: count(j.post_id))) 547 548 # Replacing (on_replace: :delete) 549 changeset = 550 post 551 |> Ecto.Changeset.change 552 |> Ecto.Changeset.put_assoc(:users, []) 553 post = TestRepo.update!(changeset) 554 assert post.users == [] 555 post = TestRepo.get!(from(Post, preload: [:users]), post.id) 556 assert post.users == [] 557 558 assert [0] == TestRepo.all(from(j in "posts_users", select: count(j.post_id))) 559 assert [2] == TestRepo.all(from(c in User, select: count(c.id))) 560 end 561 562 test "many_to_many changeset assoc with schema" do 563 p1 = TestRepo.insert! %Post{title: "1"} 564 p2 = %Post{title: "2"} 565 566 # Inserting 567 changeset = 568 %User{name: "1"} 569 |> Ecto.Changeset.change 570 |> Ecto.Changeset.put_assoc(:schema_posts, [p2]) 571 user = TestRepo.insert!(changeset) 572 [p2] = user.schema_posts 573 assert p2.id 574 user = TestRepo.get!(from(User, preload: [:schema_posts]), user.id) 575 [p2] = user.schema_posts 576 assert p2.title == "2" 577 578 [up2] = TestRepo.all(PostUser) |> Enum.sort_by(&(&1.id)) 579 assert up2.post_id == p2.id 580 assert up2.user_id == user.id 581 assert up2.inserted_at 582 assert up2.updated_at 583 584 # Updating 585 changeset = 586 user 587 |> Ecto.Changeset.change 588 |> Ecto.Changeset.put_assoc(:schema_posts, [Ecto.Changeset.change(p1, title: "11"), 589 Ecto.Changeset.change(p2, title: "22")]) 590 user = TestRepo.update!(changeset) 591 [p1, _p2] = user.schema_posts |> Enum.sort_by(&(&1.id)) 592 assert p1.id 593 user = TestRepo.get!(from(User, preload: [:schema_posts]), user.id) 594 [p1, p2] = user.schema_posts |> Enum.sort_by(&(&1.id)) 595 assert p1.title == "11" 596 assert p2.title == "22" 597 598 [_up2, up1] = TestRepo.all(PostUser) |> Enum.sort_by(&(&1.id)) 599 assert up1.post_id == p1.id 600 assert up1.user_id == user.id 601 assert up1.inserted_at 602 assert up1.updated_at 603 end 604 605 test "many_to_many changeset assoc with self-referential binary_id" do 606 assoc_custom = TestRepo.insert!(%Custom{uuid: Ecto.UUID.generate()}) 607 custom = TestRepo.insert!(%Custom{customs: [assoc_custom]}) 608 609 custom = Custom |> TestRepo.get!(custom.bid) |> TestRepo.preload(:customs) 610 assert [_] = custom.customs 611 612 custom = 613 custom 614 |> Ecto.Changeset.change(%{}) 615 |> Ecto.Changeset.put_assoc(:customs, []) 616 |> TestRepo.update! 617 assert [] = custom.customs 618 619 custom = Custom |> TestRepo.get!(custom.bid) |> TestRepo.preload(:customs) 620 assert [] = custom.customs 621 end 622 623 @tag :unique_constraint 624 test "has_many changeset assoc with constraints" do 625 author = TestRepo.insert!(%User{name: "john doe"}) 626 p1 = TestRepo.insert!(%Post{title: "hello", author_id: author.id}) 627 TestRepo.insert!(%Post{title: "world", author_id: author.id}) 628 629 # Asserts that `unique_constraint` for `uuid` exists 630 assert_raise Ecto.ConstraintError, fn -> 631 TestRepo.insert!(%Post{title: "another", author_id: author.id, uuid: p1.uuid}) 632 end 633 634 author = TestRepo.preload author, [:posts] 635 posts_params = Enum.map author.posts, fn %Post{uuid: u} -> 636 %{uuid: u, title: "fresh"} 637 end 638 639 # This will only work if we delete before performing inserts 640 changeset = 641 author 642 |> Ecto.Changeset.cast(%{"posts" => posts_params}, ~w()) 643 |> Ecto.Changeset.cast_assoc(:posts) 644 author = TestRepo.update! changeset 645 assert Enum.map(author.posts, &(&1.title)) == ["fresh", "fresh"] 646 end 647 648 test "belongs_to changeset assoc" do 649 # Insert new 650 changeset = 651 %Permalink{url: "1"} 652 |> Ecto.Changeset.change 653 |> Ecto.Changeset.put_assoc(:post, %Post{title: "1"}) 654 perma = TestRepo.insert!(changeset) 655 post = perma.post 656 assert perma.post_id 657 assert perma.post_id == post.id 658 assert perma.post.title == "1" 659 660 # Replace with new 661 changeset = 662 perma 663 |> Ecto.Changeset.change 664 |> Ecto.Changeset.put_assoc(:post, %Post{title: "2"}) 665 perma = TestRepo.update!(changeset) 666 assert perma.post.id != post.id 667 post = perma.post 668 assert perma.post_id 669 assert perma.post_id == post.id 670 assert perma.post.title == "2" 671 672 # Replace with existing 673 existing = TestRepo.insert!(%Post{title: "3"}) 674 changeset = 675 perma 676 |> Ecto.Changeset.change 677 |> Ecto.Changeset.put_assoc(:post, existing) 678 perma = TestRepo.update!(changeset) 679 post = perma.post 680 assert perma.post_id == post.id 681 assert perma.post_id == existing.id 682 assert perma.post.title == "3" 683 684 # Replace with nil 685 changeset = 686 perma 687 |> Ecto.Changeset.change 688 |> Ecto.Changeset.put_assoc(:post, nil) 689 perma = TestRepo.update!(changeset) 690 assert perma.post == nil 691 assert perma.post_id == nil 692 end 693 694 test "belongs_to changeset assoc (on_replace: :update)" do 695 # Insert new 696 changeset = 697 %Permalink{url: "1"} 698 |> Ecto.Changeset.change 699 |> Ecto.Changeset.put_assoc(:update_post, %Post{title: "1"}) 700 perma = TestRepo.insert!(changeset) 701 post = perma.update_post 702 assert perma.post_id 703 assert perma.post_id == post.id 704 assert perma.update_post.title == "1" 705 706 # Casting on update 707 changeset = 708 perma 709 |> Ecto.Changeset.cast(%{update_post: %{title: "2"}}, []) 710 |> Ecto.Changeset.cast_assoc(:update_post) 711 perma = TestRepo.update!(changeset) 712 assert perma.update_post.id == post.id 713 post = perma.update_post 714 assert perma.post_id 715 assert perma.post_id == post.id 716 assert perma.update_post.title == "2" 717 718 # Replace with nil 719 changeset = 720 perma 721 |> Ecto.Changeset.change 722 |> Ecto.Changeset.put_assoc(:update_post, nil) 723 perma = TestRepo.update!(changeset) 724 assert perma.update_post == nil 725 assert perma.post_id == nil 726 end 727 728 test "inserting struct with associations" do 729 tree = %Permalink{ 730 url: "root", 731 post: %Post{ 732 title: "belongs_to", 733 comments: [ 734 %Comment{text: "child 1"}, 735 %Comment{text: "child 2"}, 736 ] 737 } 738 } 739 740 tree = TestRepo.insert!(tree) 741 assert tree.id 742 assert tree.post.id 743 assert length(tree.post.comments) == 2 744 assert Enum.all?(tree.post.comments, & &1.id) 745 746 tree = TestRepo.get!(from(Permalink, preload: [post: :comments]), tree.id) 747 assert tree.id 748 assert tree.post.id 749 assert length(tree.post.comments) == 2 750 assert Enum.all?(tree.post.comments, & &1.id) 751 end 752 753 test "inserting struct with empty associations" do 754 permalink = TestRepo.insert!(%Permalink{url: "root", post: nil}) 755 assert permalink.post == nil 756 757 post = TestRepo.insert!(%Post{title: "empty", comments: []}) 758 assert post.comments == [] 759 end 760 761 test "inserting changeset with empty cast associations" do 762 changeset = 763 %Permalink{} 764 |> Ecto.Changeset.cast(%{url: "root", post: nil}, [:url]) 765 |> Ecto.Changeset.cast_assoc(:post) 766 permalink = TestRepo.insert!(changeset) 767 assert permalink.post == nil 768 769 changeset = 770 %Post{} 771 |> Ecto.Changeset.cast(%{title: "root", comments: []}, [:title]) 772 |> Ecto.Changeset.cast_assoc(:comments) 773 post = TestRepo.insert!(changeset) 774 assert post.comments == [] 775 end 776 777 test "inserting changeset with empty put associations" do 778 changeset = 779 %Permalink{} 780 |> Ecto.Changeset.change() 781 |> Ecto.Changeset.put_assoc(:post, nil) 782 permalink = TestRepo.insert!(changeset) 783 assert permalink.post == nil 784 785 changeset = 786 %Post{} 787 |> Ecto.Changeset.change() 788 |> Ecto.Changeset.put_assoc(:comments, []) 789 post = TestRepo.insert!(changeset) 790 assert post.comments == [] 791 end 792 793 test "updating changeset with empty cast associations" do 794 post = TestRepo.insert!(%Post{}) 795 c1 = TestRepo.insert!(%Comment{post_id: post.id}) 796 c2 = TestRepo.insert!(%Comment{post_id: post.id}) 797 798 assert TestRepo.all(Comment) == [c1, c2] 799 800 post = TestRepo.get!(from(Post, preload: [:comments]), post.id) 801 802 post 803 |> Ecto.Changeset.change 804 |> Ecto.Changeset.put_assoc(:comments, []) 805 |> TestRepo.update!() 806 807 assert TestRepo.all(Comment) == [] 808 end 809 810 ## Dependent 811 812 test "has_many assoc on delete deletes all" do 813 post = TestRepo.insert!(%Post{}) 814 TestRepo.insert!(%Comment{post_id: post.id}) 815 TestRepo.insert!(%Comment{post_id: post.id}) 816 TestRepo.delete!(post) 817 818 assert TestRepo.all(Comment) == [] 819 refute Process.get(Comment) 820 end 821 822 test "has_many assoc on delete nilifies all" do 823 user = TestRepo.insert!(%User{}) 824 TestRepo.insert!(%Comment{author_id: user.id}) 825 TestRepo.insert!(%Comment{author_id: user.id}) 826 TestRepo.delete!(user) 827 828 author_ids = Comment |> TestRepo.all() |> Enum.map(fn(comment) -> comment.author_id end) 829 830 assert author_ids == [nil, nil] 831 refute Process.get(Comment) 832 end 833 834 test "has_many assoc on delete does nothing" do 835 user = TestRepo.insert!(%User{}) 836 TestRepo.insert!(%Post{author_id: user.id}) 837 838 TestRepo.delete!(user) 839 assert Enum.count(TestRepo.all(Post)) == 1 840 end 841 842 test "many_to_many assoc on delete deletes all" do 843 p1 = TestRepo.insert!(%Post{title: "1", visits: 1}) 844 p2 = TestRepo.insert!(%Post{title: "2", visits: 2}) 845 846 u1 = TestRepo.insert!(%User{name: "john"}) 847 u2 = TestRepo.insert!(%User{name: "mary"}) 848 849 TestRepo.insert_all "posts_users", [[post_id: p1.id, user_id: u1.id], 850 [post_id: p1.id, user_id: u1.id], 851 [post_id: p2.id, user_id: u2.id]] 852 TestRepo.delete!(p1) 853 854 [pid2] = TestRepo.all from(p in Post, select: p.id) 855 assert pid2 == p2.id 856 857 [[pid2, uid2]] = TestRepo.all from(j in "posts_users", select: [j.post_id, j.user_id]) 858 assert pid2 == p2.id 859 assert uid2 == u2.id 860 861 [uid1, uid2] = TestRepo.all from(u in User, select: u.id) 862 assert uid1 == u1.id 863 assert uid2 == u2.id 864 end 865 end