【问题标题】:How to store and compare :symbols in an ActiveRecord (Ruby on Rails)如何存储和比较: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 解决方案,你应该写一个答案,我会投票!

标签: ruby-on-rails ruby-on-rails-3 activerecord symbols


【解决方案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 属性方法以添加更复杂的逻辑。

      【讨论】:

      【解决方案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 不同(在这种情况下是指向字符串文字的指针)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多