Skip to content

Instantly share code, notes, and snippets.

@mcelaney
Last active August 29, 2015 14:21
Show Gist options
  • Save mcelaney/71ca7b729d74b86f44c7 to your computer and use it in GitHub Desktop.
Save mcelaney/71ca7b729d74b86f44c7 to your computer and use it in GitHub Desktop.

Associations in Rails

belongs_to


| Orders | _____________ | - id | | Customers | | - customer_id | -----------> | - id |


The class

class Order < ActiveRecord::Base
  belongs_to :customer
end


order = Order.first
order.customer

=> returns the customer for order

has_many


| Orders | _____________ | - id | | Customers | | - customer_id | <----------- | - id |


The class

class Order < ActiveRecord::Base
  belongs_to :customer
end

class Customer < ActiveRecord::Base
  has_many :orders
end

customer = Customer.first
customer.orders

=> returns the orders for customer

You might also see inverse_of...

class Order < ActiveRecord::Base
  belongs_to :customer, inverse_of: :orders
end

class Customer < ActiveRecord::Base
  has_many :orders, inverse_of: :customer
end

Migration for this:

class CreateOrders < ActiveRecord::Migration
  def change
    create_table :customers do |t|
      t.string :name
      t.timestamps null: false
    end

    create_table :orders do |t|
      t.belongs_to :customer, index: true
      t.datetime :order_date
      t.timestamps null: false
    end
  end
end

...or...

class CreateOrders < ActiveRecord::Migration
  def change
    create_table :orders do |t|
      t.integer :customer_id
      t.datetime :order_date
      t.timestamps null: false
    end

    add_index :orders, :customer_id
  end
end

Associations can be nested: belong_to


| OrderItem | _________________ | - id | | Order | _____________ | - order_id | -----------> | - id | | Customer | | - product_id | | - customer_id | -----------> | - id |


Classes for this

class OrderItem < ActiveRecord::Base
  belongs_to :order
end

class Order < ActiveRecord::Base
  belongs_to :customer
  has_many :order_items
end

class Customer < ActiveRecord::Base
  has_many :orders
end

Using this...

customer = Customer.first
customer.orders.first.order_items

=> returns order items for first order

customer.orders.map(&:order_items)

=> returns array of all order items for customer

Other direction

order_item = OrderItem.first
order_item.order.customer

=> Returns customer for the order_item

Could be better... create a customer method

class OrderItem < ActiveRecord::Base
  belongs_to :order

  def customer
    order.custoemr
  end
end

OrderItem.first.customer
=> returns customer for the order_item

Or even better... delegate

class OrderItem < ActiveRecord::Base
  delegate :customer, to: :order
  belongs_to :order
end

OrderItem.first.customer
=> returns customer for the order_item

has_many_through


| OrderItem | _________________ | - id | | Order | _____________ | - order_id | <----------- | - id | | Customer | | - product_id | | - customer_id | <----------- | - id |


Classes for this

class OrderItem < ActiveRecord::Base
  delegate :customer, to: :order
  belongs_to :order
end

class Order < ActiveRecord::Base
  belongs_to :customer
  has_many :order_items
end

class Customer < ActiveRecord::Base
  has_many :orders
  has_many :order_items, through: :orders
end

Using this

customer = Customer.first
customer.order_items

=> returns all customer order items... same as
customer.orders.map(&:order_items)

=> and
customer = Customer.first
customer.orders.first.order_items
=> still returns order items for an order

has_and_belongs_to_many


| Users | ____ ___________________ | - id | \ | AddressesUsers | ------------- -----> | - user_id | _____________ /-----> | - address_id | | Addresses | / ------------------- | - id | ___/

Class for this

class User < ActiveRecord::Base
  has_and_belongs_to_many :addresses
end

class Address < ActiveRecord::Base
  has_and_belongs_to_many :users
end

Address.first.users
=> returns all users at an address

User.first.addresses
=> returns all addresses for a user

The migration

class CreateUsersAndAddresses < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps null: false
    end

    create_table :addresses do |t|
      t.string :street
      t.timestamps null: false
    end

    create_table :addresses_users, id: false do |t|
      t.belongs_to :user, index: true
      t.belongs_to :address, index: true
    end
  end
end

Join table


| Users | ____ _________________ | - id | \ | AddressUsers | ------------- -----> | - user_id | _____________ /-----> | - address_id | | Addresses | / ----------------- | - id | ___/

Classes for this

class User < ActiveRecord::Base
  has_many :address_users
  has_many :addresses, through: :address_users
end

class Address < ActiveRecord::Base
  has_many :address_users
  has_many :users, through: :address_users
end

class AddressUser
  belongs_to :user
  belongs_to :address
end

...works the same way
Address.first.users
=> returns all users at an address

User.first.addresses
=> returns all addresses for a user

The migration

class CreateUsersAndAddresses < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps null: false
    end

    create_table :addresses do |t|
      t.string :street
      t.timestamps null: false
    end

    create_table :addresses_users, id: false do |t|
      t.belongs_to :user, index: true
      t.belongs_to :address, index: true
    end
  end
end

There are others...

  • has_one
  • has_one :through
  • polymorphic
  • self joins

But we'll cover those later

questions

Do has any?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment