介紹完 migrate 之後,今天想來講講 model 與 model 之間,該怎麼有關聯呢?
比方說:每個駕駛員可以駕駛一台
鋼彈,而每台鋼彈都擁有一名
駕駛員。
這段話敘述的是 1 對 1的關係,所以我們先來建立這樣的模型吧!
rails g model Pilot name region new_type:boolean
Running via Spring preloader in process 60733
invoke active_record
create db/migrate/20190925141652_create_pilots.rb
create app/models/pilot.rb
invoke test_unit
create test/models/pilot_test.rb
create test/fixtures/pilots.yml
這裡我們建立了一個 Pilot 的 model 欄位有 name, region, new_type
資料型態分別是 string, string, boolean
rails g model Gundam code height:integer weight:integer pilot:references
Running via Spring preloader in process 60814
invoke active_record
create db/migrate/20190925141713_create_gundams.rb
create app/models/gundam.rb
invoke test_unit
create test/models/gundam_test.rb
create test/fixtures/gundams.yml
Gundam 這個 model 的欄位則有 code, height, weight, pilot 咦
pilot 這個欄位的資料型態怎麼有點特別? references
這又是什麼?
先不急~我們先進檔案內看看究竟做了什麼吧!
create_table "gundams", force: :cascade do |t|
t.string "code"
t.integer "height"
t.integer "weight"
t.integer "pilot_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["pilot_id"], name: "index_gundams_on_pilot_id"
end
我們進到 schema.rb 檔案中看到 gundams 這張 table,發現建立了一個 pilot_id 的欄位,
並且資料型態為 integer,原來 references 的作用可以將我們指定的欄位改寫為 xxx_id
,
在上面我們產生 model 時是寫了 pilot:refernces,並且自動帶入資料型態 integer
,
可是這樣感覺我們在建立時直接寫 pilot_id:integer 好像也沒什麼太大差別?
nonononono!其實 references 還幫了我們偷偷做了一件事!
我們再進到 gundam.rb 這個 model 去看看吧~
class Gundam < ApplicationRecord
belongs_to :pilot
end
沒想到這裡突然多了一段程式碼了,而這也跟我們要介紹的關聯性有著很大的關係~
我們也進到 pilot.rb 這個 model 裡面去新增一段 code
class Pilot < ApplicationRecord
has_one :gundam
end
其實稍微仔細一點看的話,會發現
我們在 pilot 這個 model 裡面加的話 不正是有一台鋼彈
而 gundam 這個 model 則是寫著 屬於駕駛員
是的,這樣就正式建立起了 所謂 1 對 1的關聯性
has_one 與 belongs_to 其實是密不可分的,將他們兩個配成對的話
我們還多了一些好用的類別方法
可以使用呢!
以下for has_one :gundam
以下 for belongs_to :pilot
演員都到齊了,那我們就開始看看怎麼運用這些方法吧!
p1 = Pilot.create(name: 'KiraYamato')
p2 = Pilot.create(name: 'Sin')
我們先建立了兩個駕駛員
g1 = Gundam.create(code: 'Freedom')
(0.1ms) begin transaction
(0.1ms) rollback transaction
這裡我們發現,如果我們寫的是 Gundam.create 會發生什麼錯誤呢?
居然被rollback了!為什麼呢?
當然一台鋼彈我們會希望有人駕駛,所以不能是沒有人在裡面的狀態,
目前的狀態是有 new(建立)了一台鋼彈出來,但是沒辦法寫入資料庫中,
這時候解決的方法很簡單,直接指派一名駕駛給他就可以了!
g1.pilot = p1
這時就會發現到 g1 的 pilot_id 突然出現1號 id 的駕駛員,
我們這時候可以再輸入
g1.save
(0.1ms) begin transaction
Gundam Create (8.5ms) INSERT INTO "gundams" ("code", "pilot_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["code", "Freedom"], ["pilot_id", 1], ["created_at", "2019-09-25 15:11:02.650491"], ["updated_at", "2019-09-25 15:11:02.650491"]]
(1.4ms) commit transaction
=> true
就會發現 1號鋼彈也順利寫入到資料庫中囉!
除此之外
我們也可以直接使用 build_gundam & create_gundam 這兩個方法來幫助我們
p2.create_gundam(code:"Destiny")
(0.1ms) begin transaction
Gundam Create (0.7ms) INSERT INTO "gundams" ("code", "pilot_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["code", "Destiny"], ["pilot_id", 2], ["created_at", "2019-09-25 15:12:00.457714"], ["updated_at", "2019-09-25 15:12:00.457714"]]
(1.4ms) commit transaction
Gundam Load (0.3ms) SELECT "gundams".* FROM "gundams" WHERE "gundams"."pilot_id" = ? LIMIT ? [["pilot_id", 2], ["LIMIT", 1]]
p3.build_gundam(code:"Justice")
Gundam Load (0.2ms) SELECT "gundams".* FROM "gundams" WHERE "gundams"."pilot_id" = ? LIMIT ? [["pilot_id", 3], ["LIMIT", 1]]
p3.gundam.save
(0.1ms) begin transaction
Gundam Create (0.9ms) INSERT INTO "gundams" ("code", "pilot_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["code", "Justice"], ["pilot_id", 3], ["created_at", "2019-09-25 15:14:11.618396"], ["updated_at", "2019-09-25 15:14:11.618396"]]
(1.1ms) commit transaction
=> true
而其實 build_ 跟 create_ 這兩個方法跟 new 以及 create 也是同個意思
使用 build_ 的時候,會幫你建立好,但不會把資料寫入資料庫中,反之 creat_ 就會直接寫進資料庫。
有趣的是從我們不論輸入了 build 跟 create 指令,
其實我們透過 model 幫我們翻譯成 sql 語法才把資料能夠順利的 讀取、寫入,
而這也是之前我們所介紹的 ORM 他想做的事,這樣有多少體會到了吧!