一對多關聯


一個客戶會擁有多個訂單,這形成了一對多關聯,可以使用has_many

# app/models/customer.rb
class Customer < ActiveRecord::Base
    has_many :orders
end

若如上定義,預設在orders表格上,必須有個customer_id欄位作為外鍵,參考至customers表格的id主鍵。例如:

class CreateCustomers < ActiveRecord::Migration
  def change
    create_table :customers do |t|
      t.string :name

      t.timestamps
    end
  end
end

class CreateOrders < ActiveRecord::Migration
  def change
    create_table :orders do |t|
      t.integer :customer_id  # 外鍵
      t.datetime :order_date

      t.timestamps
    end
  end
end

has_many會在模型上加入一些方法:

  • collection(force_reload = false)
  • collection<<(object, …)
  • collection.delete(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是關聯的物件複數名稱,例如orders這樣的名稱。一個儲存的例子如下所示:

c = Customer.new(:name => "Justin")
c.orders.build(:order_date => Time.now)  # 建立第一筆訂單
c.orders.build(:order_date => Time.now) 
# 建立第二筆訂單
c.save

儲存時會先儲存Customer,再分別儲存兩筆Order,也就是會下三次SQL:

INSERT INTO "customers" ("created_at", "name", "updated_at") VALUES (?, ?, ?)
INSERT INTO "orders" ("created_at", "customer_id", "order_date", "updated_at") VALUES (?, ?, ?, ?)
INSERT INTO "orders" ("created_at", "customer_id", "order_date", "updated_at") VALUES (?, ?, ?, ?)

如果查找客戶時,預設訂單是不會一併查詢出來的。例如:

c = Customer.find(1)
orders = c.orders # 此時才從orders表格中查詢訂單

當然,客戶與訂單的關係,也可以說某訂單屬於某客戶,也就是如下:

# app/models/order.rb
class Order < ActiveRecord::Base
    belongs_to :customer
end

從訂單看客戶,就是多對一的關係,此時就必須如下儲存:

c = Customer.new(:name => "Justin")
o1 = Order.new(:customer => c, :order_date => Time.now)
o2 = Order.new(:customer => c, :order_date => Time.now)

o1.save
o2.save

o1儲存時會先儲存c,以上會下三次SQL:

INSERT INTO "customers" ("created_at", "name", "updated_at") VALUES (?, ?, ?)
INSERT INTO "orders" ("created_at", "customer_id", "order_date", "updated_at") VALUES (?, ?, ?, ?)
INSERT INTO "orders" ("created_at", "customer_id", "order_date", "updated_at") VALUES (?, ?, ?, ?)

有關has_many,還有一些可設置的選項,可參考 A Guide to Active Record Associations 中 4.3 has_many Association Reference 的內容。