In Rails, an association is a connection between two Active Record models. Creating association between models make common operations simpler and easier in your code.
Consider the following example:
class Author < ApplicationRecord
end
class Book < ApplicationRecord
end
Without association, if we want to add a book to an existing author, we should do this:
@book = Book.create(published_at: Time.now, author_id: @author.id)
And when deleting an author, to ensure all his books are deleted as well, we should do this:
@books = Book.where(author_id: @author.id)
@books.each do |book|
book.destroy
end
@author.destroy
With Active Record associations, we can streamline these operations to:
class Author < ApplicationRecord
has_many :books, dependent: :destroy
end
class Book < ApplicationRecord
belongs_to :author
end
@book = @author.books.create(published_at: Time.now)
@author.destroy
There are three kinds of associations:
For one-to-one relationship, Rails provides us with these macro-style calls:
For one-to-one relationship, Rails provides us with these macro-style calls:
For one-to-one relationship, Rails provides us with these macro-style calls:
Polymorphic association is a slightly more advanced twist on associations. With polymorphic associations, a model can belong to more than one other model, on a single association.
This is how you define a has_one association in Rails.
class Supplier < ApplicationRecord
has_one :account
end
class Account < ApplicationRecord
belongs_to :supplier
end
The migration files may look like this.
class CreateSuppliers < ActiveRecord::Migration[5.0]
def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end
end
end
class CreateAccounts < ActiveRecord::Migration[5.0]
def change
create_table :accounts do |t|
t.belongs_to :supplier, index: true
t.string :account_number
t.timestamps
end
end
end
This is how you define a has_one through association in Rails.
class Supplier < ApplicationRecord
has_one :account
has_one :account_history, through: :account
end
class Account < ApplicationRecord
belongs_to :supplier
has_one :account_history
end
class AccountHistory < ApplicationRecord
belongs_to :account
end
The migration files may look like this.
class CreateSuppliers < ActiveRecord::Migration[5.0]
def change
create_table :suppliers do |t|
t.string :name
t.timestamps
end
end
end
class CreateAccounts < ActiveRecord::Migration[5.0]
def change
create_table :accounts do |t|
t.belongs_to :supplier, index: true
t.string :account_number
t.timestamps
end
end
end
class CreateAccountHistories < ActiveRecord::Migration[5.0]
def change
create_table :account_histories do |t|
t.belongs_to :account, index: true
t.integer :credit_rating
t.timestamps
end
end
end
This is how you define a has_many association in Rails.
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
end
The migration files may look like this.
class CreateAuthors < ActiveRecord::Migration[5.0]
def change
create_table :authors do |t|
t.string :name
t.timestamps
end
end
end
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
t.belongs_to :author, index: true
t.datetime :published_at
t.timestamps
end
end
end
This is how you define a has_many through association in Rails.
class Doctor < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :doctor
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :doctors, through: :appointments
end
The migration files may look like this.
class CreateDoctors < ActiveRecord::Migration[5.0]
def change
create_table :doctors do |t|
t.string :name
t.timestamps
end
end
end
class CreatePatients < ActiveRecord::Migration[5.0]
def change
create_table :patients do |t|
t.string :name
t.timestamps
end
end
end
class CreateAppointments < ActiveRecord::Migration[5.0]
def change
create_table :appointments do |t|
t.belongs_to :doctor, index: true
t.belongs_to :patient, index: true
t.datetime :appointment_date
t.timestamps
end
end
end
This is how you define a has_and_belongs_to_many association in Rails.
class Food < ApplicationRecord
has_and_belongs_to_many :tags
end
class Tags < ApplicationRecord
has_and_belongs_to_many :foods
end
The migration files may look like this.
# No need to create migration for Food as we already have it
class CreateTags < ActiveRecord::Migration[5.0]
def change
create_table :tags do |t|
t.string :name
t.timestamps
end
end
end
class CreateFoodsTags < ActiveRecord::Migration[5.0]
def change
create_table :foods_tags, id: false do |t|
t.belongs_to :food, index: true
t.belongs_to :tag, index: true
end
end
end
This is how you define a polymorphic association in Rails.
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Food < ApplicationRecord
has_many :pictures, as: :imageable
end
class Restaurant < ApplicationRecord
has_many :pictures, as: :imageable
end
The migration files may look like this.
class CreatePictures < ActiveRecord::Migration[5.0]
def change
create_table :pictures do |t|
t.string :name
t.references :imageable, polymorphic: true, index: true
t.timestamps
end
end
end
Sometimes we need to create a model that has a relation to itself. A model Employee, for instance, can have subordinates and managers that are themselves employees too.
class Employee < ApplicationRecord
has_many :subordinates, class_name: "Employee", foreign_key: "manager_id"
belongs_to :manager, class_name: "Employee"
end
The migration files may look like this.
class CreateEmployees < ActiveRecord::Migration[5.0]
def change
create_table :employees do |t|
t.references :manager, index: true
t.timestamps
end
end
end
When we use associations, we get several methods added to our model. For instance, when we declare that a Book belongs to an Author, we can call a method "book.author" for any instance of class Book.
Now let's see what methods are available for each kind of association.
"belongs_to" association adds these methods to our model:
association
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})
reload_association
# So, if we have a class named Book
# that belongs_to a class named Author,
# we can do:
book.author
book.author = Author.create(name: "Author 1")
book.build_author(name: "Author 1")
book.create_author(name: "Author 1")
book.create_author!(name: "Author 1")
book.reload_author
"has_one" association adds these methods to our model:
association
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})
reload_association
# So, if we have a class named Supplier
# that has_one a class named Account,
# we can do:
supplier.account
supplier.account = Account.create(name: "Accont 1")
supplier.build_account(name: "Account 1")
supplier.create_account(name: "Account 1")
supplier.create_account!(name: "Account 1")
supplier.reload_account
"has_many" and "has_and_belongs_to_many" association adds these methods to our model:
collection
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=(objects)
collection_singular_ids
collection_singular_ids=(ids)
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {}, ...)
collection.create(attributes = {})
collection.create!(attributes = {})
collection.reload
"has_many" and "has_and_belongs_to_many" association adds these methods to our model:
# So, if we have a class named Author
# that has_many a class named Book,
# we can do:
author.books
author.books << Book.create(title: "Book 1")
author.books.delete(@book1)
author.books.destroy(@book1)
author.books = Book.create([{title: "Book 1"}, {title: "Book 2"}])
author.book_ids
author.book_ids = Book.where("title like 'Book%'").ids
author.books.clear
author.books.empty?
author.books.size
author.books.find(title: 'Book 1')
author.books.where("title like 'Book%'")
author.books.exists?(title: "Book 1")
author.books.build(title: "Book 1")
author.books.create(title: "Book 1")
author.books.create!(title: "Book 1")
author.books.reload
1. Create an MVC for model named Tag with requirements:
2. Create an MVC for model named Restaurant with requirements:
3. Create an MVC for model named Review with requirements:
Of course, write all of these features in TDD fashion!