如何存储和比较:ActiveRecord 中的符号(Ruby on Rails)
Posted
技术标签:
【中文标题】如何存储和比较:ActiveRecord 中的符号(Ruby on Rails)【英文标题】:How to store and compare :symbols in an ActiveRecord (Ruby on Rails) 【发布时间】:2012-02-20 16:56:14 【问题描述】:我认为使用常量填充 activeRecord 表中的状态字段会很好。但是,在检查此状态是否具有特定状态时,我遇到了麻烦。
如果我执行以下操作,
e = Mytable.new
e.status = :cancelled
e.save
然后重新查找记录并尝试将我的状态与符号进行比较,检查失败。我有一些来自控制台的输出来显示这一点。
irb(main):060:0> e.status.eql?("cancelled")
=> true
irb(main):061:0> e.status.eql?(:cancelled)
=> false
irb(main):062:0> e.status == :cancelled
=> false
irb(main):063:0> e.status == "cancelled"
=> true
irb(main):064:0> e.status == :cancelled.to_s
=> true
有没有更好的方法在记录中保存状态?有没有一种方法可以测试当前字段值是否等于 :symbol 而不将 :symbol 转换为字符串?我在想可能有一个我不知道的操作员。
【问题讨论】:
ecoologic 为您提供了一个很好的解决方案,但我建议您不要这样做,并可能创建一个包含常量的类。你可以做e.status = Statuses::CANCELLED
之类的事情,而不能做的事情。在内部,这可能是一个字符串,这并不重要。您仍在使用常量,如果该常量不存在,它会出错,这样会更干净。
为什么不覆盖列的getter?
我在阅读这两个 cmets 之前修改了我的答案,但我想说我喜欢 @MrDanA 解决方案,你应该写一个答案,我会投票!
【参考方案1】:
在 Rails 4.1.0 中,您可能希望使用 Active Record 枚举。
引用official release notes:
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
conversation.archived!
conversation.active? # => false
conversation.status # => "archived"
Conversation.archived # => Relation for all archived Conversations
Conversation.statuses # => "active" => 0, "archived" => 1
【讨论】:
【参考方案2】:这有点晚了,但可能对其他人有所帮助。
如果你有不同状态的类,你可能会考虑使用常量和这样的范围的方法:
class Account < ActiveRecord::Base
#-------------------------------------------------------------------------------
# Configuration
#-------------------------------------------------------------------------------
# STATUS is used to denote what state the account is in.
STATUS = :active => 1, :suspended => 2, :closed => 3
# Scopes
scope :active, where(:status => Account::STATUS[:active])
scope :suspended, where(:status => Account::STATUS[:suspended])
scope :closed, where(:status => Account::STATUS[:closed])
...
end
然后你可以很容易地根据状态找到记录,像这样:
# get all active accounts
active_accounts = Consumer.active.all
# get 50 suspended accounts
suspended_accounts = Consumer.suspended.limit(50)
# get accounts that are closed and [some search criteria]
closed_accounts = Consumer.closed.where([some search criteria])
希望这对其他人有帮助!
编辑: 如果您更喜欢使用 gem,simple_enum gem 看起来是个不错的选择。
【讨论】:
【参考方案3】:应生态的要求,这是我的评论作为答案:
ecoologic 为您提供了一个很好的解决方案,但我建议您远离这个并创建一个包含常量的类。您可以执行诸如 e.status = Statuses::CANCELLED 之类的操作。在内部,这可能是一个字符串,这并不重要。您仍在使用常量,如果该常量不存在,它将出错,这样会更干净。
【讨论】:
作为最佳实践,您会将这个类放在哪里?在它自己的 lib 文件中,还是 helpers 中? 我已将状态类放在模型文件中。它运作良好。我还在类中添加了一个 self.method 以将状态转换为颜色。这样就可以将所有内容保存在一个地方。 我通常把它们放在自己的lib文件中,是的。然后,您可以通过编辑应用程序配置文件来自动加载文件(而不必require
它们)。
这个答案已经过时了。 @jiehanzheng 使用枚举为 Rails 4 提供了更新的答案。【参考方案4】:
如果我记得 ActiveRecord 中的符号是以 yaml 格式存储的,则必须进行某种转换,因为关系数据库中没有符号之类的东西(至少我知道)。当您阅读它时,它是一个与您的符号不匹配的字符串,甚至与符号的字符串都不匹配,实际上它应该是这样的:
:x # => "--- :x\n"
我认为这个插件可以解决你的问题,但我没有诚实地使用它。 https://github.com/zargony/activerecord_symbolize
* 编辑 *
我离开上面是因为我记得那是我遇到的情况,如果我错了我可以纠正,不过我现在正在尝试这个,存储的值(Rails 3.1.3)是一个简单的字符串符号的值,所以以下应该足够了。
class Example < ActiveRecord::Base
def aaa
super.to_sym
end
def aaa=(value)
super(value.to_sym)
aaa
end
end
这当然会强制值始终是符号
** 年龄后编辑 ** 我认为在这种情况下很好,因为很明显在 db 中它是一个字符串并且逻辑很简单,但我强烈反对重写 db 属性方法以添加更复杂的逻辑。
【讨论】:
还要记住,在用户输入上使用 :to_sym 是个坏主意:tricksonrails.com/2010/06/… 是的,值得一提,这是不好的做法【参考方案5】:从 Rails 4.1 开始,Active Record 现在支持枚举
来自release notes:
2.5 Active Record 枚举
声明一个枚举属性,其中值映射到 数据库,但可以按名称查询。
class Conversation < ActiveRecord::Base
enum status: [ :active, :archived ]
end
conversation.archived!
conversation.active? # => false
conversation.status # => "archived"
Conversation.archived # => Relation for all archived Conversations
Conversation.statuses # => "active" => 0, "archived" => 1
此处的其他文档:http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html
【讨论】:
【参考方案6】:您也可以覆盖reader
方法:
def status
read_attribute(:status).to_sym
end
【讨论】:
【参考方案7】:来自 Programming Ruby 1.9,关于 Symbol 类中的 == 运算符(第 729 页):
Returns true only if sym and obj are symbols with the same object_id.
无论您存储在数据库中的什么,object_id 总是与符号的固定 object_id 不同(在这种情况下是指向字符串文字的指针)。
【讨论】:
以上是关于如何存储和比较:ActiveRecord 中的符号(Ruby on Rails)的主要内容,如果未能解决你的问题,请参考以下文章
如何将现有的一对多关系迁移到 Rails 和 ActiveRecord 中的多对多
使用范围在 ActiveRecord 中的多个 DateTime 范围内返回结果
Castle ActiveRecord / NHibernate - 密码加密或散列
如何在 Rails 中链接原始 SQL 查询或如何从 Rails 中的原始 SQL 查询返回 ActiveRecord_Relation?