如何在Rails中设置默认值?

bia*_*idp 104 ruby ruby-on-rails

我正在尝试找到在Rails中为对象设置默认值的最佳方法.

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

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

SFE*_*ley 95

"正确"在Ruby中是一个危险的词.通常有多种方法可以做任何事情.如果您知道您将始终希望该表上该列的默认值,则在数据库迁移文件中设置它们是最简单的方法:

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
Run Code Online (Sandbox Code Playgroud)

因为ActiveRecord会自动发现您的表和列属性,所以这将导致在任何标准Rails应用程序中使用它的任何模型中设置相同的默认值.

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

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

然后,当你这样做GenericPerson.new()时,Person.new()除非你用别的东西覆盖它,否则它总是会把"Doe"属性涓涓细流.

  • 请注意,对于较新的rails版本,您需要在默认参数之前使用其他类型参数.搜索:在此页面上输入. (9认同)
  • 更适合向下迁移:`change_column_default:people,:last_name,nil` http://stackoverflow.com/a/1746246/483520 (8认同)
  • @JoelBrewer我今天碰到了这个 - 对于Rails 4,你还需要指定列类型:`change_column:people,:last_name,:string,default:"Doe"` (3认同)
  • 试试吧.它将适用于使用`.new`类方法调用的新模型对象.该博客文章关于ActiveRecord直接调用`.allocate`的讨论是关于从数据库中加载现有数据的模型对象.(_对于ActiveRecord这样工作来说,这是一个可怕的想法,IMO.但这不是重点.) (2认同)
  • 如果你再回过头来再次阅读原始海报的问题,尼基塔,然后我的评论,它可能对你更有意义.如果不是......好吧,问题已得到解答.祝你今天愉快. (2认同)

J-_*_*_-L 56

基于SFEley的答案,这里是针对较新的Rails版本的更新/修复版本:

class SetDefault < ActiveRecord::Migration
  def change
    change_column :table_name, :column_name, :type, default: "Your value"
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这不适用于 Rails 4.2.x(未在更高版本上测试)。由于 `change_column` 可以非常“结构化”,因此无法推断出相反的操作。如果您不确定,只需在之后运行 `db:migrate` 和 `db:rollback` 来测试它。接受的答案是相同的结果,但至少是假设的! (2认同)

Ian*_*ton 20

首先,你不能超载,initialize(*args)因为在所有情况下都没有调用.

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

add_column :accounts, :max_users, :integer, :default => 10
Run Code Online (Sandbox Code Playgroud)

第二个最好的方法是将默认值放入模型中,但这只适用于最初为零的属性.您可能会遇到与boolean列相关的问题:

def after_initialize
  if new_record?
    max_users ||= 10
  end
end
Run Code Online (Sandbox Code Playgroud)

您需要new_record?这样,默认值不会覆盖从数据库加载的值.

您需要||=阻止Rails覆盖传递给initialize方法的参数.

  • 小调 - 你想做两件事:.... 1)不要在after_initialize之后调用你的方法.你想`after_initiation:your_method_name` .... 2使用`self.max_users || = 10` (12认同)
  • 对于布尔值,只需这样做:prop = true如果prop.nil? (5认同)
  • 或者`after_initialize do`而不是`def after_initialize` (2认同)

Seb*_*yet 13

您也可以尝试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
Run Code Online (Sandbox Code Playgroud)

change_column_default Rails API文档


Vla*_*anu 7

如果您指的是ActiveRecord对象,那么您有两种以上的方法:

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
Run Code Online (Sandbox Code Playgroud)

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

2.使用回调

例如 before_validation_on_create

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

  • 在数据库中使用默认值是不是有问题,直到保存对象才设置它们?如果我想构造一个填充了默认值的新对象,我需要在初始化程序中设置它们。 (2认同)

err*_*ric 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
Run Code Online (Sandbox Code Playgroud)

Foo.bar = default_value对于此实例,除非实例包含attribute_whose_presence_has_been_validated先前的保存/更新.该default_value项目就会在结合使用视图渲染使用形式default_valuebar属性.

至多这是hacky ......

编辑 - 使用'new_record?' 检查是否从新呼叫实例化

而不是检查属性值,使用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
Run Code Online (Sandbox Code Playgroud)

这更清洁.啊,Rails的魔力 - 它比我更聪明.

  • @Dfr - 我们使用`after_initialize`而不是`before_create`回调的原因是我们想要在创建新对象时为_user_(在视图中使用)设置默认值.在**_user_被提供一个新对象,提供输入并将创建对象提交给控制器之后,`before_create`回调被调用**.然后控制器检查任何`before_create`回调.这似乎是违反直觉的,但它是一个命名法 - "before_create"指的是"创造"行动.实例化一个新对象不会"创建"该对象. (5认同)

小智 5

至少对于Rails 3.2.6中的布尔字段,这将适用于您的迁移.

def change
  add_column :users, :eula_accepted, :boolean, default: false
end
Run Code Online (Sandbox Code Playgroud)

在这里放置一个1或者0一个默认值是不行的,因为它是一个布尔字段.它必须是一个true或一个false值.


Pau*_*lgo 5

如果您正在处理模型,则可以在 Rails 5+ http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute 中使用 Attriutes API

只需添加具有正确列名的迁移,然后在模型中将其设置为:

class StoreListing < ActiveRecord::Base
  attribute :country, :string, default: 'PT'
end
Run Code Online (Sandbox Code Playgroud)