如何存储和比较: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 范围内返回结果

ActiveRecord 子类化?

Castle ActiveRecord / NHibernate - 密码加密或散列

如何在 Rails 中链接原始 SQL 查询或如何从 Rails 中的原始 SQL 查询返回 ActiveRecord_Relation?

如何禁用某个列的 ActiveRecord 日志记录?