iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 9
0
Modern Web

關於 Ruby on Rails,我想說的是系列 第 13

[Day 13] Model 生成與資料庫遷移

[Day 11] MVC 架構介紹了MVC 架構,提到Model是商業邏輯與資料庫溝通的重要部分。今天就從 Model 的產生,介紹到資料庫遷移。

大綱

  • Model 生成
    1.資料型態
  • 資料庫遷移 (Migration)
    • 想修改 Migration 該怎麼辦?
      1.法一:建立一個新的migration檔
      2.法二:把遷移退回 db:migrate前的狀態
  • schema.rb 是什麼?
  • 建立第一筆資料

Model 生成

先建立一個Product Model,每一筆產品資料要有品名title,價格price跟介紹description欄位。在 CLI 下指令:

rails generate model product title price:float description:text 

1.上面這行指令是Rails提供的CLI指令,也可以不用這指令,自己新增需要的檔案,內容再一個個自己輸入,會比較慢而且也有打錯的風險。
2.generate 可以用g代替。
3.如果這行指令執行後又反悔了,把generate改成d就可以回到執行前的狀態
4.有發現price後面帶floatdescription帶了text

Output:

invoke  active_record
create    db/migrate/20190929051903_create_products.rb
create    app/models/product.rb
invoke    test_unit
create      test/models/product_test.rb
create      test/fixtures/products.yml 

可以看到 Rails 幫我們產生了四個檔案,前兩個是跟資料表active_record有關,後兩個跟測試test_unit有關。

資料型態

來看第一個檔案 db/migrate/20190929051903_create_products.rb
這是個資料庫遷移(Migration)

class CreateProducts < ActiveRecord::Migration[5.2]
  def change
    create_table :products do |t|
      t.string :title
      t.float :price
      t.text :description

      t.timestamps
    end
  end
end

ActiveRecord::Migration[5.2]change類別方法內,包了create_table這個方法。稍後執行遷移(migration)後,在資料庫建立一張products資料表。

讓我們來解析 create_table後面的block 內容:

t.string :title
t.float :price
t.text :description
t.timestamps

t代表欄位,.string代表資料型態是string(預設值,最多接受256個文字)。因為價格可能會有小數點,所以:price的型態使用浮點數float而不是整數integer。商品介紹可能會超過string准許的256個字,我使用不限長度的text。最後一個timestamps方法是Rails幫我產生的,timestamps方法會產生created_atupdated_at兩個欄位,代表這筆資料建立時間跟最新更新時間。

所有的資料型態ihower大大都幫大家整理好了,

在遷移檔裡,每個欄位除了設定資料屬性,還可以設定諸如預設值欄位是准許空值null之類的參數。列舉如下:

  • null 是否允許NULL,預設是允許,即true
  • default 預設值
  • limit 用於string、text、integer、binary指定最大值
  • index => true 直接加上索引
  • index => { :unique => true } 加上唯一索引 => 欄位值不得重複
  • foreign_key => true 加上外部鍵限制

客製化products資料表,把20190929051903_create_products.rb 的 create_table 的 block 內容改成:

create_table :products do |t|
  t.string :title, :limit => 30
  t.float :price, :null => false, 
  t.text :description, :default => "絕贊商品,只此一家"

  t.timestamps
end

這樣 migration 檔就準備好了


資料庫遷移 (Migration)

跑完第一行code:

rails generate model product title price:float description:text 

雖然產生了包含xxx_create_products.rb 共四個檔案,但其實資料庫裡還沒有products資料表,我們還需要執行資料庫遷移 (Migration)才真正建立資料表。

執行資料庫遷移 (Migration):

bin/rake db:migrate

如果建立成功了,打開db/schema.rb可以看到長得跟剛才修改的migration檔有點像,剛建立的products資料表:

ActiveRecord::Schema.define(version: 2019_09_29_051903) do
  enable_extension "plpgsql"

  create_table "products", force: :cascade do |t|
    t.string "title", limit: 30
    t.float "price", null: false
    t.text "description", default: "絕讚商品,只此一家"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
end

products出現了(๑•̀ㅂ•́)و✧

想修改 Migration 該怎麼辦?

一旦執行了rake db:migrate,再修改migration檔就沒用了,因為 Migrations 會自動追蹤哪些變更已經執行過了,以執行的就不會再跑一次。
以剛才的 20190929051903_create_products.rb為例,當我rake db:migrate之後,再把

t.text "description", default: "絕讚商品,只此一家"

改成

t.text "description", default: "要買要快"

存檔並跑rake db:migrate
打開schema.rb來看,products資料表的description欄位還是:

t.text "description", default: "絕讚商品,只此一家"

那該怎麼辦?

法一:建立一個新的migration檔

# migration後面是要新增的檔名,可以自己取名
rails g migration change_product_destription_default 

就產生db/migrate/xxxx_change_product_destription_default.rb
這個 migration檔。不過內容只有:

class ChangeProductDestriptionDefault < ActiveRecord::Migration[5.2]
  def change
  end
end

到底這個migration 要做什麼,我們要再輸入指令。

使用change_column_default方法,來修改欄位預設值

class ChangeProductDestriptionDefault < ActiveRecord::Migration[5.2]
  def change
    change_column_default :products, :description, "要買要快"
  end
end

一樣跑rake db:migrate

完成後,打開schema.rb來看,products資料表的description欄位被改成"要買要快":

  create_table "products", force: :cascade do |t|
    t.string "title", limit: 30
    t.float "price", null: false
    t.text "description", default: "要買要快"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

法二:把遷移退回 db:migrate前的狀態

把遷移退回 db:migrate前的狀態,在CLI下

rake db:rollback

再把20190929051903_create_products.rb改成

 t.text :description, :default => "要買要快"

存檔後執行rake db:migrate就可以了

schema.rb 是什麼?

database schema 是資料庫綱要,schema.rb是用來展示目前資料庫的狀態,並且用 create_table、add_index 等方法,來表達資料庫的結構。由於 schema 是獨立於資料庫系統的(database independent),只要是 Active Record 有支援的資料庫系統,它都可以載入。如果應用程式要散佈到多種資料庫系統,這點會非常有用。

建立第一筆資料

先在 CLI 啟動rails console (Rails 操作台)

rails console

啟動後,來建立第一筆product資料

Product.create(title: '哀鳳11', price: 29999)

產生了三行code

Product Create (2.2ms)  INSERT INTO "products" ("title", "price", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["title", "哀鳳11"], ["price", 29999.0], ["created_at", "2019-09-29 11:24:48.090598"], ["updated_at", "2019-09-29 11:24:48.090598"]]
(0.4ms)  COMMIT
#<Product id: 1, title: "哀鳳11", price: 29999.0, description: "絕讚商品,只此一家", created_at: "2019-09-29 11:24:48", updated_at: "2019-09-29 11:24:48"> 

第一行是SQL語法,要把資料塞進products資料表,第二行是執行時間。第三行就是第一筆Product資料。
如果不使用方便的 RailsORM語法,要改輸入落落長的第一行SQL原生語法,想到就累。由此可見 Rails ORM語法威力的生產效率。


上一篇
#[Day 12] Ruby 算符優先序 Operator Precedence
下一篇
[Day 14] 資料表關聯,以一對多為例
系列文
關於 Ruby on Rails,我想說的是23
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言