Railsでデフォルト値を設定するにはどうすればよいですか?


108

Railsでオブジェクトのデフォルト値を設定する最良の方法を見つけようとしています。

私が考えることができる最高のことは、デフォルト値を new方法は、コントローラーのメソッドに。

これが許容できる場合、またはそれを行うより良い方法がある場合、誰かが何か入力を持っていますか?


1
これらのオブジェクトは何ですか。それらはどのように消費/使用されますか?それらはビューのレンダリング中に使用されますか、それともコントローラーロジックに使用されますか?
岐阜

3
ActiveRecordオブジェクトについて話している場合、「デフォルト値」の問題には正解がないことを伝えなければなりません。非常識なハックのみ、そしてRailsの作者はこの機能が価値があるとは考えていないようです(Railsコミュニティのみがそうであるように驚くべきことです。)
Mauricio

受け入れられ、ほとんどの回答はActiveRecordsに焦点を当てているため、元の質問はAcitveRecordsに関するものであると想定しています。したがって、stackoverflow.com
/ questions / 328525 /…の

回答:


98

Rubyでは「正しい」とは危険な言葉です。通常、何を行うにも複数の方法があります。そのテーブルのその列のデフォルト値が常に必要であることがわかっている場合は、DB移行ファイルでそれらを設定するのが最も簡単な方法です。

class SetDefault < ActiveRecord::Migration
  def self.up
    change_column :people, :last_name, :type, :default => "Doe"
  end

  def self.down
    # You can't currently remove default values in Rails
    raise ActiveRecord::IrreversibleMigration, "Can't remove the default"
  end
end

ActiveRecordはテーブルと列のプロパティを自動検出するため、これにより、標準のRailsアプリでそれを使用するすべてのモデルに同じデフォルトが設定されます。

ただし、特定の場合にのみデフォルト値を設定する場合(たとえば、他のユーザーとテーブルを共有する継承モデルである場合)、モデルオブジェクトが作成されたときにRailsコードで直接実行するのが別のエレガントな方法です。

class GenericPerson < Person
  def initialize(attributes=nil)
    attr_with_defaults = {:last_name => "Doe"}.merge(attributes)
    super(attr_with_defaults)
  end
end

次に、を実行するとGenericPerson.new()、それをPerson.new()他の何かでオーバーライドしない限り、常に "Doe"属性がトリクルされます。


2
それを試してみてください。.newクラスメソッドで呼び出される新しいモデルオブジェクトで機能します。ActiveRecordの直接呼び出しに関するブログ投稿の議論.allocateは、データベースからの既存のデータでロードされたモデルオブジェクトに関するものでした。(そして、ActiveRecordがそのように機能するのはひどい考えです、IMO。しかしそれは要点の外です。)
SFEley

2
元の投稿者の質問、ニキータ、そして私のコメントを順に読むために前に戻った場合、それはあなたにとってより意味をなすかもしれません。そうでない場合...まあ、質問は答えられました。ごきげんよう。
SFEley

8
ダウンマイグレーションの改善:change_column_default :people, :last_name, nil stackoverflow.com/a/1746246/483520
Nolan Amy

9
新しいバージョンのレールでは、デフォルトのパラメーターの前に追加のタイプパラメーターが必要であることに注意してください。このページで:typeを検索してください。
Ben Wheeler 14年

3
@JoelBrewer私はこの今日に走った-レール4のために、あなたはまた、列の型を指定する必要がありますchange_column :people, :last_name, :string, default: "Doe"
GoBusto

56

SFEleyの回答に基づいて、ここに新しいRailsバージョン用の更新/修正されたものがあります:

class SetDefault < ActiveRecord::Migration
  def change
    change_column :table_name, :column_name, :type, default: "Your value"
  end
end

2
これはRails 4.2.xでは動作しないことに注意してください(それ以降のバージョンではテストされていません)。change_columnかなり「構造化」することができ、逆の操作を推定することはできません。あなたがわからない場合は、単に実行することによってそれをテストdb:migrateし、db:rollback右後。同じ結果として受け入れられた回答ですが、少なくともそれは想定されています!
GFD

1
これは、レール5.1で動作しますが、あなたが行う必要があります...私にとっては正解であるupdown@GoBustoから、上述のように
ファブリツィオBertoglio

22

まず第一に、あなたは過負荷にすることはできません initialize(*args)、すべてのケースで呼び出されるわけで。

最善の方法は、デフォルトを移行に組み込むことです。

add_column :accounts, :max_users, :integer, :default => 10

2番目に最適な方法は、デフォルトをモデルに配置することですが、これは最初はnilである属性でのみ機能します。boolean列の場合と同じように、問題が発生する可能性があります。

def after_initialize
  if new_record?
    max_users ||= 10
  end
end

new_record?デフォルトがdatbaseからロードされた値を上書きしないようにするために必要です。

||=Railsがinitializeメソッドに渡されたパラメーターをオーバーライドしないようにする必要があります。


12
マイナーな注意-2つのことを実行したい:.... 1)after_initializeメソッドを呼び出さないでください。あなたが欲しいafter_initiation :your_method_name.... 2回の使用self.max_users ||= 10
ジェシー・ウォルガモット

5
ブール値の場合は、次のようにします。prop= trueの場合、prop.nil?
フランシス・ポッター、

2
またはafter_initialize do代わりにdef after_initialize
fotanus 2013

安全を確保するために、self.max_users。
Ken Ratanachai S.18年

15

change_column_default移行を試すこともできます(Rails 3.2.8でテスト済み):

class SetDefault < ActiveRecord::Migration
  def up
    # Set default value
    change_column_default :people, :last_name, "Smith"
  end

  def down
    # Remove default
    change_column_default :people, :last_name, nil
  end
end

change_column_default Rails APIドキュメント


1
受け入れられた答えはより完全ですが、私はこのきれいで文書化されたものが好きです...ありがとう!
GFD

7

ActiveRecordオブジェクトを参照している場合、これを行うには2つ(以上)の方法があります。

1. DBで:defaultパラメータを使用します

例えば

class AddSsl < ActiveRecord::Migration
  def self.up
    add_column :accounts, :ssl_enabled, :boolean, :default => true
  end

  def self.down
    remove_column :accounts, :ssl_enabled
  end
end

詳細はこちら:http : //api.rubyonrails.org/classes/ActiveRecord/Migration.html

2.コールバックを使用する

例えば before_validation_on_create

詳細はこちら:http : //api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147


2
データベースでデフォルトを使用することの問題は、オブジェクトが保存されるまで設定されないことではないですか?デフォルトが入力された状態で新しいオブジェクトを作成する場合は、初期化子でそれらを設定する必要があります。
Rafe、

ブール値のデフォルトを1または0にすることはできません。ブール値はtrueまたはfalseに設定する必要があります(Silasjの回答を参照)。
Jamon Holmgren

@JamonHolmgren発言のおかげで、私は答えを修正しました:)
Vlad Zloteanu

問題ありません@VladZloteanu
Jamon Holmgren

7

Ruby on Rails v3.2.8では、after_initializeActiveRecordコールバックを使用して、新しいオブジェクトにデフォルト値を割り当てるモデルのメソッドを呼び出すことができます。

after_initializeコールバックは、ファインダーによって検出およびインスタンス化された各オブジェクトに対してトリガーされます。また、after_initializeは、新しいオブジェクトがインスタンス化された後にもトリガーされます(ActiveRecordコールバックを参照)。

したがって、IMOは次のようになります。

class Foo < ActiveRecord::Base
  after_initialize :assign_defaults_on_new_Foo
  ...
  attr_accessible :bar
  ...
  private
  def assign_defaults_on_new_Foo
    # required to check an attribute for existence to weed out existing records
    self.bar = default_value unless self.attribute_whose_presence_has_been_validated
  end
end

Foo.bar = default_valueインスタンスにattribute_whose_presence_has_been_validated以前の保存/更新が含まれていない限り、このインスタンスの場合。次にdefault_value、をビューと組み合わせて使用​​して、default_valuefor bar属性を使用してフォームをレンダリングします。

せいぜいこれはハッキーです...

編集-'new_record?'を使用します 新しい呼び出しからインスタンス化するかどうかを確認するには

属性値をチェックする代わりに、new_record?組み込みのメソッドをレールで使用します。したがって、上記の例は次のようになります。

class Foo < ActiveRecord::Base
  after_initialize :assign_defaults_on_new_Foo, if: 'new_record?'
  ...
  attr_accessible :bar
  ...
  private
  def assign_defaults_on_new_Foo
    self.bar = default_value
  end
end

これはずっときれいです。ああ、Railsの魔法-それは私よりも賢いです。


これは期待どおりに機能しません。デフォルトでnewを設定するだけでしたが、見つかってインスタンス化された各オブジェクト(つまり、dbからロードされたレコード)の属性にもデフォルトを設定します。デフォルトを割り当てる前に値を探すオブジェクト属性のチェックを行うことができますが、これは非常にエレガントまたは堅牢なソリューションではありません。
エラーが発生した

1
なぜafter_initializeここで折り返し電話をすすめますか?コールバックに関するRailsドキュメントには、before_create追加の条件チェックなしでデフォルト値を設定する例があります
Dfr

@Dfr-見逃していたに違いありません。レビューへのリンクを
送っていただければ

はい、ここでapi.rubyonrails.org/classes/ActiveRecord/Callbacks.html最初の例、クラスSubscription
Dfr

5
@Dfr- コールバックではafter_initializeなくを使用する理由before_createは、ユーザーが新しいオブジェクトを作成するときに(ビューで使用するために)ユーザーのデフォルト値を設定するためです。before_createコールバックが呼び出された後、ユーザーが新しいオブジェクトを務めてきた、彼らの入力を提供し、コントローラに作成するためにオブジェクトを提出しました。次に、コントローラーはbefore_createコールバックをチェックします。直感に反するように見えますが、それは命名法です- アクションをbefore_create指しcreateます。新しいオブジェクトをインスタンス化してもオブジェクトは生成されませんcreate
エラーが発生した2013年

5

Rails 3.2.6のブールフィールドの場合、これは移行で機能します。

def change
  add_column :users, :eula_accepted, :boolean, default: false
end

置く10、それはboolean型のフィールドであるため、デフォルトのために、ここでは動作しません。trueまたはfalse値でなければなりません。



2

移行を生成して使用しchange_column_default、簡潔で可逆的です:

class SetDefaultAgeInPeople < ActiveRecord::Migration[5.2]
  def change
    change_column_default :people, :age, { from: nil, to: 0 }
  end
end

1

データベースを使用するモデルの特定の属性にデフォルトを設定するだけの場合、SQLのデフォルトの列値を使用することを検討します。使用しているデフォルトのタイプを明確にできますか?

これを処理する方法はいくつかありますが、このプラグインは興味深いオプションのように見えます。


1
デフォルトと制約を処理するためにデータベースに依存するべきではないと私は思います、それらすべてのものはモデル層で分類されるべきです。このプラグインはActiveRecordモデルでのみ機能し、オブジェクトのデフォルトを設定する一般的な方法ではありません。
Lukas Stejskal、2009

それはあなたが使用しようとしているデフォルトのタイプに依存すると私は主張します、それは私が頻繁に行う必要があったものではありませんが、制約は実際にはデータベースとモデル-データがデータベースで無効になるのを防ぎます。
paulthenerd 09/07/27

1

new / initializeをオーバーライドする提案はおそらく不完全です。Railsは(頻繁に)ActiveRecordオブジェクトの割り当てを呼び出します。割り当てを呼び出すと、初期化が呼び出されません。

ActiveRecordオブジェクトについて話している場合は、after_initializeのオーバーライドを確認してください。

これらのブログ投稿(私のものではありません)は役に立ちます。

デフォルト値 呼び出されないデフォルトコンストラクター

[編集:SFEleyは、メモリ内の新しいオブジェクトをインスタンス化するときに、Railsが実際にデータベースのデフォルトを確認することを指摘しています-私はそれを認識していませんでした。]


1

DBでデフォルトの列値として指定されているかのように、デフォルトを設定する必要がありました。したがって、このように動作します

a = Item.new
a.published_at # => my default value

a = Item.new(:published_at => nil)
a.published_at # => nil

引数から属性を設定した後にafter_initializeコールバックが呼び出されるため、属性が設定されたことがないため、または意図的にnilとして設定されたため、属性がnilであるかどうかを知る方法がありませんでした。だから私は少し内部を突く必要があり、この簡単な解決策を思いつきました。

class Item < ActiveRecord::Base
  def self.column_defaults
    super.merge('published_at' => Time.now)
  end
end

私にとってはうまくいきます。(Rails 3.2.x)



0

私はここで同様の質問に答えました..これを行うクリーンな方法はRails attr_accessor_with_defaultを使用することです

class SOF
  attr_accessor_with_default :is_awesome,true
end

sof = SOF.new
sof.is_awesome

=> true

更新

attr_accessor_with_defaultはRails 3.2で非推奨になりました。純粋なRubyで代わりにこれを行うことができます

class SOF
  attr_writer :is_awesome

  def is_awesome
    @is_awesome ||= true
  end
end

sof = SOF.new
sof.is_awesome

#=> true

attr_accessor_with_default3.1.0以降のレールでは非推奨
flynfish '11年

あなたの例では、@ is_awesome == falseであってもis_awesomeは常にtrueになります。
リッチー



-3

ActiveRecordモデルのコンストラクターをオーバーライドできます。

このような:

def initialize(*args)
  super(*args)
  self.attribute_that_needs_default_value ||= default_value
  self.attribute_that_needs_another_default_value ||= another_default_value
  #ad nauseum
end

2
これは、コアレールの機能を変更するためのひどい場所です。他のものを壊す可能性の低い他のはるかに安定したものがあります。これを行う方法は他の回答で述べられています。サルのパッチは、他の方法では実行できない可能性があることの最後の手段にすぎません。
ウィル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.