web-dev-qa-db-ja.com

ActiveRecord(Ruby on Rails)に:symbolsを保存して比較する方法

定数を使用して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と等しいかどうかをテストする方法はありますか?知らないオペレーターがいるかもしれないと思っています。

25
seanyboy

生態学の要請で、ここに答えとしての私のコメントがあります:

ecoologicはあなたにとって良い解決策を持っていますが、私はこれから離れて、定数を含むクラスを作ることをお勧めします。 e.status = Statuses :: CANCELLEDのようなことができること。そして内部的にはそれは文字列である可能性があり、それは問題ではありません。まだ定数を使用していますが、その定数が存在しない場合はエラーが発生し、その方がクリーンです。

7
MrDanA

Rails 4.1.0の場合、ActiveRecord列挙型を使用することをお勧めします。

公式リリースノート を引用するには:

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 }
13
jiehanzheng

これはちょっと遅いですが、他の誰かを助けるかもしれません。

ステータスが異なるクラスがある場合は、次のようなスコープとともに定数を使用するアプローチを検討できます。

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が優れた代替手段のように見えます。

9
MikeH

Rails 4.1の時点で、ActiveRecordは列挙型をサポートするようになりました

リリースノート から:

2.5アクティブレコード列挙型

値がデータベース内の整数にマップされる列挙型属性を宣言しますが、名前で照会できます。

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

7
Readonly

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属性メソッドをオーバーライドしてより複雑なロジックを追加することは強くお勧めしません。

7
ecoologic

また、readerメソッドを上書きすることもできます。

def status
  read_attribute(:status).to_sym
end
6
fguillen

プログラミングRuby 1.9、Symbolクラス(p。729)の==演算子に関して:

Returns true only if sym and obj are symbols with the same object_id.

DBに保存したものはすべて、シンボルの固定object_id(この場合は文字列リテラルへのポインター)とは常に異なるobject_idを持ちます。

1
jordanpg