Skip to content

Instantly share code, notes, and snippets.

@tjaved573
Last active September 8, 2022 20:17
Show Gist options
  • Save tjaved573/e8a555cc1014228fabf5a55a804f81eb to your computer and use it in GitHub Desktop.
Save tjaved573/e8a555cc1014228fabf5a55a804f81eb to your computer and use it in GitHub Desktop.
Rails Association Guide
#// 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