diesel/associations/
mod.rs

1//! Traits related to relationships between multiple tables.
2//!
3//! Associations in Diesel are always child-to-parent.
4//! You can declare an association between two records with `#[diesel(belongs_to)]`.
5//! Unlike other ORMs, Diesel has no concept of `has many`
6//!
7//! ```rust
8//! # include!("../doctest_setup.rs");
9//! use schema::{posts, users};
10//!
11//! #[derive(Identifiable, Queryable, PartialEq, Debug)]
12//! #[diesel(table_name = users)]
13//! pub struct User {
14//!     id: i32,
15//!     name: String,
16//! }
17//!
18//! #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
19//! #[diesel(belongs_to(User))]
20//! #[diesel(table_name = posts)]
21//! pub struct Post {
22//!     id: i32,
23//!     user_id: i32,
24//!     title: String,
25//! }
26//!
27//! # fn main() {
28//! #     run_test().unwrap();
29//! # }
30//! #
31//! # fn run_test() -> QueryResult<()> {
32//! #     let connection = &mut establish_connection();
33//! #     use self::users::dsl::*;
34//! let user = users.find(2).get_result::<User>(connection)?;
35//! let users_post = Post::belonging_to(&user).first(connection)?;
36//! let expected = Post {
37//!     id: 3,
38//!     user_id: 2,
39//!     title: "My first post too".into(),
40//! };
41//! assert_eq!(expected, users_post);
42//! #     Ok(())
43//! # }
44//! ```
45//!
46//! Note that in addition to the `#[diesel(belongs_to)]` annotation, we also need to
47//! `#[derive(Associations)]`
48//!
49//! `#[diesel(belongs_to)]` is given the name of the struct that represents the parent.
50//! Both the parent and child must implement [`Identifiable`].
51//! The struct given to `#[diesel(belongs_to)]` must be in scope,
52//! so you will need `use some_module::User` if `User` is defined in another module.
53//!
54//! If the parent record is generic over lifetimes, they can be written as `'_`.
55//! You will also need to wrap the type in quotes until
56//! `unrestricted_attribute_tokens` is stable.
57//!
58//! ```rust
59//! # include!("../doctest_setup.rs");
60//! # use schema::{posts, users};
61//! # use std::borrow::Cow;
62//! #
63//! #[derive(Identifiable)]
64//! #[diesel(table_name = users)]
65//! pub struct User<'a> {
66//!     id: i32,
67//!     name: Cow<'a, str>,
68//! }
69//!
70//! #[derive(Associations)]
71//! #[diesel(belongs_to(User<'_>))]
72//! #[diesel(table_name = posts)]
73//! pub struct Post {
74//!     id: i32,
75//!     user_id: i32,
76//!     title: String,
77//! }
78//! #
79//! # fn main() {}
80//! ```
81//!
82//!
83//! By default, Diesel assumes that your foreign keys will follow the convention `table_name_id`.
84//! If your foreign key has a different name,
85//! you can provide the `foreign_key` argument to `#[diesel(belongs_to)]`.
86//! For example, `#[diesel(belongs_to(Foo, foreign_key = mykey))]`.
87//!
88//! Associated data is typically loaded in multiple queries (one query per table).
89//! This is usually more efficient than using a join,
90//! especially if 3 or more tables are involved.
91//! For most datasets,
92//! using a join to load in a single query transmits so much duplicate data
93//! that it costs more time than the extra round trip would have.
94//!
95//! You can load the children for one or more parents using
96//! [`belonging_to`]
97//!
98//! [`belonging_to`]: crate::query_dsl::BelongingToDsl::belonging_to
99//!
100//! ```rust
101//! # include!("../doctest_setup.rs");
102//! # use schema::users;
103//! # use schema::posts;
104//! #
105//! # #[derive(Debug, PartialEq, Identifiable, Queryable)]
106//! # pub struct User {
107//! #     id: i32,
108//! #     name: String,
109//! # }
110//! #
111//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)]
112//! # #[diesel(belongs_to(User))]
113//! # pub struct Post {
114//! #     id: i32,
115//! #     user_id: i32,
116//! #     title: String,
117//! # }
118//! #
119//! # fn main() {
120//! #   use self::users::dsl::*;
121//! #   let connection = &mut establish_connection();
122//! #
123//! let user = users
124//!     .find(1)
125//!     .first::<User>(connection)
126//!     .expect("Error loading user");
127//! let post_list = Post::belonging_to(&user)
128//!     .load::<Post>(connection)
129//!     .expect("Error loading posts");
130//! let expected = vec![
131//!     Post {
132//!         id: 1,
133//!         user_id: 1,
134//!         title: "My first post".to_string(),
135//!     },
136//!     Post {
137//!         id: 2,
138//!         user_id: 1,
139//!         title: "About Rust".to_string(),
140//!     },
141//! ];
142//!
143//! assert_eq!(post_list, expected);
144//! # }
145//! ```
146//!
147//! If you're coming from other ORMs, you'll notice that this design is quite different from most.
148//! There you would have an instance method on the parent, or have the children stored somewhere on
149//! the posts. This design leads to many problems, including [N+1 query
150//! bugs][load-your-entire-database-into-memory-lol], and runtime errors when accessing an
151//! association that isn't there.
152//!
153//! [load-your-entire-database-into-memory-lol]: https://stackoverflow.com/q/97197/1254484
154//!
155//! In Diesel, data and its associations are considered to be separate. If you want to pass around
156//! a user and all of its posts, that type is `(User, Vec<Post>)`.
157//!
158//! Next lets look at how to load the children for more than one parent record.
159//! [`belonging_to`] can be used to load the data, but we'll also need to group it
160//! with its parents. For this we use an additional method [`grouped_by`].
161//!
162//! [`grouped_by`]: GroupedBy::grouped_by
163//! [`belonging_to`]: crate::query_dsl::BelongingToDsl::belonging_to
164//!
165//! ```rust
166//! # include!("../doctest_setup.rs");
167//! # use schema::{posts, users};
168//! #
169//! # #[derive(Identifiable, Queryable)]
170//! # pub struct User {
171//! #     id: i32,
172//! #     name: String,
173//! # }
174//! #
175//! # #[derive(Debug, PartialEq)]
176//! # #[derive(Identifiable, Queryable, Associations)]
177//! # #[diesel(belongs_to(User))]
178//! # pub struct Post {
179//! #     id: i32,
180//! #     user_id: i32,
181//! #     title: String,
182//! # }
183//! #
184//! # fn main() {
185//! #     run_test();
186//! # }
187//! #
188//! # fn run_test() -> QueryResult<()> {
189//! #     let connection = &mut establish_connection();
190//! #     use self::users::dsl::*;
191//! #     use self::posts::dsl::{posts, title};
192//! let sean = users.filter(name.eq("Sean")).first::<User>(connection)?;
193//! let tess = users.filter(name.eq("Tess")).first::<User>(connection)?;
194//!
195//! let seans_posts = Post::belonging_to(&sean)
196//!     .select(title)
197//!     .load::<String>(connection)?;
198//! assert_eq!(vec!["My first post", "About Rust"], seans_posts);
199//!
200//! // A vec or slice can be passed as well
201//! let more_posts = Post::belonging_to(&vec![sean, tess])
202//!     .select(title)
203//!     .load::<String>(connection)?;
204//! assert_eq!(
205//!     vec!["My first post", "About Rust", "My first post too"],
206//!     more_posts
207//! );
208//! #     Ok(())
209//! # }
210//! ```
211//!
212//! Typically you will want to group up the children with their parents.
213//! In other ORMs, this is often called a `has_many` relationship.
214//! Diesel provides support for doing this grouping, once the data has been
215//! loaded.
216//!
217//! [`grouped_by`] is called on a `Vec<Child>` with a `&[Parent]`.
218//! The return value will be `Vec<Vec<Child>>` indexed to match their parent.
219//! Or to put it another way, the returned data can be passed to `zip`,
220//! and it will be combined with its parent.
221//!
222//! ```rust
223//! # include!("../doctest_setup.rs");
224//! # use schema::{posts, users};
225//! #
226//! # #[derive(Identifiable, Queryable, PartialEq, Debug)]
227//! # pub struct User {
228//! #     id: i32,
229//! #     name: String,
230//! # }
231//! #
232//! # #[derive(Debug, PartialEq)]
233//! # #[derive(Identifiable, Queryable, Associations)]
234//! # #[diesel(belongs_to(User))]
235//! # pub struct Post {
236//! #     id: i32,
237//! #     user_id: i32,
238//! #     title: String,
239//! # }
240//! #
241//! # fn main() {
242//! #     run_test();
243//! # }
244//! #
245//! # fn run_test() -> QueryResult<()> {
246//! #     let connection = &mut establish_connection();
247//! let users = users::table.load::<User>(connection)?;
248//! let posts = Post::belonging_to(&users)
249//!     .load::<Post>(connection)?
250//!     .grouped_by(&users);
251//! let data = users.into_iter().zip(posts).collect::<Vec<_>>();
252//!
253//! let expected_data = vec![
254//!     (
255//!         User {
256//!             id: 1,
257//!             name: "Sean".into(),
258//!         },
259//!         vec![
260//!             Post {
261//!                 id: 1,
262//!                 user_id: 1,
263//!                 title: "My first post".into(),
264//!             },
265//!             Post {
266//!                 id: 2,
267//!                 user_id: 1,
268//!                 title: "About Rust".into(),
269//!             },
270//!         ],
271//!     ),
272//!     (
273//!         User {
274//!             id: 2,
275//!             name: "Tess".into(),
276//!         },
277//!         vec![Post {
278//!             id: 3,
279//!             user_id: 2,
280//!             title: "My first post too".into(),
281//!         }],
282//!     ),
283//! ];
284//!
285//! assert_eq!(expected_data, data);
286//! #     Ok(())
287//! # }
288//! ```
289//!
290//! [`grouped_by`] can be called multiple times
291//! if you have multiple children or grandchildren.
292//!
293//! For example, this code will load some users,
294//! all of their posts,
295//! and all of the comments on those posts.
296//! Explicit type annotations have been added
297//! to make each line a bit more clear.
298//!
299//! ```rust
300//! # include!("../doctest_setup.rs");
301//! # use schema::{users, posts, comments};
302//! #
303//! # #[derive(Debug, PartialEq, Identifiable, Queryable)]
304//! # pub struct User {
305//! #     id: i32,
306//! #     name: String,
307//! # }
308//! #
309//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)]
310//! # #[diesel(belongs_to(User))]
311//! # pub struct Post {
312//! #     id: i32,
313//! #     user_id: i32,
314//! #     title: String,
315//! # }
316//! #
317//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)]
318//! # #[diesel(belongs_to(Post))]
319//! # pub struct Comment {
320//! #     id: i32,
321//! #     post_id: i32,
322//! #     body: String,
323//! # }
324//! #
325//! # fn main() {
326//! #   let connection = &mut establish_connection();
327//! #
328//! let users: Vec<User> = users::table
329//!     .load::<User>(connection)
330//!     .expect("error loading users");
331//! let posts: Vec<Post> = Post::belonging_to(&users)
332//!     .load::<Post>(connection)
333//!     .expect("error loading posts");
334//! let comments: Vec<Comment> = Comment::belonging_to(&posts)
335//!     .load::<Comment>(connection)
336//!     .expect("Error loading comments");
337//! let grouped_comments: Vec<Vec<Comment>> = comments.grouped_by(&posts);
338//! let posts_and_comments: Vec<Vec<(Post, Vec<Comment>)>> =
339//!     posts.into_iter().zip(grouped_comments).grouped_by(&users);
340//! let result: Vec<(User, Vec<(Post, Vec<Comment>)>)> =
341//!     users.into_iter().zip(posts_and_comments).collect();
342//! let expected = vec![
343//!     (
344//!         User {
345//!             id: 1,
346//!             name: "Sean".to_string(),
347//!         },
348//!         vec![
349//!             (
350//!                 Post {
351//!                     id: 1,
352//!                     user_id: 1,
353//!                     title: "My first post".to_string(),
354//!                 },
355//!                 vec![Comment {
356//!                     id: 1,
357//!                     post_id: 1,
358//!                     body: "Great post".to_string(),
359//!                 }],
360//!             ),
361//!             (
362//!                 Post {
363//!                     id: 2,
364//!                     user_id: 1,
365//!                     title: "About Rust".to_string(),
366//!                 },
367//!                 vec![Comment {
368//!                     id: 2,
369//!                     post_id: 2,
370//!                     body: "Yay! I am learning Rust".to_string(),
371//!                 }],
372//!             ),
373//!         ],
374//!     ),
375//!     (
376//!         User {
377//!             id: 2,
378//!             name: "Tess".to_string(),
379//!         },
380//!         vec![(
381//!             Post {
382//!                 id: 3,
383//!                 user_id: 2,
384//!                 title: "My first post too".to_string(),
385//!             },
386//!             vec![Comment {
387//!                 id: 3,
388//!                 post_id: 3,
389//!                 body: "I enjoyed your post".to_string(),
390//!             }],
391//!         )],
392//!     ),
393//! ];
394//!
395//! assert_eq!(result, expected);
396//! # }
397//! ```
398//!
399//! And that's it.
400//! It may seem odd to have load, group, and zip be explicit separate steps
401//! if you are coming from another ORM.
402//! However, the goal is to provide simple building blocks which can
403//! be used to construct the complex behavior applications need.
404mod belongs_to;
405
406use std::hash::Hash;
407
408use crate::query_source::Table;
409
410pub use self::belongs_to::{BelongsTo, GroupedBy, TryGroupedByError};
411
412#[doc(inline)]
413pub use diesel_derives::Associations;
414
415/// This trait indicates that a struct is associated with a single database table.
416///
417/// This trait is implemented by structs which implement `Identifiable`,
418/// as well as database tables themselves.
419pub trait HasTable {
420    /// The table this type is associated with.
421    type Table: Table;
422
423    /// Returns the table this type is associated with.
424    fn table() -> Self::Table;
425}
426
427impl<T: HasTable> HasTable for &T {
428    type Table = T::Table;
429
430    fn table() -> Self::Table {
431        T::table()
432    }
433}
434
435/// This trait indicates that a struct represents a single row in a database table.
436///
437/// This must be implemented to use associations.
438/// Additionally, implementing this trait allows you to pass your struct to `update`
439/// (`update(&your_struct)` is equivalent to
440/// `update(YourStruct::table().find(&your_struct.primary_key())`).
441///
442/// This trait is usually implemented on a reference to a struct,
443/// not on the struct itself. It can be [derived](derive@Identifiable).
444pub trait Identifiable: HasTable {
445    /// The type of this struct's identifier.
446    ///
447    /// For single-field primary keys, this is typically `&'a i32`, or `&'a String`
448    /// For composite primary keys, this is typically `(&'a i32, &'a i32)`
449    /// or `(&'a String, &'a String)`, etc.
450    type Id: Hash + Eq;
451
452    /// Returns the identifier for this record.
453    ///
454    /// This takes `self` by value, not reference.
455    /// This is because composite primary keys
456    /// are typically stored as multiple fields.
457    /// We could not return `&(String, String)` if each string is a separate field.
458    ///
459    /// Because of Rust's rules about specifying lifetimes,
460    /// this means that `Identifiable` is usually implemented on references
461    /// so that we have a lifetime to use for `Id`.
462    fn id(self) -> Self::Id;
463}
464
465#[doc(inline)]
466pub use diesel_derives::Identifiable;