blob: 947af3933e21929e711b31c2acaa636615e3aba7 [file] [log] [blame]
//! Traits related to relationships between multiple tables.
//!
//! Associations in Diesel are always child-to-parent.
//! You can declare an association between two records with `#[belongs_to]`.
//! Unlike other ORMs, Diesel has no concept of `#[has_many`]
//!
//! ```rust
//! # #[macro_use] extern crate diesel;
//! # include!("../doctest_setup.rs");
//! use schema::{posts, users};
//!
//! #[derive(Identifiable, Queryable, PartialEq, Debug)]
//! #[table_name = "users"]
//! pub struct User {
//! id: i32,
//! name: String,
//! }
//!
//! #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
//! #[belongs_to(User)]
//! #[table_name = "posts"]
//! pub struct Post {
//! id: i32,
//! user_id: i32,
//! title: String,
//! }
//!
//! # fn main() {
//! # run_test().unwrap();
//! # }
//! #
//! # fn run_test() -> QueryResult<()> {
//! # let connection = establish_connection();
//! # use self::users::dsl::*;
//! let user = users.find(2).get_result::<User>(&connection)?;
//! let users_post = Post::belonging_to(&user)
//! .first(&connection)?;
//! let expected = Post { id: 3, user_id: 2, title: "My first post too".into() };
//! assert_eq!(expected, users_post);
//! # Ok(())
//! # }
//! ```
//!
//! Note that in addition to the `#[belongs_to]` annotation, we also need to
//! `#[derive(Associations)]`
//!
//! `#[belongs_to]` is given the name of the struct that represents the parent.
//! Both the parent and child must implement [`Identifiable`].
//! The struct given to `#[belongs_to]` must be in scope,
//! so you will need `use some_module::User` if `User` is defined in another module.
//!
//! If the parent record is generic over lifetimes, they can be written as `'_`.
//! You will also need to wrap the type in quotes until
//! `unrestricted_attribute_tokens` is stable.
//!
//! ```rust
//! # #[macro_use] extern crate diesel;
//! # include!("../doctest_setup.rs");
//! # use schema::{posts, users};
//! # use std::borrow::Cow;
//! #
//! #[derive(Identifiable)]
//! #[table_name = "users"]
//! pub struct User<'a> {
//! id: i32,
//! name: Cow<'a, str>,
//! }
//!
//! #[derive(Associations)]
//! #[belongs_to(parent = "User<'_>")]
//! #[table_name = "posts"]
//! pub struct Post {
//! id: i32,
//! user_id: i32,
//! title: String,
//! }
//! #
//! # fn main() {}
//! ```
//!
//! [`Identifiable`]: trait.Identifiable.html
//!
//! By default, Diesel assumes that your foreign keys will follow the convention `table_name_id`.
//! If your foreign key has a different name,
//! you can provide the `foreign_key` argument to `#[belongs_to]`.
//! For example, `#[belongs_to(Foo, foreign_key = "mykey")]`.
//!
//! Associated data is typically loaded in multiple queries (one query per table).
//! This is usually more efficient than using a join,
//! especially if 3 or more tables are involved.
//! For most datasets,
//! using a join to load in a single query transmits so much duplicate data
//! that it costs more time than the extra round trip would have.
//!
//! You can load the children for one or more parents using
//! [`belonging_to`]
//!
//! [`belonging_to`]: ../query_dsl/trait.BelongingToDsl.html#tymethod.belonging_to
//!
//! ```rust
//! # #[macro_use] extern crate diesel;
//! # include!("../doctest_setup.rs");
//! # use schema::users;
//! # use schema::posts;
//! #
//! # #[derive(Debug, PartialEq, Identifiable, Queryable)]
//! # pub struct User {
//! # id: i32,
//! # name: String,
//! # }
//! #
//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)]
//! # #[belongs_to(User)]
//! # pub struct Post {
//! # id: i32,
//! # user_id: i32,
//! # title: String,
//! # }
//! #
//! # fn main() {
//! # use self::users::dsl::*;
//! # let connection = establish_connection();
//! #
//! let user = users.find(1).first::<User>(&connection).expect("Error loading user");
//! let post_list = Post::belonging_to(&user)
//! .load::<Post>(&connection)
//! .expect("Error loading posts");
//! let expected = vec![
//! Post { id: 1, user_id: 1, title: "My first post".to_string() },
//! Post { id: 2, user_id: 1, title: "About Rust".to_string() },
//! ];
//!
//! assert_eq!(post_list, expected);
//! # }
//! ```
//!
//! If you're coming from other ORMs, you'll notice that this design is quite different from most.
//! There you would have an instance method on the parent, or have the children stored somewhere on
//! the posts. This design leads to many problems, including [N+1 query
//! bugs][load-your-entire-database-into-memory-lol], and runtime errors when accessing an
//! association that isn't there.
//!
//! [load-your-entire-database-into-memory-lol]: https://stackoverflow.com/q/97197/1254484
//!
//! In Diesel, data and its associations are considered to be separate. If you want to pass around
//! a user and all of its posts, that type is `(User, Vec<Post>)`.
//!
//! Next lets look at how to load the children for more than one parent record.
//! [`belonging_to`] can be used to load the data, but we'll also need to group it
//! with its parents. For this we use an additional method [`grouped_by`].
//!
//! [`grouped_by`]: trait.GroupedBy.html#tymethod.grouped_by
//! [`belonging_to`]: ../query_dsl/trait.BelongingToDsl.html#tymethod.belonging_to
//!
//! ```rust
//! # #[macro_use] extern crate diesel;
//! # include!("../doctest_setup.rs");
//! # use schema::{posts, users};
//! #
//! # #[derive(Identifiable, Queryable)]
//! # pub struct User {
//! # id: i32,
//! # name: String,
//! # }
//! #
//! # #[derive(Debug, PartialEq)]
//! # #[derive(Identifiable, Queryable, Associations)]
//! # #[belongs_to(User)]
//! # pub struct Post {
//! # id: i32,
//! # user_id: i32,
//! # title: String,
//! # }
//! #
//! # fn main() {
//! # run_test();
//! # }
//! #
//! # fn run_test() -> QueryResult<()> {
//! # let connection = establish_connection();
//! # use self::users::dsl::*;
//! # use self::posts::dsl::{posts, title};
//! let sean = users.filter(name.eq("Sean")).first::<User>(&connection)?;
//! let tess = users.filter(name.eq("Tess")).first::<User>(&connection)?;
//!
//! let seans_posts = Post::belonging_to(&sean)
//! .select(title)
//! .load::<String>(&connection)?;
//! assert_eq!(vec!["My first post", "About Rust"], seans_posts);
//!
//! // A vec or slice can be passed as well
//! let more_posts = Post::belonging_to(&vec![sean, tess])
//! .select(title)
//! .load::<String>(&connection)?;
//! assert_eq!(vec!["My first post", "About Rust", "My first post too"], more_posts);
//! # Ok(())
//! # }
//! ```
//!
//! Typically you will want to group up the children with their parents.
//! In other ORMs, this is often called a `has_many` relationship.
//! Diesel provides support for doing this grouping, once the data has been
//! loaded.
//!
//! [`grouped_by`] is called on a `Vec<Child>` with a `&[Parent]`.
//! The return value will be `Vec<Vec<Child>>` indexed to match their parent.
//! Or to put it another way, the returned data can be passed to `zip`,
//! and it will be combined with its parent.
//!
//! ```rust
//! # #[macro_use] extern crate diesel;
//! # include!("../doctest_setup.rs");
//! # use schema::{posts, users};
//! #
//! # #[derive(Identifiable, Queryable, PartialEq, Debug)]
//! # pub struct User {
//! # id: i32,
//! # name: String,
//! # }
//! #
//! # #[derive(Debug, PartialEq)]
//! # #[derive(Identifiable, Queryable, Associations)]
//! # #[belongs_to(User)]
//! # pub struct Post {
//! # id: i32,
//! # user_id: i32,
//! # title: String,
//! # }
//! #
//! # fn main() {
//! # run_test();
//! # }
//! #
//! # fn run_test() -> QueryResult<()> {
//! # let connection = establish_connection();
//! let users = users::table.load::<User>(&connection)?;
//! let posts = Post::belonging_to(&users)
//! .load::<Post>(&connection)?
//! .grouped_by(&users);
//! let data = users.into_iter().zip(posts).collect::<Vec<_>>();
//!
//! let expected_data = vec![
//! (
//! User { id: 1, name: "Sean".into() },
//! vec![
//! Post { id: 1, user_id: 1, title: "My first post".into() },
//! Post { id: 2, user_id: 1, title: "About Rust".into() },
//! ],
//! ),
//! (
//! User { id: 2, name: "Tess".into() },
//! vec![
//! Post { id: 3, user_id: 2, title: "My first post too".into() },
//! ],
//! ),
//! ];
//!
//! assert_eq!(expected_data, data);
//! # Ok(())
//! # }
//! ```
//!
//! [`grouped_by`] can be called multiple times
//! if you have multiple children or grandchildren.
//!
//! For example, this code will load some users,
//! all of their posts,
//! and all of the comments on those posts.
//! Explicit type annotations have been added
//! to make each line a bit more clear.
//!
//! ```rust
//! # #[macro_use] extern crate diesel;
//! # include!("../doctest_setup.rs");
//! # use schema::{users, posts, comments};
//! #
//! # #[derive(Debug, PartialEq, Identifiable, Queryable)]
//! # pub struct User {
//! # id: i32,
//! # name: String,
//! # }
//! #
//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)]
//! # #[belongs_to(User)]
//! # pub struct Post {
//! # id: i32,
//! # user_id: i32,
//! # title: String,
//! # }
//! #
//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)]
//! # #[belongs_to(Post)]
//! # pub struct Comment {
//! # id: i32,
//! # post_id: i32,
//! # body: String,
//! # }
//! #
//! # fn main() {
//! # let connection = establish_connection();
//! #
//! let users: Vec<User> = users::table.load::<User>(&connection)
//! .expect("error loading users");
//! let posts: Vec<Post> = Post::belonging_to(&users)
//! .load::<Post>(&connection)
//! .expect("error loading posts");
//! let comments: Vec<Comment> = Comment::belonging_to(&posts)
//! .load::<Comment>(&connection)
//! .expect("Error loading comments");
//! let grouped_comments: Vec<Vec<Comment>> = comments.grouped_by(&posts);
//! let posts_and_comments: Vec<Vec<(Post, Vec<Comment>)>> = posts
//! .into_iter()
//! .zip(grouped_comments)
//! .grouped_by(&users);
//! let result: Vec<(User, Vec<(Post, Vec<Comment>)>)> = users
//! .into_iter()
//! .zip(posts_and_comments)
//! .collect();
//! let expected = vec![
//! (
//! User { id: 1, name: "Sean".to_string() },
//! vec![
//! (
//! Post { id: 1, user_id: 1, title: "My first post".to_string() },
//! vec![ Comment { id: 1, post_id: 1, body: "Great post".to_string() } ]
//! ),
//! (
//! Post { id: 2, user_id: 1, title: "About Rust".to_string() },
//! vec![
//! Comment { id: 2, post_id: 2, body: "Yay! I am learning Rust".to_string() }
//! ]
//!
//! )
//! ]
//! ),
//! (
//! User { id: 2, name: "Tess".to_string() },
//! vec![
//! (
//! Post { id: 3, user_id: 2, title: "My first post too".to_string() },
//! vec![ Comment { id: 3, post_id: 3, body: "I enjoyed your post".to_string() } ]
//! )
//! ]
//! )
//! ];
//!
//! assert_eq!(result, expected);
//! # }
//! ```
//!
//! And that's it.
//! It may seem odd to have load, group, and zip be explicit separate steps
//! if you are coming from another ORM.
//! However, the goal is to provide simple building blocks which can
//! be used to construct the complex behavior applications need.
mod belongs_to;
use std::hash::Hash;
use crate::query_source::Table;
pub use self::belongs_to::{BelongsTo, GroupedBy};
/// This trait indicates that a struct is associated with a single database table.
///
/// This trait is implemented by structs which implement `Identifiable`,
/// as well as database tables themselves.
pub trait HasTable {
/// The table this type is associated with.
type Table: Table;
/// Returns the table this type is associated with.
fn table() -> Self::Table;
}
impl<'a, T: HasTable> HasTable for &'a T {
type Table = T::Table;
fn table() -> Self::Table {
T::table()
}
}
/// This trait indicates that a struct represents a single row in a database table.
///
/// This must be implemented to use associations.
/// Additionally, implementing this trait allows you to pass your struct to `update`
/// (`update(&your_struct)` is equivalent to
/// `update(YourStruct::table().find(&your_struct.primary_key())`).
///
/// This trait is usually implemented on a reference to a struct,
/// not the struct itself.
///
/// ### Deriving
///
/// This trait can be automatically derived by adding `#[derive(Identifiable)]`
/// to your struct.
/// By default, the "id" field is assumed to be a single field called `id`.
/// If it's not, you can put `#[primary_key(your_id)]` on your struct.
/// If you have a composite primary key, the syntax is `#[primary_key(id1, id2)]`.
///
/// By default, `#[derive(Identifiable)]` will assume that your table
/// name is the plural form of your struct name.
/// Diesel uses very simple pluralization rules.
/// It only adds an `s` to the end, and converts `CamelCase` to `snake_case`.
/// If your table name does not follow this convention
/// or the plural form isn't just an `s`,
/// you can specify the table name with `#[table_name = "some_table_name"]`.
/// Our rules for inferring table names is considered public API.
/// It will never change without a major version bump.
pub trait Identifiable: HasTable {
/// The type of this struct's identifier.
///
/// For single-field primary keys, this is typically `&'a i32`, or `&'a String`
/// For composite primary keys, this is typically `(&'a i32, &'a i32)`
/// or `(&'a String, &'a String)`, etc.
type Id: Hash + Eq;
/// Returns the identifier for this record.
///
/// This takes `self` by value, not reference.
/// This is because composite primary keys
/// are typically stored as multiple fields.
/// We could not return `&(String, String)` if each string is a separate field.
///
/// Because of Rust's rules about specifying lifetimes,
/// this means that `Identifiable` is usually implemented on references
/// so that we have a lifetime to use for `Id`.
fn id(self) -> Self::Id;
}