Rails model 的種類與狀態寫法( kind / status ... )


#1

單純寫個簡單的教學,類似

create_table "students" do |t|
  t.integer :kind , :limit => 1 , :default => 0 , :null => false , :unsigned => true
  t.integer :status , :limit => 1 , :default => 0 , :null => false , :unsigned => true
end

:limit => 1 代表 1byte,所以是 -128 ~ 127
:default => 0 預設為 0,對下表
:null => false 此項目必填
:unsigned => true 不允許負數,也就是剛剛的range會變成0 ~ 255
這些其實都是 RDBMS 的寫法,請參考 RDBMS 對 integer 這個 datatype 的相關文章

class Student < ActiveRecord::Base
  KIND = [['國小' , 0],['國中' , 1],['高中' , 2],['五專' , 3],['大學' , 4],['研究所' , 5]]
  STATUS = [['在學' , 0],['畢業' , 1],['肄業' , 2]]
end

先訂好常數,定法一定要是這樣的

<%= form_for ... do |f| %>
  <%= f.select :kind , Student::KIND %>
  <%= f.select :status , Student::STATUS %>
<% end%>

最後顯示時使用類似

<% @students.each do |student| %>
  <%= Student::KIND[student.kind][0] %>
  <%= Student::KIND[student.status][0] %>
<% end %>

okay~ 前後台連 DB 都對好好,以上 :slight_smile:


詢問兩個資料查詢的方式
#2
== 20150430031518 AddBaseVersionAndProjectToRequest: migrating =============
-- add_column(:requests, :project, :integer, {:limit=>1, :default=>0, :null=>false, :unsigned=>true})
   -> 0.0382s
== 20150430031518 AddBaseVersionAndProjectToRequest: migrated (0.0728s) ====

測試

rcms :002 > Request.first.update(project:1)
Request Load (0.3ms)  SELECT  `requests`.* FROM `requests`  ORDER BY `requests`.`id` ASC LIMIT 1
   (0.1ms)  BEGIN
  SQL (0.3ms)  UPDATE `requests` SET `project` = 1, `updated_at` = '2015-04-30 03:45:55.934260' WHERE `requests`.`id` = 1
   (0.6ms)  COMMIT
 => true
rcms :003 > Request.first.update(project:-2)
  Request Load (0.3ms)  SELECT  `requests`.* FROM `requests`  ORDER BY `requests`.`id` ASC LIMIT 1
   (0.1ms)  BEGIN
  SQL (0.2ms)  UPDATE `requests` SET `project` = -2, `updated_at` = '2015-04-30 03:46:01.103324' WHERE `requests`.`id` = 1
   (8.3ms)  COMMIT
 => true

:unsigned=>true

犧牲樓上所有人招換JC 求解


#4

http://stackoverflow.com/questions/14360436/rails-3-migration-for-adding-unsigned-int-column

乾…所以連我也被搞了X"D…你可以用

change_column :xxx , :yyy , 'INT UNSIGNED' ... #照舊

試試看?不過對我而言如果流程safe,倒是沒太大關係才是


#5

f.select 後面要先接 :kind, 再來才是常數…早上嚐試時發現的小闕漏.


#6

X"DD…改了


#8

JC 老師我有問題,為何不用 enum XDDD


#9

先註冊好 enum

class Student < ActiveRecord::Base
  enum kind: {
    '國小': 0,
    '國中': 1,
    '高中': 2,
    '五專': 3,
    '大學': 4,
    '研究所': 5
  }
  enum status: {
    '在學': 0,
    '畢業': 1,
    '肄業': 2
  }
end

使用在 form 上的時候也可以

<%= form_for ... do |f| %>
  <%= f.select :kind  , options_for_select(Student.kinds) %>
  <%= f.select :status, options_for_select(Student.statuses) %>
<% end%>

最後顯示時可以直接使用

<% @students.each do |student| %>
  <%= student.kind %>
  <%= student.status %>
<% end %>

如果搭配 EnumHelp 可以做到更方便的 i18n


#10

因為 MySQL 的 enum 對我而言像是一坨屎 … 丟 string 進去存的卻是 int,SQL 要解析 string 後還要再過一次對照表,所以是 int 快還是 string 快?

當然上了 enum 有 limit 是好事,but 玩到後面連 schema 都難改,整票看到最後還不如自己建常數會快點 and 好點,統計起來也方便,增加狀態也是哩

至於 i18n … 我們家都厚顏無恥的用中文當 key 了 X"DD 只要不怕丟臉拋棄工程師的自尊心,沒啥做不出來的啦

至於 gem 的問題,我們家大概都自己寫 helper 唄,能不用 gem 就不用 gem,不然天知道哪天你會被哪個 gem 搞到就是了,我連 curl 這種 gem 都會被搞到了(隨機噴 kernel error )到後面都不敢用了,還是自己寫最實在就是

我剛看了一下 ActiveRecord::Enum 發覺他和 MySQL enum 無關,那這樣應該可以用啦,所以剩下單純的原因是 … 舊版的沒這東西可以用哈哈,然後我也不愛 enum 會自動加 scope 之類的…

額外參考說明: https://ruby-china.org/topics/28654


#11

額……這個不是 MySQL 的 enum…… 這是 Rails 的

一樣在 Rails 端就過對照表,下 query 的時候就已經是 int 了

啊哈你有發現了XDDD 我回完才發現你發現了XDDD


#12

ActiveRecordEnum 的缺點:
Select 出來的值會直接幫你轉成你設定的單字,產生 html 很方便,
但是開 api 就麻煩了,select 出來還要用hash轉換一次才可以取得真正的值。
可能用有意義的單字作為 enum 的對應會是比較好的解法,
但某種程度是多過一層。


#13

enum 本來就要用有意義的字作名稱,就跟變數或參數命名一樣
我覺得這個拿來開 api 完全不是問題,不知道你問題在哪裡?

(我自己也是拿來開發 api)


#14

因為阿 Rails 5 以前的 ActiveRecord Enum 如果在不同 model 中宣告一樣的名字,
是會相衝的,比如果我在 Post 裡面宣告 0: not_complete, 1: complete。
在 Group 就不能宣告一樣的 enum 關鍵字。
所以你要幫enum取名字變得非常的困難。一定會有贅字。

然後 select 出來不是 db 的值,會讓你需要多一層思考的轉換。比如說你想要下 SELECT * FROM posts status IN (0,1),
這時候你就不能用 enum 了。


#15

你可以用 Post.where(status: [:not_complete, :complete])


#16

請問這樣的寫法可以用在Dynamic的顯示方式嗎?
譬如選擇台北市會出現松山區、信義區…
選擇花蓮市則會出現其他區域?


#17

請參閱新手教學…

先把你前端搞定後再去想後端如何儲存如何?