Skip to main content

toasty_driver_integration_suite/tests/
select_projection_has_many.rs

1//! `.select(...)` projection through a `HasMany` relation field.
2//!
3//! Field handles for `HasMany` relations return
4//! `<Target as Relation>::ManyField<__Origin>` (the macro-generated
5//! `*FieldList` struct).  An `IntoExpr<List<TargetModel>>` impl on that
6//! struct lets the field handle flow through `.select(...)` the same way
7//! `BelongsTo`/`HasOne` handles do; each parent row projects to a list of
8//! related rows, and the executor decodes the result as `Vec<Vec<Target>>`.
9
10use crate::prelude::*;
11
12#[driver_test(id(ID), requires(sql))]
13pub async fn select_has_many_basic(t: &mut Test) -> Result<()> {
14    #[derive(Debug, toasty::Model)]
15    struct User {
16        #[key]
17        #[auto]
18        id: ID,
19        name: String,
20
21        #[has_many]
22        posts: toasty::HasMany<Post>,
23    }
24
25    #[derive(Debug, toasty::Model)]
26    struct Post {
27        #[key]
28        #[auto]
29        id: ID,
30        title: String,
31
32        #[index]
33        user_id: ID,
34
35        #[belongs_to(key = user_id, references = id)]
36        user: toasty::BelongsTo<User>,
37    }
38
39    let mut db = t.setup_db(models!(User, Post)).await;
40
41    toasty::create!(User {
42        name: "Alice",
43        posts: [Post::create().title("alpha"), Post::create().title("beta"),],
44    })
45    .exec(&mut db)
46    .await?;
47
48    let posts_per_user: Vec<Vec<Post>> = User::all()
49        .select(User::fields().posts())
50        .exec(&mut db)
51        .await?;
52
53    assert_eq!(posts_per_user.len(), 1);
54    let mut titles: Vec<String> = posts_per_user[0].iter().map(|p| p.title.clone()).collect();
55    titles.sort();
56    assert_eq!(titles, vec!["alpha".to_string(), "beta".to_string()]);
57
58    Ok(())
59}
60
61#[driver_test(id(ID), requires(sql))]
62pub async fn select_has_many_with_filter(t: &mut Test) -> Result<()> {
63    #[derive(Debug, toasty::Model)]
64    struct User {
65        #[key]
66        #[auto]
67        id: ID,
68        name: String,
69
70        #[has_many]
71        posts: toasty::HasMany<Post>,
72    }
73
74    #[derive(Debug, toasty::Model)]
75    struct Post {
76        #[key]
77        #[auto]
78        id: ID,
79        title: String,
80
81        #[index]
82        user_id: ID,
83
84        #[belongs_to(key = user_id, references = id)]
85        user: toasty::BelongsTo<User>,
86    }
87
88    let mut db = t.setup_db(models!(User, Post)).await;
89
90    toasty::create!(User {
91        name: "Alice",
92        posts: [Post::create().title("alpha")],
93    })
94    .exec(&mut db)
95    .await?;
96    toasty::create!(User {
97        name: "Bob",
98        posts: [
99            Post::create().title("beta one"),
100            Post::create().title("beta two"),
101        ],
102    })
103    .exec(&mut db)
104    .await?;
105
106    let posts_per_user: Vec<Vec<Post>> = User::filter(User::fields().name().eq("Bob"))
107        .select(User::fields().posts())
108        .exec(&mut db)
109        .await?;
110
111    assert_eq!(posts_per_user.len(), 1);
112    let mut titles: Vec<String> = posts_per_user[0].iter().map(|p| p.title.clone()).collect();
113    titles.sort();
114    assert_eq!(titles, vec!["beta one".to_string(), "beta two".to_string()]);
115
116    Ok(())
117}
118
119#[driver_test(id(ID), requires(sql))]
120pub async fn select_has_many_first(t: &mut Test) -> Result<()> {
121    #[derive(Debug, toasty::Model)]
122    struct User {
123        #[key]
124        #[auto]
125        id: ID,
126        name: String,
127
128        #[has_many]
129        posts: toasty::HasMany<Post>,
130    }
131
132    #[derive(Debug, toasty::Model)]
133    struct Post {
134        #[key]
135        #[auto]
136        id: ID,
137        title: String,
138
139        #[index]
140        user_id: ID,
141
142        #[belongs_to(key = user_id, references = id)]
143        user: toasty::BelongsTo<User>,
144    }
145
146    let mut db = t.setup_db(models!(User, Post)).await;
147
148    toasty::create!(User {
149        name: "Alice",
150        posts: [Post::create().title("alpha"), Post::create().title("beta"),],
151    })
152    .exec(&mut db)
153    .await?;
154
155    let posts: Option<Vec<Post>> = User::filter(User::fields().name().eq("Alice"))
156        .select(User::fields().posts())
157        .first()
158        .exec(&mut db)
159        .await?;
160
161    let posts = posts.expect("first() returned None for a matching user");
162    let mut titles: Vec<String> = posts.iter().map(|p| p.title.clone()).collect();
163    titles.sort();
164    assert_eq!(titles, vec!["alpha".to_string(), "beta".to_string()]);
165
166    Ok(())
167}