iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 10
0
自我挑戰組

Ruby菜鳥村村民遊記系列 第 10

遊記ep.10 關聯性的Rails村

介紹完 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

  • .gundam
  • .gundam=
  • .build_gundam
  • .create_gundam

以下 for belongs_to :pilot

  • .pilot
  • .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 他想做的事,這樣有多少體會到了吧!


上一篇
遊記ep.9 沒有?那就自己來吧!的Rails村
下一篇
遊記ep.11 關聯性的Rails村 -2
系列文
Ruby菜鳥村村民遊記30

尚未有邦友留言

立即登入留言