Last active
September 8, 2022 20:17
-
-
Save tjaved573/e8a555cc1014228fabf5a55a804f81eb to your computer and use it in GitHub Desktop.
Rails Association Guide
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#// RAILS ASSOCIATIONS | |
class Author | |
has_one :book | |
# indicates that one other model has a reference to this model. | |
# Rails assumes that Books.author_id is a foreign key to Author.id in this table. | |
end | |
class Book < AR | |
belongs_to :author | |
# tells Rails that books.author_id is linked to Author.id | |
# RAILS INFERS THE CLASS NAME FROM THE ASSOCIATION NAME. | |
end | |
# corresponding migration | |
class CreateBooks < ActiveRecord::Migration[7.0] | |
def change | |
create_table :books do |t| | |
t.belongs_to :author , [foreign_key: true] | |
# belongs_to does not enforce FK constraint, so we add DB level foreign key constraint. | |
# Each book knows their author, but author does not know its books. | |
# OR | |
t.references :author || t.integer :author_id | |
# creates a field called author_id in books table | |
t.datetime :published_at | |
t.timestamps | |
end | |
end | |
end | |
class CreateAuthors < ActiveRecord::Migration[7.0] | |
def change | |
create_table :authors do |t| | |
t.string :name | |
t.timestamps | |
end | |
end | |
end | |
#* ______________________________________________________ | |
#// For a has_one association the method to create a new associated object would be user.create_post. | |
#// For a has_many association the method to create a new associated object would be user.posts.create. | |
```ruby | |
a = Author.new(name:"Daniel Silva") | |
a.create_book(book_name:"The Last Heist") # creates both book, and associates it with the author | |
``` | |
#* ______________________________________________________ | |
# without has_one association in the model, if we run | |
a1 = Author.first | |
a1.book => gives error: method_missing': undefined method `book' for #<Author:0x000000010e9c7778> | |
#* basically has_[one|many] association creates a method with the same name as the attribute | |
#* so has_one adds a method to the AR oject which encapsulates this query: | |
```sql | |
SELECT "books".* FROM "books" WHERE "books"."author_id" = ? LIMIT ? [["author_id", 1], ["LIMIT", 1]] | |
``` | |
# direction => moves from author to book in the database. | |
#without belongs_to association in the model, if we run | |
b1 = Book.first | |
b1.author => gives error: method_missing': undefined method `author' for #<Book:0x000000010b7a31e8> | |
```sql | |
SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] | |
``` | |
# direction => moves from book to author in the database. | |
#* ______________________________________________________ | |
#// MANY-TO-MANY ASSOCIATIONS | |
#! p = Patient.first | |
#! p.appointments.create(location:"NY") | |
`raise_validation_error': Validation failed: Physician must exist (ActiveRecord::RecordInvalid) | |
# this ensures that all belongs_to for a model are met. | |
#! p.patients = all_p | |
```sql | |
Patient Load (0.1ms) SELECT "patients".* FROM "patients" INNER JOIN "appointments" ON "patients"."id" = "appointments"."patient_id" WHERE "appointments"."physician_id" = ? [["physician_id", 1]] | |
TRANSACTION (0.0ms) begin transaction | |
Appointment Create (0.3ms) INSERT INTO "appointments" ("created_at", "updated_at", "location", "physician_id", "patient_id") VALUES (?, ?, ?, ?, ?) [["created_at", "2022-04-24 13:47:50.003907"], ["updated_at", "2022-04-24 13:47:50.003907"], ["location", nil], ["physician_id", 1], ["patient_id", 2]] | |
Appointment Create (0.0ms) INSERT INTO "appointments" ("created_at", "updated_at", "location", "physician_id", "patient_id") VALUES (?, ?, ?, ?, ?) [["created_at", "2022-04-24 13:47:50.005083"], ["updated_at", "2022-04-24 13:47:50.005083"], ["location", nil], ["physician_id", 1], ["patient_id", 3]] | |
TRANSACTION (0.9ms) commit transaction | |
``` | |
#! patient.physicians | |
```sql | |
Physician Load (0.4ms) SELECT "physicians".* FROM "physicians" INNER JOIN "appointments" ON "physicians"."id" = "appointments"."physician_id" WHERE "appointments"."patient_id" = ? [["patient_id", 2]] | |
#* ______________________________________________________ | |
#// SETTING UP FOREIGN KEYS | |
# By convention, Rails assumes that the column used to hold the foreign key on this model | |
# is the name of the # association with the suffix _id added. The :foreign_key option lets you | |
# set the name of the foreign key # directly. | |
#// IF CLASS NAME IS NOT THAT OBVIOUS FOR AN ASSOCIATION | |
# If the name of the other model cannot be derived from the association name, you can use the | |
# :class_name # option to supply the model name. | |
# class_name is used when the name of the model cannot be derived from the association name, therefore we | |
# use class name to supply the model name. | |
``` ruby | |
class Book < ApplicationRecord | |
belongs_to :author, class_name: "Patron", | |
foreign_key: "patron_id" | |
end | |
``` | |
# we can also set the foreign key on the has_many side, if the foreign key column is something other | |
# than the name of the model (with has_many/has_one) with the suffix _id added. | |
``` ruby | |
class Author < ApplicationRecord | |
has_many :books, foreign_key: "cust_id" | |
end | |
``` | |
#* ______________________________________________________ | |
#// ADDING A SELF JOIN ASSOCIATION AFTER THE TABLE HAS BEEN CREATED | |
# in migration script to add mentor_id field on the authors table, add the link: | |
add_reference :authors, :mentor, references: :author, foreign_key: { to_table: :authors } | |
# this creates a reference to the mentor_id field on the authors table, and references itself using foreignKey as authors_id | |
# in the model itself: | |
``` ruby | |
has_many :subordinates, class_name: "Author", foreign_key: "mentor_id" | |
belongs_to :mentor, class_name: "Author" | |
``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment