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でこれを行うエレガントな方法は何ですか?
どうもありがとう。
移行の列にデフォルトのオプションを設定できます
....
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
少し前にこの問題に遭遇し、Rails 3.0のオプションが少し異なるため、この質問に対する別の回答を提供します。
Rails 3.0では、次のようなことを行います。
class MyModel < ActiveRecord::Base
after_initialize :default_values
private
def default_values
self.name ||= "default value"
end
end
デフォルト値が必要なときは、通常、新しいアクションのビューがレンダリングされる前に新しいレコードに対して使用されます。次のメソッドは、フォームのレンダリング時に使用できるように、新しいレコードのみにデフォルト値を設定します。 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
コードをまったく書かずにそれを行うことができます:)データベースの列にデフォルト値を設定するだけです。これは移行で行うことができます。例えば:
create_table :projects do |t|
t.string :status, :null => false, :default => 'P'
...
t.timestamps
end
お役に立てば幸いです。
解決策はいくつかのことに依存します。
デフォルト値は、作成時に利用可能な他の情報に依存していますか?最小限の結果でデータベースを消去できますか?
最初の質問に「はい」と答えた場合、ジムのソリューションを使用します
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を再定義しても、多くの問題は発生しません。特に本番環境では。この移行は、ロードされて呼び出される頻度が少ないため、適切な方法です。
Attr_defaults found here を使用することを検討します。あなたの最大の夢が実現します。
列タイプの場合、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
ご覧のとおり、デフォルト値が必要な場合に対処する必要がある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
人々がこのアプローチを考える理由を知りたいです。
私は今それを行うより良い方法を見つけました:
def status=(value)
self[:status] = 'P'
end
Rubyでは、メソッド呼び出しに括弧を使用できないため、ローカル変数に別の名前を付ける必要があります。そうしないと、Rubyがメソッド呼び出しとして認識します。