web-dev-qa-db-ja.com

Rails activerecordのモデルの属性のデフォルト値を作成するにはどうすればよいですか?

ActiveRecordで定義することにより、属性のデフォルト値を作成したい。デフォルトでは、レコードが作成されるたびに、属性:statusのデフォルト値が必要です。私はこれをやろうとしました:

class Task < ActiveRecord::Base
  def status=(status)
    status = 'P'
    write_attribute(:status, status)
  end
end

しかし、作成時にデータベースからこのエラーを取得します:

ActiveRecord::StatementInvalid: Mysql::Error: Column 'status' cannot be null

したがって、値が属性に適用されなかったと思います。

Railsでこれを行うエレガントな方法は何ですか?

どうもありがとう。

184
Joshua Partogi

移行の列にデフォルトのオプションを設定できます

....
add_column :status, :string, :default => "P"
....

OR

コールバックbefore_saveを使用できます

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P' # note self.status = 'P' if self.status.nil? might be safer (per @frontendbeauty)
  end
end
281
Jim

少し前にこの問題に遭遇し、Rails 3.0のオプションが少し異なるため、この質問に対する別の回答を提供します。

Rails 3.0では、次のようなことを行います。

class MyModel < ActiveRecord::Base
  after_initialize :default_values

  private
    def default_values
      self.name ||= "default value"
    end
end
189
BeepDog

デフォルト値が必要なときは、通常、新しいアクションのビューがレンダリングされる前に新しいレコードに対して使用されます。次のメソッドは、フォームのレンダリング時に使用できるように、新しいレコードのみにデフォルト値を設定します。 before_saveおよびbefore_create手遅れですおよび機能しません入力フィールドにデフォルト値を表示したい場合

after_initialize do
  if self.new_record?
    # values will be available for new record forms.
    self.status = 'P'
    self.featured = true
  end
end
81
Tim Santeford

コードをまったく書かずにそれを行うことができます:)データベースの列にデフォルト値を設定するだけです。これは移行で行うことができます。例えば:

create_table :projects do |t|
  t.string :status, :null => false, :default => 'P'
  ...
  t.timestamps
end

お役に立てば幸いです。

77

解決策はいくつかのことに依存します。

デフォルト値は、作成時に利用可能な他の情報に依存していますか?最小限の結果でデータベースを消去できますか?

最初の質問に「はい」と答えた場合、ジムのソリューションを使用します

2番目の質問に「はい」と答えた場合は、ダニエルのソリューションを使用します

両方の質問に「いいえ」と答えた場合は、新しい移行を追加して実行することをお勧めします。

class AddDefaultMigration < ActiveRecord::Migration
  def self.up
     change_column :tasks, :status, :string, :default => default_value, :null => false
  end
end

:stringは、ActiveRecord :: Migrationが認識する任意のタイプに置き換えることができます。

CPUは安価であるため、JimのソリューションでTaskを再定義しても、多くの問題は発生しません。特に本番環境では。この移行は、ロードされて呼び出される頻度が少ないため、適切な方法です。

23
EmFi

Attr_defaults found here を使用することを検討します。あなたの最大の夢が実現します。

12
PeppyHeppy

強化するだけ ジムの答え

プレゼンス を使用すると1つできます

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status = status.presence || 'P'
  end
end
10
swapab

列タイプの場合、Railsは、この質問の文字列のようにすぐにサポートします。最善のアプローチは、Daniel Kristensenが示すように、データベース自体に列のデフォルトを設定することです。 RailsはDBを内省し、それに応じてオブジェクトを初期化します。さらに、これにより、DBがRailsアプリの外部に行を追加し、その列の初期化を忘れることから安全になります。

列タイプの場合、Railsはそのままではサポートされません。 ENUM列-Railsは列のデフォルトを内省できません。これらの場合、not after_initializeを使用したい(DBからオブジェクトがロードされるたびに、および.newを使用してオブジェクトが作成されるたびに呼び出される)、before_create(後に発生するため)検証)、またはbefore_save(更新時にも発生するため、通常は必要ではありません)。

むしろ、次のようなbefore_validationの属性を作成します:create

before_validation :set_status_because_Rails_cannot, on: :create

def set_status_because_Rails_cannot
  self.status ||= 'P'
end
4
aec

ご覧のとおり、デフォルト値が必要な場合に対処する必要がある2つの問題があります。

  1. 新しいオブジェクトが初期化されるときに存在する値が必要です。 after_initializeの使用は適切ではありません。前述のように、#findの呼び出し中に呼び出され、パフォーマンスが低下するためです。
  2. 保存時にデフォルト値を保持する必要があります

ここに私の解決策があります:

# the reader providers a default if nil
# but this wont work when saved
def status
  read_attribute(:status) || "P"
end

# so, define a before_validation callback
before_validation :set_defaults
protected
def set_defaults
  # if a non-default status has been assigned, it will remain
  # if no value has been assigned, the reader will return the default and assign it
  # this keeps the default logic DRY
  status = status
end

人々がこのアプローチを考える理由を知りたいです。

4
Peter P.

私は今それを行うより良い方法を見つけました:

def status=(value) 
  self[:status] = 'P' 
end 

Rubyでは、メソッド呼び出しに括弧を使用できないため、ローカル変数に別の名前を付ける必要があります。そうしないと、Rubyがメソッド呼び出しとして認識します。

0
Joshua Partogi