译:使用ActiveRecord Enums创建简单易读的属性

 Creating Easy, Readable Attributes With ActiveRecord Enums

设想一个问题的状态可能为“暂停”,“通过”或“标注”。或者一个电话号码可能是“家庭号码”,“办公号码”,“手机号码”或者“传真号码”(1982年的话)
有些模块需要这种类型的数据:只对应少许值的属性,并且这些值几乎永远不会改变。
如果使用纯Ruby的话,可以通过使用symbol来解决这个问题。
可以创建PhoneNumberType或者QuestionStatus模块,并通过定义belongs_to关系来关联这些值,但是这么简单的需求似乎并不值得这么做,因为仅仅将这些值放在yaml文件中一样可以满足需求。
现在我们来看一个解决这类需求的真正利器:在Rails4.1中引入的 ActiveRecord enums

Model中的少量值

ActiveRecord enums用法很简单,你可以为model创建一个整数类型的列:

bin/rails g model phone number:string phone_number_type:integer

列出这个属性可能的值

class Phone < ActiveRecord::Base
  enum phone_number_type: [:home, :office, :mobile, :fax]
end
 ```
现在你可以直接操作字符串而不是数字了。
从前:
```ruby
irb(main):001:0> Phone.first.phone_number_type
=> 3




<div class="se-preview-section-delimiter"></div>

采用ActiveRecord enums后:

irb(main):002:0> Phone.first.phone_number_type
=> "fax"




<div class="se-preview-section-delimiter"></div>

通过字符串或者数字都可以改变属性的值

irb(main):003:0> phone.phone_number_type = 1; phone.phone_number_type
=> "office"
irb(main):004:0> phone.phone_number_type = "mobile"; phone.phone_number_type
=> "mobile"




<div class="se-preview-section-delimiter"></div>

甚至是通过感叹方法

irb(main):005:0> phone.office!
=> true
irb(main):006:0> phone.phone_number_type
=> "office"




<div class="se-preview-section-delimiter"></div>

查看属性是否支持某些值:

irb(main):007:0> phone.office?
=> true




<div class="se-preview-section-delimiter"></div>

查询满足属性值的所有对象:

irb(main):008:0> Phone.office
  Phone Load (0.3ms)  SELECT "phones".* FROM "phones" WHERE "phones"."phone_number_type" = ?  [["phone_number_type", 1]]




<div class="se-preview-section-delimiter"></div>

查看属性所有可用值:

irb(main):009:0> Phone.phone_number_types
=> {"home"=>0, "office"=>1, "mobile"=>2, "fax"=>3}




<div class="se-preview-section-delimiter"></div>

在HTML表单中也可以方便使用:

<div class="field">
  <%= f.label :phone_number_type %><br>
  <%= f.select :phone_number_type, Phone.phone_number_types.keys %>
</div>




<div class="se-preview-section-delimiter"></div>

表单效果

需要注意的是

Enums并不是没有缺陷,如果不想在日后陷入麻烦的话,这里有几个问题需要注意。
定义enum时,注意顺序。假设你突然决定使用字母顺序来排列enum值:

class Phone < ActiveRecord::Base
   enum phone_number_type: [:fax, :home, :mobile, :office]
end




<div class="se-preview-section-delimiter"></div>

那么你电话号码的类型匹配就有问题咯。可以通过设定序号值来解决这个问题:

class Phone < ActiveRecord::Base
   enum phone_number_type: {fax: 3, home: 0, mobile: 2, office: 1}
end

但是讲真,最好的选择是不要改变值的顺序。
另一个更大的问题存在于Rails之外。尽管Rails将这些enum值当作字符串处理,而在数据库中它们只是一些数字。其他人看原始数据时并不知道这些数字所代表的含义,这就意味着读取这个数据库在所有应用都要有一份enum值的映射。
碰到这样需求的时候可以选择将enum映射存入数据库或yaml文件中。但这不符合DRY原则,因为现在你在两个地方定义了enum。而且随着应用规模的扩展,可能我们一开始想要避免的做法反而更好:创建另一个模型和关联,即Phone belong_to PhoneNumberType
但是如果想保持简单,enums依然是不错的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值