昨天建立了Product
Model,今天要繼續建立商品款式 ProductStyle
Model ,並讓這兩個 Model 產生一對多的關聯(一個product
可以有多個product_style
)
create
方法建立 product1 的商品款式 style1product_styles=
方法建立商品款式 style2Model之間的關聯,主要有三種
因為has_one
跟has_many
性質差不多,而且實務上has_many
最常用,今天就只介紹has_many (ㄧ對多)
。
昨天透過:
rails generate model product title price:float description:text
產生了products
這個 model,又在migration
修改了一些欄位屬性
今天一樣的起手式,先產生ProductStyle
Model
rails generate model product_style title price:float sku product_id:integer
上面這段code,多了sku
商品編號,還有product_id
。其中product_id
是為了讓belongs_to
或has_many
關係成立,而設置的外部鍵(foreign key)欄位,這時關聯還沒建立,要在model檔案設定後才真的建立好關聯。
一樣記得跑rake db:migrate
,就建立好product_style
資料表了。
打開schema.rb
來看:
create_table "product_styles", force: :cascade do |t|
t.string "title"
t.float "price"
t.string "sku"
t.integer "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
成了!
一對多關聯,畫圖給大家參考:
在app/models/product.rb
,加上類別方法has_many
class Product < ApplicationRecord
has_many :product_styles
end
設定has_many :product_styles
後會多了以下幾個方法:
product1 = Product.create(title: '果乾', price: 995)
#=> #<Product id: 3, title: "果乾", price: 995.0, description: "要買要快", created_at: "2019-09-29 23:57:54", updated_at: "2019-09-29 23:57:54">
styles = product1.product_styles
# => #<ActiveRecord::Associations::CollectionProxy []>
styles.size #=> 0
如果不加has_many :product_styles
,第二行就會噴錯undefined method 'product_styles'
。
又因為product 1 還沒有任何商品款式,所以第三行回傳0,
create
方法建立 product1 的商品款式 style1style1 = product1.product_styles.create(title: '芒果乾', price: 1450)
# => #<ProductStyle id: 1, title: "芒果乾", price: 1450.0, sku: nil, product_id: 3, created_at: "2019-09-30 00:14:05", updated_at: "2019-09-30 00:14:05">
外部鍵product_id
自動帶入3
(product1的id
)。外部鍵的作用就是當我輸入
product1.product_styles
會先產生一段SQL:
# => ProductStyle Load (0.3ms) SELECT "product_styles".* FROM "product_styles" WHERE "product_styles"."product_id" = $1 LIMIT $2 [["product_id", 3], ["LIMIT", 11]]
這段SQL目的是在product_styles
資料表裡,找出product_id
欄位跟product1
一樣是3
的每一筆資料。
product_styles=
方法建立商品款式 style2style2 = ProductStyle.new(title: '香蕉乾', price: '462')
# => #<ProductStyle id: nil, title: "香蕉乾", price: 462.0, sku: nil, product_id: nil, created_at: nil, updated_at: nil>
product1.product_styles=[style1, style2]
# => [#<ProductStyle id: 1, title: "芒果乾", price: 1450.0, sku: nil, product_id: 3, created_at: "2019-09-30 00:14:05", updated_at: "2019-09-30 00:14:05">, #<ProductStyle id: 2, title: "香蕉乾", price: 462.0, sku: nil, product_id: 3, created_at: "2019-09-30 00:26:57", updated_at: "2019-09-30 00:26:57">]
如果需要透過product_styles
裡的資料來反查相對應的products
資料,我們要在app/models/product_style.rb
加上belongs_to
方法。
class ProductStyle < ApplicationRecord
belongs_to :product
end
如此一來在console就可以找到style1是屬於哪個商品的款式
style1.product
# => #<Product id: 3, title: "果乾", price: 995.0, description: "要買要快", created_at: "2019-09-29 23:57:54", updated_at: "2019-09-29 23:57:54">
Foreign key 幫助找出其他資料表關聯的資料
一年前面試的時候,被問到Foreign key該放在哪個table?products
還是product_styles
?
記住口訣:有Foreign Key的Model,就是設定belongs_to的Model。
剛才是product_style
belongs_to product
,所以是放在ProductStyle Model喔
在Rails 5.1以後,如果是belongs_to
的Model,每一筆資料存入DB前,必須要有foreign key,不然會噴錯。像這樣:
style2 = ProductStyle.create!(title: '香蕉乾', price: '462')
# => ActiveRecord::RecordInvalid (Validation failed: Product must exist)
所以前面的style2
先用.new來建立,這時還沒有存入DB,指定給product1才真正存進去。或是在create
時指定product_id
為3
style3 = ProductStyle.create!(title: '香蕉乾', price: '462', product_id: 3)
has_many還有一些方便的參數,可以讓每次查找更有效率
原本
product1.product_styles
順序會是流水編號id
小的在前面,改成用order指定id順序大排到小:
class Product < ApplicationRecord
has_many :product_styles, ->{ order("id DESC") }
end
參數->{ order("id DESC") }
是lambda,之後會介紹。
也可以串連where條件:
class Product < ApplicationRecord
has_many :product_styles, ->{ where(["created_at > ?", Time.now - 7.days]).order("id DESC") }
end
覺得每次要找product
的款式都要輸入.product_styles
,很麻煩,也可以使用class_name
參數,has_many 的第一個參數改用:styles
。
class Product < ApplicationRecord
has_many :styles, class_name: 'ProductStyle'
end