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