1use crate::prelude::*;
2
3#[driver_test(id(ID), scenario(crate::scenarios::has_one_optional_belongs_to))]
6pub async fn preload_has_one_option_none_then_some(test: &mut Test) -> Result<()> {
7 let mut db = setup(test).await;
8
9 let user_no_profile = User::create().name("No Profile").exec(&mut db).await?;
11
12 let user_no_profile = User::filter_by_id(user_no_profile.id)
14 .include(User::fields().profile())
15 .get(&mut db)
16 .await?;
17
18 assert!(user_no_profile.profile.get().is_none());
20
21 let user_with_profile = User::create()
23 .name("Has Profile")
24 .profile(Profile::create().bio("A bio"))
25 .exec(&mut db)
26 .await?;
27
28 let user_with_profile = User::filter_by_id(user_with_profile.id)
30 .include(User::fields().profile())
31 .get(&mut db)
32 .await?;
33
34 let profile = user_with_profile.profile.get().as_ref().unwrap();
35 assert_eq!("A bio", profile.bio);
36 assert_eq!(user_with_profile.id, *profile.user_id.as_ref().unwrap());
37
38 Ok(())
39}
40
41#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
42pub async fn basic_has_many_and_belongs_to_preload(test: &mut Test) -> Result<()> {
43 let mut db = setup(test).await;
44
45 let user = User::create()
47 .name("Alice")
48 .todos([Todo::create().title("todo 1")])
49 .todos([Todo::create().title("todo 2")])
50 .todos([Todo::create().title("todo 3")])
51 .exec(&mut db)
52 .await?;
53
54 let user = User::filter_by_id(user.id)
56 .include(User::fields().todos())
57 .get(&mut db)
58 .await?;
59
60 assert_eq!(3, user.todos.get().len());
62
63 let id = user.todos.get()[0].id;
64
65 let todo = Todo::filter_by_id(id)
66 .include(Todo::fields().user())
67 .get(&mut db)
68 .await?;
69
70 assert_eq!(user.id, todo.user.get().id);
71 assert_eq!(user.id, todo.user_id);
72 Ok(())
73}
74
75#[driver_test(id(ID), scenario(crate::scenarios::user_two_children))]
76pub async fn multiple_includes_same_model(test: &mut Test) -> Result<()> {
77 let mut db = setup(test).await;
78
79 let user = User::create().name("Test User").exec(&mut db).await?;
81
82 Post::create()
84 .title("Post 1")
85 .user(&user)
86 .exec(&mut db)
87 .await?;
88
89 Post::create()
90 .title("Post 2")
91 .user(&user)
92 .exec(&mut db)
93 .await?;
94
95 Comment::create()
97 .text("Comment 1")
98 .user(&user)
99 .exec(&mut db)
100 .await?;
101
102 Comment::create()
103 .text("Comment 2")
104 .user(&user)
105 .exec(&mut db)
106 .await?;
107
108 Comment::create()
109 .text("Comment 3")
110 .user(&user)
111 .exec(&mut db)
112 .await?;
113
114 let user_with_posts = User::filter_by_id(user.id)
116 .include(User::fields().posts())
117 .get(&mut db)
118 .await?;
119 assert_eq!(2, user_with_posts.posts.get().len());
120
121 let user_with_comments = User::filter_by_id(user.id)
122 .include(User::fields().comments())
123 .get(&mut db)
124 .await?;
125 assert_eq!(3, user_with_comments.comments.get().len());
126
127 let loaded_user = User::filter_by_id(user.id)
129 .include(User::fields().posts()) .include(User::fields().comments()) .get(&mut db)
132 .await?;
133
134 assert_eq!(2, loaded_user.posts.get().len());
135 assert_eq!(3, loaded_user.comments.get().len());
136 Ok(())
137}
138
139#[driver_test(id(ID), scenario(crate::scenarios::has_one_optional_belongs_to))]
140pub async fn basic_has_one_and_belongs_to_preload(test: &mut Test) -> Result<()> {
141 let mut db = setup(test).await;
142
143 let user = User::create()
145 .name("John Doe")
146 .profile(Profile::create().bio("A person"))
147 .exec(&mut db)
148 .await?;
149
150 let user = User::filter_by_id(user.id)
152 .include(User::fields().profile())
153 .get(&mut db)
154 .await?;
155
156 let profile = user.profile.get().as_ref().unwrap();
158 assert_eq!("A person", profile.bio);
159 assert_eq!(user.id, *profile.user_id.as_ref().unwrap());
160
161 let profile_id = profile.id;
162
163 let profile = Profile::filter_by_id(profile_id)
165 .include(Profile::fields().user())
166 .get(&mut db)
167 .await?;
168
169 assert_eq!(user.id, profile.user.get().as_ref().unwrap().id);
170 assert_eq!("John Doe", profile.user.get().as_ref().unwrap().name);
171 Ok(())
172}
173
174#[driver_test(id(ID), scenario(crate::scenarios::user_profile_settings))]
175pub async fn multiple_includes_with_has_one(test: &mut Test) -> Result<()> {
176 let mut db = setup(test).await;
177
178 let user = User::create()
180 .name("Jane Doe")
181 .profile(Profile::create().bio("Software engineer"))
182 .settings(Settings::create().theme("dark"))
183 .exec(&mut db)
184 .await?;
185
186 let user_with_profile = User::filter_by_id(user.id)
188 .include(User::fields().profile())
189 .get(&mut db)
190 .await?;
191 assert!(user_with_profile.profile.get().is_some());
192 assert_eq!(
193 "Software engineer",
194 user_with_profile.profile.get().as_ref().unwrap().bio
195 );
196
197 let user_with_settings = User::filter_by_id(user.id)
198 .include(User::fields().settings())
199 .get(&mut db)
200 .await?;
201 assert!(user_with_settings.settings.get().is_some());
202 assert_eq!(
203 "dark",
204 user_with_settings.settings.get().as_ref().unwrap().theme
205 );
206
207 let loaded_user = User::filter_by_id(user.id)
209 .include(User::fields().profile()) .include(User::fields().settings()) .get(&mut db)
212 .await?;
213
214 assert!(loaded_user.profile.get().is_some());
215 assert_eq!(
216 "Software engineer",
217 loaded_user.profile.get().as_ref().unwrap().bio
218 );
219 assert!(loaded_user.settings.get().is_some());
220 assert_eq!("dark", loaded_user.settings.get().as_ref().unwrap().theme);
221 Ok(())
222}
223
224#[driver_test(id(ID), scenario(crate::scenarios::user_profile_and_todos))]
225pub async fn combined_has_many_and_has_one_preload(test: &mut Test) -> Result<()> {
226 let mut db = setup(test).await;
227
228 let user = User::create()
230 .name("Bob Smith")
231 .profile(Profile::create().bio("Developer"))
232 .todos([Todo::create().title("Task 1")])
233 .todos([Todo::create().title("Task 2")])
234 .todos([Todo::create().title("Task 3")])
235 .exec(&mut db)
236 .await?;
237
238 let loaded_user = User::filter_by_id(user.id)
240 .include(User::fields().profile()) .include(User::fields().todos()) .get(&mut db)
243 .await?;
244
245 assert!(loaded_user.profile.get().is_some());
247 assert_eq!("Developer", loaded_user.profile.get().as_ref().unwrap().bio);
248
249 assert_eq!(3, loaded_user.todos.get().len());
251 let todo_titles: Vec<&str> = loaded_user
252 .todos
253 .get()
254 .iter()
255 .map(|t| t.title.as_str())
256 .collect();
257 assert!(todo_titles.contains(&"Task 1"));
258 assert!(todo_titles.contains(&"Task 2"));
259 assert!(todo_titles.contains(&"Task 3"));
260 Ok(())
261}
262
263#[driver_test(
264 id(ID),
265 requires(scan),
266 scenario(crate::scenarios::has_many_belongs_to)
267)]
268pub async fn preload_on_empty_table(test: &mut Test) -> Result<()> {
269 let mut db = setup(test).await;
270
271 let users: Vec<User> = User::all()
273 .include(User::fields().todos())
274 .exec(&mut db)
275 .await?;
276
277 assert_eq!(0, users.len());
278 Ok(())
279}
280
281#[driver_test(id(ID), scenario(crate::scenarios::has_many_belongs_to))]
282pub async fn preload_on_empty_query(test: &mut Test) -> Result<()> {
283 let mut db = setup(test).await;
284
285 let users: Vec<User> = User::filter_by_name("foo")
287 .include(User::fields().todos())
288 .exec(&mut db)
289 .await?;
290
291 assert_eq!(0, users.len());
292 Ok(())
293}
294
295#[driver_test(id(ID), scenario(crate::scenarios::has_many_nullable_fk))]
298pub async fn preload_has_many_with_optional_belongs_to(test: &mut Test) -> Result<()> {
299 let mut db = setup(test).await;
300
301 let user = User::create()
303 .todos([Todo::create().title("Task 1")])
304 .todos([Todo::create().title("Task 2")])
305 .exec(&mut db)
306 .await?;
307
308 let user = User::filter_by_id(user.id)
310 .include(User::fields().todos())
311 .get(&mut db)
312 .await?;
313
314 assert_eq!(2, user.todos.get().len());
315
316 let todo_id = user.todos.get()[0].id;
317
318 let todo = Todo::filter_by_id(todo_id)
320 .include(Todo::fields().user())
321 .get(&mut db)
322 .await?;
323
324 assert_eq!(user.id, todo.user.get().as_ref().unwrap().id);
325
326 let orphan = Todo::create().title("Orphan").exec(&mut db).await?;
328
329 let orphan = Todo::filter_by_id(orphan.id)
331 .include(Todo::fields().user())
332 .get(&mut db)
333 .await?;
334
335 assert!(orphan.user.get().is_none());
336
337 Ok(())
338}
339
340#[driver_test(id(ID))]
344pub async fn preload_has_one_optional_with_required_belongs_to(test: &mut Test) -> Result<()> {
345 #[derive(Debug, toasty::Model)]
346 struct User {
347 #[key]
348 #[auto]
349 id: ID,
350
351 name: String,
352
353 #[has_one]
354 profile: toasty::Deferred<Option<Profile>>,
355 }
356
357 #[derive(Debug, toasty::Model)]
358 struct Profile {
359 #[key]
360 #[auto]
361 id: ID,
362
363 bio: String,
364
365 #[unique]
366 user_id: ID,
367
368 #[belongs_to(key = user_id, references = id)]
369 user: toasty::Deferred<User>,
370 }
371
372 let mut db = test.setup_db(models!(User, Profile)).await;
373
374 let user_with = User::create()
376 .name("Has Profile")
377 .profile(Profile::create().bio("hello"))
378 .exec(&mut db)
379 .await?;
380
381 let user_without = User::create().name("No Profile").exec(&mut db).await?;
383
384 let loaded = User::filter_by_id(user_with.id)
386 .include(User::fields().profile())
387 .get(&mut db)
388 .await?;
389
390 let profile = loaded.profile.get().as_ref().unwrap();
391 assert_eq!("hello", profile.bio);
392 assert_eq!(user_with.id, profile.user_id);
393
394 let loaded = User::filter_by_id(user_without.id)
396 .include(User::fields().profile())
397 .get(&mut db)
398 .await?;
399
400 assert!(loaded.profile.get().is_none());
401
402 let profile = Profile::filter_by_user_id(user_with.id)
404 .include(Profile::fields().user())
405 .get(&mut db)
406 .await?;
407
408 assert_eq!(user_with.id, profile.user.get().id);
409 assert_eq!("Has Profile", profile.user.get().name);
410
411 Ok(())
412}
413
414#[driver_test(id(ID))]
418pub async fn preload_has_one_required_with_optional_belongs_to(test: &mut Test) -> Result<()> {
419 #[derive(Debug, toasty::Model)]
420 struct User {
421 #[key]
422 #[auto]
423 id: ID,
424
425 name: String,
426
427 #[has_one]
428 profile: toasty::Deferred<Profile>,
429 }
430
431 #[derive(Debug, toasty::Model)]
432 struct Profile {
433 #[key]
434 #[auto]
435 id: ID,
436
437 bio: String,
438
439 #[unique]
440 user_id: Option<ID>,
441
442 #[belongs_to(key = user_id, references = id)]
443 user: toasty::Deferred<Option<User>>,
444 }
445
446 let mut db = test.setup_db(models!(User, Profile)).await;
447
448 let user = User::create()
450 .name("Alice")
451 .profile(Profile::create().bio("a bio"))
452 .exec(&mut db)
453 .await?;
454
455 let loaded = User::filter_by_id(user.id)
457 .include(User::fields().profile())
458 .get(&mut db)
459 .await?;
460
461 let profile = loaded.profile.get();
462 assert_eq!("a bio", profile.bio);
463 assert_eq!(user.id, *profile.user_id.as_ref().unwrap());
464
465 let profile = Profile::filter_by_id(profile.id)
467 .include(Profile::fields().user())
468 .get(&mut db)
469 .await?;
470
471 assert_eq!(user.id, profile.user.get().as_ref().unwrap().id);
472 assert_eq!("Alice", profile.user.get().as_ref().unwrap().name);
473
474 Ok(())
475}
476
477#[driver_test(id(ID), scenario(crate::scenarios::user_todo_step))]
478pub async fn nested_has_many_preload(test: &mut Test) {
479 let mut db = setup(test).await;
480
481 let user = User::create()
483 .name("Alice")
484 .todos([Todo::create()
485 .title("Todo 1")
486 .steps([Step::create().description("Step 1a")])
487 .steps([Step::create().description("Step 1b")])])
488 .todos([Todo::create()
489 .title("Todo 2")
490 .steps([Step::create().description("Step 2a")])
491 .steps([Step::create().description("Step 2b")])
492 .steps([Step::create().description("Step 2c")])])
493 .exec(&mut db)
494 .await
495 .unwrap();
496
497 let user = User::filter_by_id(user.id)
499 .include(User::fields().todos().steps())
500 .get(&mut db)
501 .await
502 .unwrap();
503
504 let todos = user.todos.get();
506 assert_eq!(2, todos.len());
507
508 let mut all_step_descriptions: Vec<&str> = Vec::new();
510 for todo in todos {
511 let steps = todo.steps.get();
512 for step in steps {
513 all_step_descriptions.push(&step.description);
514 }
515 }
516 all_step_descriptions.sort();
517 assert_eq!(
518 all_step_descriptions,
519 vec!["Step 1a", "Step 1b", "Step 2a", "Step 2b", "Step 2c"]
520 );
521}
522
523#[driver_test(id(ID))]
526pub async fn nested_has_many_then_has_one_optional(test: &mut Test) -> Result<()> {
527 #[derive(Debug, toasty::Model)]
528 #[allow(dead_code)]
529 struct User {
530 #[key]
531 #[auto]
532 id: ID,
533
534 name: String,
535
536 #[has_many]
537 posts: toasty::Deferred<Vec<Post>>,
538 }
539
540 #[derive(Debug, toasty::Model)]
541 #[allow(dead_code)]
542 struct Post {
543 #[key]
544 #[auto]
545 id: ID,
546
547 title: String,
548
549 #[index]
550 user_id: ID,
551
552 #[belongs_to(key = user_id, references = id)]
553 user: toasty::Deferred<User>,
554
555 #[has_one]
556 detail: toasty::Deferred<Option<Detail>>,
557 }
558
559 #[derive(Debug, toasty::Model)]
560 #[allow(dead_code)]
561 struct Detail {
562 #[key]
563 #[auto]
564 id: ID,
565
566 body: String,
567
568 #[unique]
569 post_id: Option<ID>,
570
571 #[belongs_to(key = post_id, references = id)]
572 post: toasty::Deferred<Option<Post>>,
573 }
574
575 let mut db = test.setup_db(models!(User, Post, Detail)).await;
576
577 let user = User::create()
578 .name("Alice")
579 .posts([Post::create()
580 .title("P1")
581 .detail(Detail::create().body("D1"))])
582 .posts([Post::create().title("P2")]) .exec(&mut db)
584 .await?;
585
586 let user = User::filter_by_id(user.id)
587 .include(User::fields().posts().detail())
588 .get(&mut db)
589 .await?;
590
591 let posts = user.posts.get();
592 assert_eq!(2, posts.len());
593
594 let mut with_detail = 0;
595 let mut without_detail = 0;
596 for post in posts {
597 match post.detail.get() {
598 Some(d) => {
599 assert_eq!("D1", d.body);
600 with_detail += 1;
601 }
602 None => without_detail += 1,
603 }
604 }
605 assert_eq!(1, with_detail);
606 assert_eq!(1, without_detail);
607
608 Ok(())
609}
610
611#[driver_test(id(ID))]
614pub async fn nested_has_many_then_has_one_required(test: &mut Test) -> Result<()> {
615 #[derive(Debug, toasty::Model)]
616 #[allow(dead_code)]
617 struct User {
618 #[key]
619 #[auto]
620 id: ID,
621
622 name: String,
623
624 #[has_many]
625 accounts: toasty::Deferred<Vec<Account>>,
626 }
627
628 #[derive(Debug, toasty::Model)]
629 #[allow(dead_code)]
630 struct Account {
631 #[key]
632 #[auto]
633 id: ID,
634
635 label: String,
636
637 #[index]
638 user_id: ID,
639
640 #[belongs_to(key = user_id, references = id)]
641 user: toasty::Deferred<User>,
642
643 #[has_one]
644 settings: toasty::Deferred<Settings>,
645 }
646
647 #[derive(Debug, toasty::Model)]
648 #[allow(dead_code)]
649 struct Settings {
650 #[key]
651 #[auto]
652 id: ID,
653
654 theme: String,
655
656 #[unique]
657 account_id: Option<ID>,
658
659 #[belongs_to(key = account_id, references = id)]
660 account: toasty::Deferred<Option<Account>>,
661 }
662
663 let mut db = test.setup_db(models!(User, Account, Settings)).await;
664
665 let user = User::create()
666 .name("Bob")
667 .accounts([Account::create()
668 .label("A1")
669 .settings(Settings::create().theme("dark"))])
670 .accounts([Account::create()
671 .label("A2")
672 .settings(Settings::create().theme("light"))])
673 .exec(&mut db)
674 .await?;
675
676 let user = User::filter_by_id(user.id)
677 .include(User::fields().accounts().settings())
678 .get(&mut db)
679 .await?;
680
681 let accounts = user.accounts.get();
682 assert_eq!(2, accounts.len());
683
684 let mut themes: Vec<&str> = accounts
685 .iter()
686 .map(|a| a.settings.get().theme.as_str())
687 .collect();
688 themes.sort();
689 assert_eq!(themes, vec!["dark", "light"]);
690
691 Ok(())
692}
693
694#[driver_test(id(ID))]
697pub async fn nested_has_many_then_belongs_to_required(test: &mut Test) -> Result<()> {
698 #[derive(Debug, toasty::Model)]
699 #[allow(dead_code)]
700 struct Category {
701 #[key]
702 #[auto]
703 id: ID,
704
705 name: String,
706
707 #[has_many]
708 items: toasty::Deferred<Vec<Item>>,
709 }
710
711 #[derive(Debug, toasty::Model)]
712 #[allow(dead_code)]
713 struct Brand {
714 #[key]
715 #[auto]
716 id: ID,
717
718 name: String,
719 }
720
721 #[derive(Debug, toasty::Model)]
722 #[allow(dead_code)]
723 struct Item {
724 #[key]
725 #[auto]
726 id: ID,
727
728 title: String,
729
730 #[index]
731 category_id: ID,
732
733 #[belongs_to(key = category_id, references = id)]
734 category: toasty::Deferred<Category>,
735
736 #[index]
737 brand_id: ID,
738
739 #[belongs_to(key = brand_id, references = id)]
740 brand: toasty::Deferred<Brand>,
741 }
742
743 let mut db = test.setup_db(models!(Category, Brand, Item)).await;
744
745 let brand_a = Brand::create().name("BrandA").exec(&mut db).await?;
746 let brand_b = Brand::create().name("BrandB").exec(&mut db).await?;
747
748 let cat = Category::create()
749 .name("Electronics")
750 .items([Item::create().title("Phone").brand(&brand_a)])
751 .items([Item::create().title("Laptop").brand(&brand_b)])
752 .exec(&mut db)
753 .await?;
754
755 let cat = Category::filter_by_id(cat.id)
756 .include(Category::fields().items().brand())
757 .get(&mut db)
758 .await?;
759
760 let items = cat.items.get();
761 assert_eq!(2, items.len());
762
763 let mut brand_names: Vec<&str> = items.iter().map(|i| i.brand.get().name.as_str()).collect();
764 brand_names.sort();
765 assert_eq!(brand_names, vec!["BrandA", "BrandB"]);
766
767 Ok(())
768}
769
770#[driver_test(id(ID))]
775pub async fn nested_has_many_then_shared_belongs_to(test: &mut Test) -> Result<()> {
776 #[derive(Debug, toasty::Model)]
777 #[allow(dead_code)]
778 struct Category {
779 #[key]
780 #[auto]
781 id: ID,
782
783 name: String,
784
785 #[has_many]
786 items: toasty::Deferred<Vec<Item>>,
787 }
788
789 #[derive(Debug, toasty::Model)]
790 #[allow(dead_code)]
791 struct Brand {
792 #[key]
793 #[auto]
794 id: ID,
795
796 name: String,
797 }
798
799 #[derive(Debug, toasty::Model)]
800 #[allow(dead_code)]
801 struct Item {
802 #[key]
803 #[auto]
804 id: ID,
805
806 title: String,
807
808 #[index]
809 category_id: ID,
810
811 #[belongs_to(key = category_id, references = id)]
812 category: toasty::Deferred<Category>,
813
814 #[index]
815 brand_id: ID,
816
817 #[belongs_to(key = brand_id, references = id)]
818 brand: toasty::Deferred<Brand>,
819 }
820
821 let mut db = test.setup_db(models!(Category, Brand, Item)).await;
822
823 let brand = Brand::create().name("BrandA").exec(&mut db).await?;
824 let cat = Category::create()
825 .name("Electronics")
826 .items([Item::create().title("Phone").brand(&brand)])
827 .items([Item::create().title("Laptop").brand(&brand)])
828 .exec(&mut db)
829 .await?;
830
831 let cat = Category::filter_by_id(cat.id)
832 .include(Category::fields().items().brand())
833 .get(&mut db)
834 .await?;
835
836 let items = cat.items.get();
837 assert_eq!(2, items.len());
838 for item in items {
839 assert_eq!("BrandA", item.brand.get().name);
840 }
841
842 Ok(())
843}
844
845#[driver_test(id(ID))]
848pub async fn nested_has_many_then_belongs_to_optional(test: &mut Test) -> Result<()> {
849 #[derive(Debug, toasty::Model)]
850 #[allow(dead_code)]
851 struct Team {
852 #[key]
853 #[auto]
854 id: ID,
855
856 name: String,
857
858 #[has_many]
859 tasks: toasty::Deferred<Vec<Task>>,
860 }
861
862 #[derive(Debug, toasty::Model)]
863 #[allow(dead_code)]
864 struct Assignee {
865 #[key]
866 #[auto]
867 id: ID,
868
869 name: String,
870 }
871
872 #[derive(Debug, toasty::Model)]
873 #[allow(dead_code)]
874 struct Task {
875 #[key]
876 #[auto]
877 id: ID,
878
879 title: String,
880
881 #[index]
882 team_id: ID,
883
884 #[belongs_to(key = team_id, references = id)]
885 team: toasty::Deferred<Team>,
886
887 #[index]
888 assignee_id: Option<ID>,
889
890 #[belongs_to(key = assignee_id, references = id)]
891 assignee: toasty::Deferred<Option<Assignee>>,
892 }
893
894 let mut db = test.setup_db(models!(Team, Assignee, Task)).await;
895
896 let person = Assignee::create().name("Alice").exec(&mut db).await?;
897
898 let team = Team::create()
899 .name("Engineering")
900 .tasks([Task::create().title("Assigned").assignee(&person)])
901 .tasks([Task::create().title("Unassigned")])
902 .exec(&mut db)
903 .await?;
904
905 let team = Team::filter_by_id(team.id)
906 .include(Team::fields().tasks().assignee())
907 .get(&mut db)
908 .await?;
909
910 let tasks = team.tasks.get();
911 assert_eq!(2, tasks.len());
912
913 let mut assigned = 0;
914 let mut unassigned = 0;
915 for task in tasks {
916 match task.assignee.get() {
917 Some(a) => {
918 assert_eq!("Alice", a.name);
919 assigned += 1;
920 }
921 None => unassigned += 1,
922 }
923 }
924 assert_eq!(1, assigned);
925 assert_eq!(1, unassigned);
926
927 Ok(())
928}
929
930#[driver_test(id(ID))]
933pub async fn nested_has_one_optional_then_has_many(test: &mut Test) -> Result<()> {
934 #[derive(Debug, toasty::Model)]
935 #[allow(dead_code)]
936 struct User {
937 #[key]
938 #[auto]
939 id: ID,
940
941 name: String,
942
943 #[has_one]
944 profile: toasty::Deferred<Option<Profile>>,
945 }
946
947 #[derive(Debug, toasty::Model)]
948 #[allow(dead_code)]
949 struct Profile {
950 #[key]
951 #[auto]
952 id: ID,
953
954 bio: String,
955
956 #[unique]
957 user_id: Option<ID>,
958
959 #[belongs_to(key = user_id, references = id)]
960 user: toasty::Deferred<Option<User>>,
961
962 #[has_many]
963 badges: toasty::Deferred<Vec<Badge>>,
964 }
965
966 #[derive(Debug, toasty::Model)]
967 #[allow(dead_code)]
968 struct Badge {
969 #[key]
970 #[auto]
971 id: ID,
972
973 label: String,
974
975 #[index]
976 profile_id: ID,
977
978 #[belongs_to(key = profile_id, references = id)]
979 profile: toasty::Deferred<Profile>,
980 }
981
982 let mut db = test.setup_db(models!(User, Profile, Badge)).await;
983
984 let user = User::create()
986 .name("Alice")
987 .profile(
988 Profile::create()
989 .bio("hi")
990 .badges([Badge::create().label("Gold")])
991 .badges([Badge::create().label("Silver")]),
992 )
993 .exec(&mut db)
994 .await?;
995
996 let user = User::filter_by_id(user.id)
997 .include(User::fields().profile().badges())
998 .get(&mut db)
999 .await?;
1000
1001 let profile = user.profile.get().as_ref().unwrap();
1002 assert_eq!("hi", profile.bio);
1003 let mut labels: Vec<&str> = profile
1004 .badges
1005 .get()
1006 .iter()
1007 .map(|b| b.label.as_str())
1008 .collect();
1009 labels.sort();
1010 assert_eq!(labels, vec!["Gold", "Silver"]);
1011
1012 let user2 = User::create().name("Bob").exec(&mut db).await?;
1014
1015 let user2 = User::filter_by_id(user2.id)
1016 .include(User::fields().profile().badges())
1017 .get(&mut db)
1018 .await?;
1019
1020 assert!(user2.profile.get().is_none());
1021
1022 Ok(())
1023}
1024
1025#[driver_test(id(ID))]
1028pub async fn nested_has_one_required_then_has_many(test: &mut Test) -> Result<()> {
1029 #[derive(Debug, toasty::Model)]
1030 #[allow(dead_code)]
1031 struct Order {
1032 #[key]
1033 #[auto]
1034 id: ID,
1035
1036 label: String,
1037
1038 #[has_one]
1039 invoice: toasty::Deferred<Invoice>,
1040 }
1041
1042 #[derive(Debug, toasty::Model)]
1043 #[allow(dead_code)]
1044 struct Invoice {
1045 #[key]
1046 #[auto]
1047 id: ID,
1048
1049 code: String,
1050
1051 #[unique]
1052 order_id: Option<ID>,
1053
1054 #[belongs_to(key = order_id, references = id)]
1055 order: toasty::Deferred<Option<Order>>,
1056
1057 #[has_many]
1058 line_items: toasty::Deferred<Vec<LineItem>>,
1059 }
1060
1061 #[derive(Debug, toasty::Model)]
1062 #[allow(dead_code)]
1063 struct LineItem {
1064 #[key]
1065 #[auto]
1066 id: ID,
1067
1068 description: String,
1069
1070 #[index]
1071 invoice_id: ID,
1072
1073 #[belongs_to(key = invoice_id, references = id)]
1074 invoice: toasty::Deferred<Invoice>,
1075 }
1076
1077 let mut db = test.setup_db(models!(Order, Invoice, LineItem)).await;
1078
1079 let order = Order::create()
1080 .label("Order1")
1081 .invoice(
1082 Invoice::create()
1083 .code("INV-001")
1084 .line_items([LineItem::create().description("Widget")])
1085 .line_items([LineItem::create().description("Gadget")]),
1086 )
1087 .exec(&mut db)
1088 .await?;
1089
1090 let order = Order::filter_by_id(order.id)
1091 .include(Order::fields().invoice().line_items())
1092 .get(&mut db)
1093 .await?;
1094
1095 let invoice = order.invoice.get();
1096 assert_eq!("INV-001", invoice.code);
1097 let mut descs: Vec<&str> = invoice
1098 .line_items
1099 .get()
1100 .iter()
1101 .map(|li| li.description.as_str())
1102 .collect();
1103 descs.sort();
1104 assert_eq!(descs, vec!["Gadget", "Widget"]);
1105
1106 Ok(())
1107}
1108
1109#[driver_test(id(ID))]
1112pub async fn nested_has_one_optional_then_has_one_optional(test: &mut Test) -> Result<()> {
1113 #[derive(Debug, toasty::Model)]
1114 #[allow(dead_code)]
1115 struct User {
1116 #[key]
1117 #[auto]
1118 id: ID,
1119
1120 name: String,
1121
1122 #[has_one]
1123 profile: toasty::Deferred<Option<Profile>>,
1124 }
1125
1126 #[derive(Debug, toasty::Model)]
1127 #[allow(dead_code)]
1128 struct Profile {
1129 #[key]
1130 #[auto]
1131 id: ID,
1132
1133 bio: String,
1134
1135 #[unique]
1136 user_id: Option<ID>,
1137
1138 #[belongs_to(key = user_id, references = id)]
1139 user: toasty::Deferred<Option<User>>,
1140
1141 #[has_one]
1142 avatar: toasty::Deferred<Option<Avatar>>,
1143 }
1144
1145 #[derive(Debug, toasty::Model)]
1146 #[allow(dead_code)]
1147 struct Avatar {
1148 #[key]
1149 #[auto]
1150 id: ID,
1151
1152 url: String,
1153
1154 #[unique]
1155 profile_id: Option<ID>,
1156
1157 #[belongs_to(key = profile_id, references = id)]
1158 profile: toasty::Deferred<Option<Profile>>,
1159 }
1160
1161 let mut db = test.setup_db(models!(User, Profile, Avatar)).await;
1162
1163 let user = User::create()
1165 .name("Alice")
1166 .profile(
1167 Profile::create()
1168 .bio("hi")
1169 .avatar(Avatar::create().url("pic.png")),
1170 )
1171 .exec(&mut db)
1172 .await?;
1173
1174 let user = User::filter_by_id(user.id)
1175 .include(User::fields().profile().avatar())
1176 .get(&mut db)
1177 .await?;
1178
1179 let profile = user.profile.get().as_ref().unwrap();
1180 assert_eq!("hi", profile.bio);
1181 let avatar = profile.avatar.get().as_ref().unwrap();
1182 assert_eq!("pic.png", avatar.url);
1183
1184 let user2 = User::create()
1186 .name("Bob")
1187 .profile(Profile::create().bio("no pic"))
1188 .exec(&mut db)
1189 .await?;
1190
1191 let user2 = User::filter_by_id(user2.id)
1192 .include(User::fields().profile().avatar())
1193 .get(&mut db)
1194 .await?;
1195
1196 let profile2 = user2.profile.get().as_ref().unwrap();
1197 assert_eq!("no pic", profile2.bio);
1198 assert!(profile2.avatar.get().is_none());
1199
1200 let user3 = User::create().name("Carol").exec(&mut db).await?;
1202
1203 let user3 = User::filter_by_id(user3.id)
1204 .include(User::fields().profile().avatar())
1205 .get(&mut db)
1206 .await?;
1207
1208 assert!(user3.profile.get().is_none());
1209
1210 Ok(())
1211}
1212
1213#[driver_test(id(ID))]
1216pub async fn nested_has_one_required_then_has_one_required(test: &mut Test) -> Result<()> {
1217 #[derive(Debug, toasty::Model)]
1218 #[allow(dead_code)]
1219 struct User {
1220 #[key]
1221 #[auto]
1222 id: ID,
1223
1224 name: String,
1225
1226 #[has_one]
1227 profile: toasty::Deferred<Profile>,
1228 }
1229
1230 #[derive(Debug, toasty::Model)]
1231 #[allow(dead_code)]
1232 struct Profile {
1233 #[key]
1234 #[auto]
1235 id: ID,
1236
1237 bio: String,
1238
1239 #[unique]
1240 user_id: Option<ID>,
1241
1242 #[belongs_to(key = user_id, references = id)]
1243 user: toasty::Deferred<Option<User>>,
1244
1245 #[has_one]
1246 avatar: toasty::Deferred<Avatar>,
1247 }
1248
1249 #[derive(Debug, toasty::Model)]
1250 #[allow(dead_code)]
1251 struct Avatar {
1252 #[key]
1253 #[auto]
1254 id: ID,
1255
1256 url: String,
1257
1258 #[unique]
1259 profile_id: Option<ID>,
1260
1261 #[belongs_to(key = profile_id, references = id)]
1262 profile: toasty::Deferred<Option<Profile>>,
1263 }
1264
1265 let mut db = test.setup_db(models!(User, Profile, Avatar)).await;
1266
1267 let user = User::create()
1268 .name("Alice")
1269 .profile(
1270 Profile::create()
1271 .bio("engineer")
1272 .avatar(Avatar::create().url("alice.jpg")),
1273 )
1274 .exec(&mut db)
1275 .await?;
1276
1277 let user = User::filter_by_id(user.id)
1278 .include(User::fields().profile().avatar())
1279 .get(&mut db)
1280 .await?;
1281
1282 let profile = user.profile.get();
1283 assert_eq!("engineer", profile.bio);
1284 let avatar = profile.avatar.get();
1285 assert_eq!("alice.jpg", avatar.url);
1286
1287 Ok(())
1288}
1289
1290#[driver_test(id(ID))]
1293pub async fn nested_has_one_optional_then_belongs_to_required(test: &mut Test) -> Result<()> {
1294 #[derive(Debug, toasty::Model)]
1295 #[allow(dead_code)]
1296 struct User {
1297 #[key]
1298 #[auto]
1299 id: ID,
1300
1301 name: String,
1302
1303 #[has_one]
1304 review: toasty::Deferred<Option<Review>>,
1305 }
1306
1307 #[derive(Debug, toasty::Model)]
1308 #[allow(dead_code)]
1309 struct Product {
1310 #[key]
1311 #[auto]
1312 id: ID,
1313
1314 name: String,
1315 }
1316
1317 #[derive(Debug, toasty::Model)]
1318 #[allow(dead_code)]
1319 struct Review {
1320 #[key]
1321 #[auto]
1322 id: ID,
1323
1324 body: String,
1325
1326 #[unique]
1327 user_id: Option<ID>,
1328
1329 #[belongs_to(key = user_id, references = id)]
1330 user: toasty::Deferred<Option<User>>,
1331
1332 #[index]
1333 product_id: ID,
1334
1335 #[belongs_to(key = product_id, references = id)]
1336 product: toasty::Deferred<Product>,
1337 }
1338
1339 let mut db = test.setup_db(models!(User, Product, Review)).await;
1340
1341 let product = Product::create().name("Widget").exec(&mut db).await?;
1342
1343 let user = User::create()
1344 .name("Alice")
1345 .review(Review::create().body("Great!").product(&product))
1346 .exec(&mut db)
1347 .await?;
1348
1349 let user = User::filter_by_id(user.id)
1351 .include(User::fields().review().product())
1352 .get(&mut db)
1353 .await?;
1354
1355 let review = user.review.get().as_ref().unwrap();
1356 assert_eq!("Great!", review.body);
1357 assert_eq!("Widget", review.product.get().name);
1358
1359 let user2 = User::create().name("Bob").exec(&mut db).await?;
1361
1362 let user2 = User::filter_by_id(user2.id)
1363 .include(User::fields().review().product())
1364 .get(&mut db)
1365 .await?;
1366
1367 assert!(user2.review.get().is_none());
1368
1369 Ok(())
1370}
1371
1372#[driver_test(id(ID))]
1375pub async fn nested_belongs_to_required_then_has_many(test: &mut Test) -> Result<()> {
1376 #[derive(Debug, toasty::Model)]
1377 #[allow(dead_code)]
1378 struct Post {
1379 #[key]
1380 #[auto]
1381 id: ID,
1382
1383 title: String,
1384
1385 #[has_many]
1386 tags: toasty::Deferred<Vec<Tag>>,
1387 }
1388
1389 #[derive(Debug, toasty::Model)]
1390 #[allow(dead_code)]
1391 struct Tag {
1392 #[key]
1393 #[auto]
1394 id: ID,
1395
1396 label: String,
1397
1398 #[index]
1399 post_id: ID,
1400
1401 #[belongs_to(key = post_id, references = id)]
1402 post: toasty::Deferred<Post>,
1403 }
1404
1405 #[derive(Debug, toasty::Model)]
1406 #[allow(dead_code)]
1407 struct Comment {
1408 #[key]
1409 #[auto]
1410 id: ID,
1411
1412 body: String,
1413
1414 #[index]
1415 post_id: ID,
1416
1417 #[belongs_to(key = post_id, references = id)]
1418 post: toasty::Deferred<Post>,
1419 }
1420
1421 let mut db = test.setup_db(models!(Post, Tag, Comment)).await;
1422
1423 let post = Post::create()
1424 .title("Hello")
1425 .tags([Tag::create().label("rust")])
1426 .tags([Tag::create().label("orm")])
1427 .exec(&mut db)
1428 .await?;
1429
1430 let comment = Comment::create()
1431 .body("Nice post")
1432 .post(&post)
1433 .exec(&mut db)
1434 .await?;
1435
1436 let comment = Comment::filter_by_id(comment.id)
1438 .include(Comment::fields().post().tags())
1439 .get(&mut db)
1440 .await?;
1441
1442 assert_eq!("Hello", comment.post.get().title);
1443 let mut labels: Vec<&str> = comment
1444 .post
1445 .get()
1446 .tags
1447 .get()
1448 .iter()
1449 .map(|t| t.label.as_str())
1450 .collect();
1451 labels.sort();
1452 assert_eq!(labels, vec!["orm", "rust"]);
1453
1454 Ok(())
1455}
1456
1457#[driver_test(id(ID))]
1460pub async fn nested_belongs_to_required_then_has_one_optional(test: &mut Test) -> Result<()> {
1461 #[derive(Debug, toasty::Model)]
1462 #[allow(dead_code)]
1463 struct User {
1464 #[key]
1465 #[auto]
1466 id: ID,
1467
1468 name: String,
1469
1470 #[has_one]
1471 profile: toasty::Deferred<Option<Profile>>,
1472
1473 #[has_many]
1474 todos: toasty::Deferred<Vec<Todo>>,
1475 }
1476
1477 #[derive(Debug, toasty::Model)]
1478 #[allow(dead_code)]
1479 struct Profile {
1480 #[key]
1481 #[auto]
1482 id: ID,
1483
1484 bio: String,
1485
1486 #[unique]
1487 user_id: Option<ID>,
1488
1489 #[belongs_to(key = user_id, references = id)]
1490 user: toasty::Deferred<Option<User>>,
1491 }
1492
1493 #[derive(Debug, toasty::Model)]
1494 #[allow(dead_code)]
1495 struct Todo {
1496 #[key]
1497 #[auto]
1498 id: ID,
1499
1500 title: String,
1501
1502 #[index]
1503 user_id: ID,
1504
1505 #[belongs_to(key = user_id, references = id)]
1506 user: toasty::Deferred<User>,
1507 }
1508
1509 let mut db = test.setup_db(models!(User, Profile, Todo)).await;
1510
1511 let user = User::create()
1513 .name("Alice")
1514 .profile(Profile::create().bio("developer"))
1515 .todos([Todo::create().title("Task 1")])
1516 .exec(&mut db)
1517 .await?;
1518
1519 let todo_id = Todo::get_by_user_id(&mut db, user.id).await?.id;
1520
1521 let todo = Todo::filter_by_id(todo_id)
1522 .include(Todo::fields().user().profile())
1523 .get(&mut db)
1524 .await?;
1525
1526 assert_eq!("Alice", todo.user.get().name);
1527 let profile = todo.user.get().profile.get().as_ref().unwrap();
1528 assert_eq!("developer", profile.bio);
1529
1530 let user2 = User::create()
1532 .name("Bob")
1533 .todos([Todo::create().title("Task 2")])
1534 .exec(&mut db)
1535 .await?;
1536
1537 let todo2_id = Todo::get_by_user_id(&mut db, user2.id).await?.id;
1538
1539 let todo2 = Todo::filter_by_id(todo2_id)
1540 .include(Todo::fields().user().profile())
1541 .get(&mut db)
1542 .await?;
1543
1544 assert_eq!("Bob", todo2.user.get().name);
1545 assert!(todo2.user.get().profile.get().is_none());
1546
1547 Ok(())
1548}
1549
1550#[driver_test(id(ID), scenario(crate::scenarios::user_todo_step))]
1553pub async fn nested_belongs_to_required_then_belongs_to_required(test: &mut Test) -> Result<()> {
1554 let mut db = setup(test).await;
1555
1556 let user = User::create()
1557 .name("Alice")
1558 .todos([Todo::create()
1559 .title("T1")
1560 .steps([Step::create().description("S1")])])
1561 .exec(&mut db)
1562 .await?;
1563
1564 let todo_id = Todo::get_by_user_id(&mut db, user.id).await?.id;
1565 let step_id = Step::get_by_todo_id(&mut db, todo_id).await?.id;
1566
1567 let step = Step::filter_by_id(step_id)
1569 .include(Step::fields().todo().user())
1570 .get(&mut db)
1571 .await?;
1572
1573 assert_eq!("T1", step.todo.get().title);
1574 assert_eq!("Alice", step.todo.get().user.get().name);
1575
1576 Ok(())
1577}
1578
1579#[driver_test(id(ID))]
1582pub async fn nested_belongs_to_optional_then_has_many(test: &mut Test) -> Result<()> {
1583 #[derive(Debug, toasty::Model)]
1584 #[allow(dead_code)]
1585 struct Project {
1586 #[key]
1587 #[auto]
1588 id: ID,
1589
1590 name: String,
1591
1592 #[has_many]
1593 members: toasty::Deferred<Vec<Member>>,
1594 }
1595
1596 #[derive(Debug, toasty::Model)]
1597 #[allow(dead_code)]
1598 struct Member {
1599 #[key]
1600 #[auto]
1601 id: ID,
1602
1603 name: String,
1604
1605 #[index]
1606 project_id: ID,
1607
1608 #[belongs_to(key = project_id, references = id)]
1609 project: toasty::Deferred<Project>,
1610 }
1611
1612 #[derive(Debug, toasty::Model)]
1613 #[allow(dead_code)]
1614 struct Task {
1615 #[key]
1616 #[auto]
1617 id: ID,
1618
1619 title: String,
1620
1621 #[index]
1622 project_id: Option<ID>,
1623
1624 #[belongs_to(key = project_id, references = id)]
1625 project: toasty::Deferred<Option<Project>>,
1626 }
1627
1628 let mut db = test.setup_db(models!(Project, Member, Task)).await;
1629
1630 let project = Project::create()
1631 .name("Proj1")
1632 .members([Member::create().name("Alice")])
1633 .members([Member::create().name("Bob")])
1634 .exec(&mut db)
1635 .await?;
1636
1637 let task = Task::create()
1639 .title("Linked")
1640 .project(&project)
1641 .exec(&mut db)
1642 .await?;
1643
1644 let task = Task::filter_by_id(task.id)
1645 .include(Task::fields().project().members())
1646 .get(&mut db)
1647 .await?;
1648
1649 let proj = task.project.get().as_ref().unwrap();
1650 assert_eq!("Proj1", proj.name);
1651 let mut names: Vec<&str> = proj.members.get().iter().map(|m| m.name.as_str()).collect();
1652 names.sort();
1653 assert_eq!(names, vec!["Alice", "Bob"]);
1654
1655 let orphan = Task::create().title("Orphan").exec(&mut db).await?;
1657
1658 let orphan = Task::filter_by_id(orphan.id)
1659 .include(Task::fields().project().members())
1660 .get(&mut db)
1661 .await?;
1662
1663 assert!(orphan.project.get().is_none());
1664
1665 Ok(())
1666}
1667
1668#[driver_test(id(ID))]
1671pub async fn nested_belongs_to_optional_then_belongs_to_optional(test: &mut Test) -> Result<()> {
1672 #[derive(Debug, toasty::Model)]
1673 #[allow(dead_code)]
1674 struct Category {
1675 #[key]
1676 #[auto]
1677 id: ID,
1678
1679 name: String,
1680 }
1681
1682 #[derive(Debug, toasty::Model)]
1683 #[allow(dead_code)]
1684 struct Post {
1685 #[key]
1686 #[auto]
1687 id: ID,
1688
1689 title: String,
1690
1691 #[index]
1692 category_id: Option<ID>,
1693
1694 #[belongs_to(key = category_id, references = id)]
1695 category: toasty::Deferred<Option<Category>>,
1696 }
1697
1698 #[derive(Debug, toasty::Model)]
1699 #[allow(dead_code)]
1700 struct Comment {
1701 #[key]
1702 #[auto]
1703 id: ID,
1704
1705 body: String,
1706
1707 #[index]
1708 post_id: Option<ID>,
1709
1710 #[belongs_to(key = post_id, references = id)]
1711 post: toasty::Deferred<Option<Post>>,
1712 }
1713
1714 let mut db = test.setup_db(models!(Category, Post, Comment)).await;
1715
1716 let cat = Category::create().name("Tech").exec(&mut db).await?;
1717 let post = Post::create()
1718 .title("Hello")
1719 .category(&cat)
1720 .exec(&mut db)
1721 .await?;
1722
1723 let c1 = Comment::create()
1725 .body("Nice")
1726 .post(&post)
1727 .exec(&mut db)
1728 .await?;
1729
1730 let c1 = Comment::filter_by_id(c1.id)
1731 .include(Comment::fields().post().category())
1732 .get(&mut db)
1733 .await?;
1734
1735 let loaded_post = c1.post.get().as_ref().unwrap();
1736 assert_eq!("Hello", loaded_post.title);
1737 let loaded_cat = loaded_post.category.get().as_ref().unwrap();
1738 assert_eq!("Tech", loaded_cat.name);
1739
1740 let post2 = Post::create().title("Uncategorized").exec(&mut db).await?;
1742 let c2 = Comment::create()
1743 .body("Hmm")
1744 .post(&post2)
1745 .exec(&mut db)
1746 .await?;
1747
1748 let c2 = Comment::filter_by_id(c2.id)
1749 .include(Comment::fields().post().category())
1750 .get(&mut db)
1751 .await?;
1752
1753 let loaded_post2 = c2.post.get().as_ref().unwrap();
1754 assert_eq!("Uncategorized", loaded_post2.title);
1755 assert!(loaded_post2.category.get().is_none());
1756
1757 let c3 = Comment::create().body("Orphan").exec(&mut db).await?;
1759
1760 let c3 = Comment::filter_by_id(c3.id)
1761 .include(Comment::fields().post().category())
1762 .get(&mut db)
1763 .await?;
1764
1765 assert!(c3.post.get().is_none());
1766
1767 Ok(())
1768}
1769
1770#[driver_test(id(ID))]
1773pub async fn nested_belongs_to_required_then_has_one_required(test: &mut Test) -> Result<()> {
1774 #[derive(Debug, toasty::Model)]
1775 #[allow(dead_code)]
1776 struct User {
1777 #[key]
1778 #[auto]
1779 id: ID,
1780
1781 name: String,
1782
1783 #[has_one]
1784 config: toasty::Deferred<Config>,
1785
1786 #[has_many]
1787 todos: toasty::Deferred<Vec<Todo>>,
1788 }
1789
1790 #[derive(Debug, toasty::Model)]
1791 #[allow(dead_code)]
1792 struct Config {
1793 #[key]
1794 #[auto]
1795 id: ID,
1796
1797 theme: String,
1798
1799 #[unique]
1800 user_id: Option<ID>,
1801
1802 #[belongs_to(key = user_id, references = id)]
1803 user: toasty::Deferred<Option<User>>,
1804 }
1805
1806 #[derive(Debug, toasty::Model)]
1807 #[allow(dead_code)]
1808 struct Todo {
1809 #[key]
1810 #[auto]
1811 id: ID,
1812
1813 title: String,
1814
1815 #[index]
1816 user_id: ID,
1817
1818 #[belongs_to(key = user_id, references = id)]
1819 user: toasty::Deferred<User>,
1820 }
1821
1822 let mut db = test.setup_db(models!(User, Config, Todo)).await;
1823
1824 let user = User::create()
1825 .name("Alice")
1826 .config(Config::create().theme("dark"))
1827 .todos([Todo::create().title("Task")])
1828 .exec(&mut db)
1829 .await?;
1830
1831 let todo_id = Todo::get_by_user_id(&mut db, user.id).await?.id;
1832
1833 let todo = Todo::filter_by_id(todo_id)
1834 .include(Todo::fields().user().config())
1835 .get(&mut db)
1836 .await?;
1837
1838 assert_eq!("Alice", todo.user.get().name);
1839 assert_eq!("dark", todo.user.get().config.get().theme);
1840
1841 Ok(())
1842}
1843
1844#[driver_test(id(ID), scenario(crate::scenarios::user_todo_step))]
1848pub async fn nested_has_many_then_has_many_with_empty_leaves(test: &mut Test) {
1849 let mut db = setup(test).await;
1850
1851 let user = User::create()
1852 .name("Alice")
1853 .todos([Todo::create()
1854 .title("With Steps")
1855 .steps([Step::create().description("S1")])])
1856 .todos([Todo::create().title("No Steps")]) .exec(&mut db)
1858 .await
1859 .unwrap();
1860
1861 let user = User::filter_by_id(user.id)
1862 .include(User::fields().todos().steps())
1863 .get(&mut db)
1864 .await
1865 .unwrap();
1866
1867 let todos = user.todos.get();
1868 assert_eq!(2, todos.len());
1869
1870 let mut total_steps = 0;
1871 for todo in todos {
1872 let steps = todo.steps.get();
1873 if todo.title == "With Steps" {
1874 assert_eq!(1, steps.len());
1875 assert_eq!("S1", steps[0].description);
1876 } else {
1877 assert_eq!(0, steps.len());
1878 }
1879 total_steps += steps.len();
1880 }
1881 assert_eq!(1, total_steps);
1882}
1883
1884#[driver_test(
1889 id(ID),
1890 requires(sql),
1891 scenario(crate::scenarios::has_many_multi_relation)
1892)]
1893pub async fn sibling_nested_includes_on_shared_prefix(test: &mut Test) -> Result<()> {
1894 let mut db = setup(test).await;
1895
1896 let category = toasty::create!(Category { name: "Food" })
1897 .exec(&mut db)
1898 .await?;
1899 let user = toasty::create!(User {
1900 name: "Alice",
1901 todos: [
1902 { title: "T1", category: &category },
1903 { title: "T2", category: &category },
1904 ],
1905 })
1906 .exec(&mut db)
1907 .await?;
1908
1909 let loaded = User::filter_by_id(user.id)
1912 .include(User::fields().todos().user())
1913 .include(User::fields().todos().category())
1914 .get(&mut db)
1915 .await?;
1916
1917 let todos = loaded.todos.get();
1918 assert_eq!(2, todos.len());
1919 for todo in todos {
1920 assert_eq!("Alice", todo.user.get().name);
1921 assert_eq!("Food", todo.category.get().name);
1922 }
1923
1924 Ok(())
1925}
1926
1927#[driver_test(
1931 id(ID),
1932 requires(sql),
1933 scenario(crate::scenarios::has_many_multi_relation)
1934)]
1935pub async fn bare_and_nested_includes_on_shared_prefix(test: &mut Test) -> Result<()> {
1936 let mut db = setup(test).await;
1937
1938 let category = toasty::create!(Category { name: "Food" })
1939 .exec(&mut db)
1940 .await?;
1941 let user = toasty::create!(User {
1942 name: "Alice",
1943 todos: [{ title: "T1", category: &category }],
1944 })
1945 .exec(&mut db)
1946 .await?;
1947
1948 let loaded = User::filter_by_id(user.id)
1949 .include(User::fields().todos()) .include(User::fields().todos().user()) .include(User::fields().todos().category()) .get(&mut db)
1953 .await?;
1954
1955 let todos = loaded.todos.get();
1956 assert_eq!(1, todos.len());
1957 assert_eq!("Alice", todos[0].user.get().name);
1958 assert_eq!("Food", todos[0].category.get().name);
1959
1960 Ok(())
1961}
1962
1963#[driver_test(id(ID))]
1968pub async fn sibling_nested_includes_on_shared_prefix_non_sql(test: &mut Test) -> Result<()> {
1969 #[derive(Debug, toasty::Model)]
1970 #[allow(dead_code)]
1971 struct Category {
1972 #[key]
1973 #[auto]
1974 id: ID,
1975
1976 name: String,
1977
1978 #[has_many]
1979 items: toasty::Deferred<Vec<Item>>,
1980 }
1981
1982 #[derive(Debug, toasty::Model)]
1983 #[allow(dead_code)]
1984 struct Brand {
1985 #[key]
1986 #[auto]
1987 id: ID,
1988
1989 name: String,
1990 }
1991
1992 #[derive(Debug, toasty::Model)]
1993 #[allow(dead_code)]
1994 struct Supplier {
1995 #[key]
1996 #[auto]
1997 id: ID,
1998
1999 name: String,
2000 }
2001
2002 #[derive(Debug, toasty::Model)]
2003 #[allow(dead_code)]
2004 struct Item {
2005 #[key]
2006 #[auto]
2007 id: ID,
2008
2009 title: String,
2010
2011 #[index]
2012 category_id: ID,
2013
2014 #[belongs_to(key = category_id, references = id)]
2015 category: toasty::Deferred<Category>,
2016
2017 #[index]
2018 brand_id: ID,
2019
2020 #[belongs_to(key = brand_id, references = id)]
2021 brand: toasty::Deferred<Brand>,
2022
2023 #[index]
2024 supplier_id: ID,
2025
2026 #[belongs_to(key = supplier_id, references = id)]
2027 supplier: toasty::Deferred<Supplier>,
2028 }
2029
2030 let mut db = test
2031 .setup_db(models!(Category, Brand, Supplier, Item))
2032 .await;
2033
2034 let brand_a = toasty::create!(Brand { name: "BrandA" })
2035 .exec(&mut db)
2036 .await?;
2037 let brand_b = toasty::create!(Brand { name: "BrandB" })
2038 .exec(&mut db)
2039 .await?;
2040 let sup_a = toasty::create!(Supplier { name: "SupA" })
2041 .exec(&mut db)
2042 .await?;
2043 let sup_b = toasty::create!(Supplier { name: "SupB" })
2044 .exec(&mut db)
2045 .await?;
2046
2047 let cat = toasty::create!(Category {
2048 name: "Electronics",
2049 items: [
2050 { title: "Phone", brand: &brand_a, supplier: &sup_a },
2051 { title: "Laptop", brand: &brand_b, supplier: &sup_b },
2052 ],
2053 })
2054 .exec(&mut db)
2055 .await?;
2056
2057 let loaded = Category::filter_by_id(cat.id)
2060 .include(Category::fields().items().brand())
2061 .include(Category::fields().items().supplier())
2062 .get(&mut db)
2063 .await?;
2064
2065 let items = loaded.items.get();
2066 assert_eq!(2, items.len());
2067 let mut pairs: Vec<(&str, &str)> = items
2068 .iter()
2069 .map(|i| (i.brand.get().name.as_str(), i.supplier.get().name.as_str()))
2070 .collect();
2071 pairs.sort();
2072 assert_eq!(pairs, vec![("BrandA", "SupA"), ("BrandB", "SupB")]);
2073
2074 Ok(())
2075}