From a6faf080fe8345f62305a781c63066a8edc4d562 Mon Sep 17 00:00:00 2001 From: Adrianna Chang Date: Wed, 6 Sep 2023 20:51:12 -0400 Subject: [PATCH] Document associations between models with composite primary keys in Rails guides --- guides/source/association_basics.md | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 8c1e2523be..ba58152d1a 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -537,6 +537,70 @@ end ![Polymorphic Association Diagram](images/association_basics/polymorphic.png) +### Associations between Models with Composite Primary Keys + +Rails is often able to infer the primary key - foreign key information between associated models with composite +primary keys without needing extra information. Take the following example: + +```ruby +class Order < ApplicationRecord + self.primary_key = [:shop_id, :id] + has_many :books +end + +class Book < ApplicationRecord + belongs_to :order +end +``` + +Here, Rails assumes that the `:id` column should be used as the primary key for the association between an order +and its books, just as with a regular `has_many` / `belongs_to` association. It will infer that the foreign key column +on the `books` table is `:order_id`. Accessing a book's order: + +```ruby +order = Order.create!(id: [1, 2], status: "pending") +book = order.books.create!(title: "A Cool Book") + +book.reload.order +``` + +will generate the following SQL to access the order: + +```sql +SELECT * FROM orders WHERE id = 2 +``` + +This only works if the model's composite primary key contains the `:id` column, _and_ the column is unique for +all records. In order to use the full composite primary key in associations, set the `query_constraints` option on +the association. This option specifies a composite foreign key on the association: all columns in the foreign key will +be used when querying the associated record(s). For example: + +```ruby +class Author < ApplicationRecord + self.primary_key = [:first_name, :last_name] + has_many :books, query_constraints: [:first_name, :last_name] +end + +class Book < ApplicationRecord + belongs_to :author, query_constraints: [:author_first_name, :author_last_name] +end +``` + +Accessing a book's author: + +```ruby +author = Author.create!(first_name: "Jane", last_name: "Doe") +book = author.books.create!(title: "A Cool Book") + +book.reload.author +``` + +will use `:first_name` _and_ `:last_name` in the SQL query: + +```sql +SELECT * FROM authors WHERE first_name = 'Jane' AND last_name = 'Doe' +``` + ### Self Joins In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as between manager and subordinates. This situation can be modeled with self-joining associations: