如何在Rails中设置默认值?

时间:2021-11-05 08:15:35

I'm trying to find the best way to set default values for objects in Rails.

我正在寻找为Rails中的对象设置默认值的最佳方法。

The best I can think of is to set the default value in the new method in the controller.

我能想到的最好的方法是在控制器中设置新方法的默认值。

Does anyone have any input if this is acceptable or if there's a better way to do it?

如果这是可以接受的,或者有更好的方法,有人有意见吗?

15 个解决方案

#1


88  

"Correct" is a dangerous word in Ruby. There's usually more than one way to do anything. If you know you'll always want that default value for that column on that table, setting them in a DB migration file is the easiest way:

在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

Because ActiveRecord autodiscovers your table and column properties, this will cause the same default to be set in any model using it in any standard Rails app.

由于ActiveRecord自动发现表和列属性,这将导致在任何模型中使用它在任何标准Rails应用程序中设置相同的默认值。

However, if you only want default values set in specific cases -- say, it's an inherited model that shares a table with some others -- then another elegant way is do it directly in your Rails code when the model object is created:

但是,如果您只希望在特定的情况下设置默认值——例如,它是一个继承的模型,与其他一些模型共享一个表——那么另一种优雅的方法是在创建模型对象时直接在Rails代码中执行:

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

Then, when you do a GenericPerson.new(), it'll always trickle the "Doe" attribute up to Person.new() unless you override it with something else.

然后,当您执行一个GenericPerson.new()时,它总是会将“Doe”属性输入到Person.new(),除非您用其他东西覆盖它。

#2


54  

Based on SFEley's answer, here is an updated/fixed one for newer Rails versions:

根据SFEley的回答,以下是更新的/修复的版本:

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

#3


20  

First of all you can't overload initialize(*args) as it's not called in all cases.

首先,您不能重载initialize(*args),因为它并非在所有情况下都被调用。

Your best option is to put your defaults into your migration:

您最好的选择是将您的默认值放入您的迁移中:

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

Second best is to place defaults into your model but this will only work with attributes that are initially nil. You may have trouble as I did with boolean columns:

第二种方法是将默认值放置到模型中,但这只适用于最初为nil的属性。你可能会遇到麻烦,就像我在布尔列中遇到的那样:

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

You need the new_record? so the defaults don't override values loaded from the datbase.

你需要new_record吗?因此,默认值不会覆盖从datbase加载的值。

You need ||= to stop Rails from overriding parameters passed into the initialize method.

您需要||=阻止Rails重写传递给initialize方法的参数。

#4


12  

You can also try change_column_default in your migrations (tested in Rails 3.2.8):

您还可以在您的迁移中尝试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 docs

change_column_default Rails API文档

#5


7  

If you are referring to ActiveRecord objects, you have (more than) two ways of doing this:

如果你指的是ActiveRecord对象,你有(不止)两种方法:

1. Use a :default parameter in the DB

E.G.

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

More info here: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

2. Use a callback

E.G. before_validation_on_create

例如before_validation_on_create

More info here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147

更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html M002147

#6


7  

In Ruby on Rails v3.2.8, using the after_initialize ActiveRecord callback, you can call a method in your model that will assign the default values for a new object.

在Ruby on Rails v3.2.8中,使用after_initialize ActiveRecord回调,可以调用模型中的一个方法,该方法将为新对象分配默认值。

after_initialize callback is triggered for each object that is found and instantiated by a finder, with after_initialize being triggered after new objects are instantiated as well (see ActiveRecord Callbacks).

after_initialize回调对于查找程序发现并实例化的每个对象都将被触发,而after_initialize也将在新对象实例化之后被触发(参见ActiveRecord回调)。

So, IMO it should look something like:

所以,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 for this instance unless the instance contains an attribute_whose_presence_has_been_validated previously on save/update. The default_value will then be used in conjunction with your view to render the form using the default_value for the bar attribute.

Foo。这个实例的bar = default_value,除非该实例在保存/更新之前包含一个attribute_whose_presence_has_been_been_validate。然后,default_value将与您的视图一起使用,使用bar属性的default_value来呈现表单。

At best this is hacky...

充其量这是一种黑客行为……

EDIT - use 'new_record?' to check if instantiating from a new call

Instead of checking an attribute value, use the new_record? built-in method with rails. So, the above example should look like:

不是检查属性值,而是使用new_record?使用rails内置方法。因此,上面的例子应该是:

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

This is much cleaner. Ah, the magic of Rails - it's smarter than me.

这是干净了很多。啊,Rails的魔力——它比我聪明。

#7


5  

For boolean fields in Rails 3.2.6 at least, this will work in your migration.

至少对于Rails 3.2.6中的布尔字段,这将在迁移中起作用。

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

Putting a 1 or 0 for a default will not work here, since it is a boolean field. It must be a true or false value.

输入1或0作为默认值在这里不起作用,因为它是一个布尔字段。它必须是一个真值或假值。

#8


1  

If you are just setting defaults for certain attributes of a database backed model I'd consider using sql default column values - can you clarify what types of defaults you are using?

如果您只是为数据库支持的模型的某些属性设置默认值,我将考虑使用sql默认列值——您能澄清您使用的默认类型吗?

There are a number of approaches to handle it, this plugin looks like an interesting option.

有很多方法可以处理它,这个插件看起来很有趣。

#9


1  

The suggestion to override new/initialize is probably incomplete. Rails will (frequently) call allocate for ActiveRecord objects, and calls to allocate won't result in calls to initialize.

重写new/initialize的建议可能是不完整的。Rails将(经常)调用ActiveRecord对象的分配,而调用分配不会导致调用初始化。

If you're talking about ActiveRecord objects, take a look at overriding after_initialize.

如果您正在讨论ActiveRecord对象,请查看覆盖after_initialize。

These blog posts (not mine) are useful:

这些博客文章(不是我的)很有用:

Default values Default constructors not called

默认值默认构造函数未调用

[Edit: SFEley points out that Rails actually does look at the default in the database when it instantiates a new object in memory - I hadn't realized that.]

[编辑:SFEley指出当Rails在内存中实例化一个新对象时,它确实会查看数据库中的默认值——我还没有意识到这一点。]

#10


1  

I needed to set a default just as if it was specified as default column value in DB. So it behaves like this

我需要设置一个默认值,就像它在DB中被指定为默认列值一样。它是这样的

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

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

Because after_initialize callback is called after setting attributes from arguments, there was no way to know if the attribute is nil because it was never set or because it was intentionally set as nil. So I had to poke inside a bit and came with this simple solution.

因为after_initialize回调是在设置来自参数的属性之后调用的,所以无法知道属性是否为nil,因为它从未被设置,或者因为它被故意设置为nil。所以我需要深入研究,得出这个简单的解。

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

Works great for me. (Rails 3.2.x)

工作非常适合我。(Rails 3.2.x)

#11


1  

A potentially even better/cleaner potential way than the answers proposed is to overwrite the accessor, like this:

一个可能比建议的答案更好/更干净的方法是覆盖访问器,如下所示:

def status
  self['name_of_var'] || 'desired_default_value'
end

See "Overwriting default accessors" in the ActiveRecord::Base documentation and more from * on using self.

请参阅ActiveRecord::Base文档中的“覆盖默认访问器”,更多来自使用self的*。

#12


0  

i answered a similar question here.. a clean way to do this is using Rails attr_accessor_with_default

我在这里回答了一个类似的问题。一种干净的方法是使用Rails attr_accessor_with_default

class SOF
  attr_accessor_with_default :is_awesome,true
end

sof = SOF.new
sof.is_awesome

=> true

UPDATE

更新

attr_accessor_with_default has been deprecated in Rails 3.2.. you could do this instead with pure Ruby

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

#13


0  

If you're talking about ActiveRecord objects, I use the 'attribute-defaults' gem.

如果您正在谈论ActiveRecord对象,我将使用“attribute-defaults”gem。

Documentation & download: https://github.com/bsm/attribute-defaults

文档和下载:https://github.com/bsm/attribute-defaults

#14


0  

You could use the rails_default_value gem. eg:

您可以使用rails_default_value gem。例如:

class Foo < ActiveRecord::Base
  # ...
  default :bar => 'some default value'
  # ...
end

https://github.com/keithrowell/rails_default_value

https://github.com/keithrowell/rails_default_value

#15


-3  

You can override the constructor for the ActiveRecord model.

您可以重写ActiveRecord模型的构造函数。

Like this:

是这样的:

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

#1


88  

"Correct" is a dangerous word in Ruby. There's usually more than one way to do anything. If you know you'll always want that default value for that column on that table, setting them in a DB migration file is the easiest way:

在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

Because ActiveRecord autodiscovers your table and column properties, this will cause the same default to be set in any model using it in any standard Rails app.

由于ActiveRecord自动发现表和列属性,这将导致在任何模型中使用它在任何标准Rails应用程序中设置相同的默认值。

However, if you only want default values set in specific cases -- say, it's an inherited model that shares a table with some others -- then another elegant way is do it directly in your Rails code when the model object is created:

但是,如果您只希望在特定的情况下设置默认值——例如,它是一个继承的模型,与其他一些模型共享一个表——那么另一种优雅的方法是在创建模型对象时直接在Rails代码中执行:

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

Then, when you do a GenericPerson.new(), it'll always trickle the "Doe" attribute up to Person.new() unless you override it with something else.

然后,当您执行一个GenericPerson.new()时,它总是会将“Doe”属性输入到Person.new(),除非您用其他东西覆盖它。

#2


54  

Based on SFEley's answer, here is an updated/fixed one for newer Rails versions:

根据SFEley的回答,以下是更新的/修复的版本:

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

#3


20  

First of all you can't overload initialize(*args) as it's not called in all cases.

首先,您不能重载initialize(*args),因为它并非在所有情况下都被调用。

Your best option is to put your defaults into your migration:

您最好的选择是将您的默认值放入您的迁移中:

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

Second best is to place defaults into your model but this will only work with attributes that are initially nil. You may have trouble as I did with boolean columns:

第二种方法是将默认值放置到模型中,但这只适用于最初为nil的属性。你可能会遇到麻烦,就像我在布尔列中遇到的那样:

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

You need the new_record? so the defaults don't override values loaded from the datbase.

你需要new_record吗?因此,默认值不会覆盖从datbase加载的值。

You need ||= to stop Rails from overriding parameters passed into the initialize method.

您需要||=阻止Rails重写传递给initialize方法的参数。

#4


12  

You can also try change_column_default in your migrations (tested in Rails 3.2.8):

您还可以在您的迁移中尝试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 docs

change_column_default Rails API文档

#5


7  

If you are referring to ActiveRecord objects, you have (more than) two ways of doing this:

如果你指的是ActiveRecord对象,你有(不止)两种方法:

1. Use a :default parameter in the DB

E.G.

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

More info here: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

2. Use a callback

E.G. before_validation_on_create

例如before_validation_on_create

More info here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147

更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html M002147

#6


7  

In Ruby on Rails v3.2.8, using the after_initialize ActiveRecord callback, you can call a method in your model that will assign the default values for a new object.

在Ruby on Rails v3.2.8中,使用after_initialize ActiveRecord回调,可以调用模型中的一个方法,该方法将为新对象分配默认值。

after_initialize callback is triggered for each object that is found and instantiated by a finder, with after_initialize being triggered after new objects are instantiated as well (see ActiveRecord Callbacks).

after_initialize回调对于查找程序发现并实例化的每个对象都将被触发,而after_initialize也将在新对象实例化之后被触发(参见ActiveRecord回调)。

So, IMO it should look something like:

所以,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 for this instance unless the instance contains an attribute_whose_presence_has_been_validated previously on save/update. The default_value will then be used in conjunction with your view to render the form using the default_value for the bar attribute.

Foo。这个实例的bar = default_value,除非该实例在保存/更新之前包含一个attribute_whose_presence_has_been_been_validate。然后,default_value将与您的视图一起使用,使用bar属性的default_value来呈现表单。

At best this is hacky...

充其量这是一种黑客行为……

EDIT - use 'new_record?' to check if instantiating from a new call

Instead of checking an attribute value, use the new_record? built-in method with rails. So, the above example should look like:

不是检查属性值,而是使用new_record?使用rails内置方法。因此,上面的例子应该是:

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

This is much cleaner. Ah, the magic of Rails - it's smarter than me.

这是干净了很多。啊,Rails的魔力——它比我聪明。

#7


5  

For boolean fields in Rails 3.2.6 at least, this will work in your migration.

至少对于Rails 3.2.6中的布尔字段,这将在迁移中起作用。

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

Putting a 1 or 0 for a default will not work here, since it is a boolean field. It must be a true or false value.

输入1或0作为默认值在这里不起作用,因为它是一个布尔字段。它必须是一个真值或假值。

#8


1  

If you are just setting defaults for certain attributes of a database backed model I'd consider using sql default column values - can you clarify what types of defaults you are using?

如果您只是为数据库支持的模型的某些属性设置默认值,我将考虑使用sql默认列值——您能澄清您使用的默认类型吗?

There are a number of approaches to handle it, this plugin looks like an interesting option.

有很多方法可以处理它,这个插件看起来很有趣。

#9


1  

The suggestion to override new/initialize is probably incomplete. Rails will (frequently) call allocate for ActiveRecord objects, and calls to allocate won't result in calls to initialize.

重写new/initialize的建议可能是不完整的。Rails将(经常)调用ActiveRecord对象的分配,而调用分配不会导致调用初始化。

If you're talking about ActiveRecord objects, take a look at overriding after_initialize.

如果您正在讨论ActiveRecord对象,请查看覆盖after_initialize。

These blog posts (not mine) are useful:

这些博客文章(不是我的)很有用:

Default values Default constructors not called

默认值默认构造函数未调用

[Edit: SFEley points out that Rails actually does look at the default in the database when it instantiates a new object in memory - I hadn't realized that.]

[编辑:SFEley指出当Rails在内存中实例化一个新对象时,它确实会查看数据库中的默认值——我还没有意识到这一点。]

#10


1  

I needed to set a default just as if it was specified as default column value in DB. So it behaves like this

我需要设置一个默认值,就像它在DB中被指定为默认列值一样。它是这样的

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

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

Because after_initialize callback is called after setting attributes from arguments, there was no way to know if the attribute is nil because it was never set or because it was intentionally set as nil. So I had to poke inside a bit and came with this simple solution.

因为after_initialize回调是在设置来自参数的属性之后调用的,所以无法知道属性是否为nil,因为它从未被设置,或者因为它被故意设置为nil。所以我需要深入研究,得出这个简单的解。

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

Works great for me. (Rails 3.2.x)

工作非常适合我。(Rails 3.2.x)

#11


1  

A potentially even better/cleaner potential way than the answers proposed is to overwrite the accessor, like this:

一个可能比建议的答案更好/更干净的方法是覆盖访问器,如下所示:

def status
  self['name_of_var'] || 'desired_default_value'
end

See "Overwriting default accessors" in the ActiveRecord::Base documentation and more from * on using self.

请参阅ActiveRecord::Base文档中的“覆盖默认访问器”,更多来自使用self的*。

#12


0  

i answered a similar question here.. a clean way to do this is using Rails attr_accessor_with_default

我在这里回答了一个类似的问题。一种干净的方法是使用Rails attr_accessor_with_default

class SOF
  attr_accessor_with_default :is_awesome,true
end

sof = SOF.new
sof.is_awesome

=> true

UPDATE

更新

attr_accessor_with_default has been deprecated in Rails 3.2.. you could do this instead with pure Ruby

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

#13


0  

If you're talking about ActiveRecord objects, I use the 'attribute-defaults' gem.

如果您正在谈论ActiveRecord对象,我将使用“attribute-defaults”gem。

Documentation & download: https://github.com/bsm/attribute-defaults

文档和下载:https://github.com/bsm/attribute-defaults

#14


0  

You could use the rails_default_value gem. eg:

您可以使用rails_default_value gem。例如:

class Foo < ActiveRecord::Base
  # ...
  default :bar => 'some default value'
  # ...
end

https://github.com/keithrowell/rails_default_value

https://github.com/keithrowell/rails_default_value

#15


-3  

You can override the constructor for the ActiveRecord model.

您可以重写ActiveRecord模型的构造函数。

Like this:

是这样的:

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