Ruby-on-rails 如何为 Rails activerecord 模型中的属性创建默认值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1550688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 21:51:23  来源:igfitidea点击:

How do I create a default value for attributes in Rails activerecord's model?

ruby-on-railsactiverecord

提问by Joshua Partogi

I want to create a default value for an attribute by defining it in ActiveRecord. By default everytime the record is created, I want to have a default value for attribute :status. I tried to do this:

我想通过在 ActiveRecord 中定义一个属性来创建它的默认值。默认情况下,每次创建记录时,我都希望属性有一个默认值:status。我试图这样做:

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

But upon creation I still retrieve this error from the database:

但是在创建时我仍然从数据库中检索到这个错误:

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

Therefore I presume the value was not applied to the attribute.

因此,我认为该值未应用于该属性。

What would be the elegant way to do this in Rails?

在 Rails 中这样做的优雅方式是什么?

Many thanks.

非常感谢。

回答by Jim

You can set a default option for the column in the migration

您可以为迁移中的列设置默认选项

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

OR

或者

You can use a callback, before_save

您可以使用回调, 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

回答by BeepDog

Because I encountered this issue just a little while ago, and the options for Rails 3.0 are a bit different, I'll provide another answer to this question.

因为我前段时间也遇到过这个问题,而且Rails 3.0的选项有点不同,所以我再提供一个这个问题的答案。

In Rails 3.0 you want to do something like this:

在 Rails 3.0 中,你想做这样的事情:

class MyModel < ActiveRecord::Base
  after_initialize :default_values

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

回答by Tim Santeford

When I need default values its usually for new records before the new action's view is rendered. The following method will set the default values for only new records so that they are available when rendering forms. before_saveand before_createare too lateand will not work if you want default values to show up in input fields.

当我需要默认值时,通常在呈现新操作的视图之前用于新记录。下面的方法将只为新记录设置默认值,以便在呈现表单时它们可用。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

回答by Daniel Kristensen

You can do it without writing any code at all :) You just need to set the default value for the column in the database. You can do this in your migrations. For example:

您可以在不编写任何代码的情况下完成此操作:) 您只需要为数据库中的列设置默认值。您可以在迁移中执行此操作。例如:

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

Hope that helps.

希望有帮助。

回答by EmFi

The solution depends on a few things.

解决方案取决于几件事。

Is the default value dependent on other information available at creation time? Can you wipe the database with minimal consequences?

默认值是否取决于创建时可用的其他信息?你能以最小的后果擦除数据库吗?

If you answered the first question yes, then you want to use Jim's solution

如果您回答第一个问题是肯定的,那么您想使用 Jim 的解决方案

If you answered the second question yes, then you want to use Daniel's solution

如果你回答第二个问题是,那么你想使用丹尼尔的解决方案

If you answered no to both questions, you're probably better off adding and running a new migration.

如果您对这两个问题的回答均为否,则最好添加并运行新的迁移。

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

:string can be replaced with any type that ActiveRecord::Migration recognizes.

:string 可以替换为 ActiveRecord::Migration 识别的任何类型。

CPU is cheap so the redefinition of Task in Jim's solution isn't going to cause many problems. Especially in a production environment. This migration is proper way of doing it as it is loaded it and called much less often.

CPU 很便宜,因此在 Jim 的解决方案中重新定义 Task 不会引起很多问题。尤其是在生产环境中。这种迁移是正确的方法,因为它被加载并且调用的频率要低得多。

回答by PeppyHeppy

I would consider using the attr_defaults found here. Your wildest dreams will come true.

我会考虑使用这里找到的 attr_defaults 。你最疯狂的梦想将会实现。

回答by swapab

Just strengthening Jim's answer

只是加强吉姆的回答

Using presenceone can do

使用存在可以做

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

回答by aec

For column types Rails supports out of the box - like the string in this question - the best approach is to set the column default in the database itself as Daniel Kristensen indicates. Rails will introspect on the DB and initialize the object accordingly. Plus, that makes your DB safe from somebody adding a row outside of your Rails app and forgetting to initialize that column.

对于列类型 Rails 支持开箱即用 - 就像这个问题中的字符串 - 最好的方法是在数据库本身中设置列默认值,如 Daniel Kristensen 指出的那样。Rails 将内省数据库并相应地初始化对象。另外,这使您的数据库免受有人在您的 Rails 应用程序之外添加一行并忘记初始化该列的影响。

For column types Rails doesn't support out of the box - e.g. ENUM columns - Rails won't be able to introspect the column default. For these cases you do notwant to use after_initialize (it is called every time an object is loaded from the DB as well as every time an object is created using .new), before_create (because it occurs after validation), or before_save (because it occurs upon update too, which is usually not what you want).

对于列类型 Rails 不支持开箱即用 - 例如 ENUM 列 - Rails 将无法内省列默认值。对于这些情况下,你希望使用after_initialize(它被称为每一个对象从数据库加载以及对象使用。新创建的每个时间的时间),before_create(因为它验证后出现),或before_save(因为它也会在更新时发生,这通常不是您想要的)。

Rather, you want to set the attribute in a before_validation on: create, like so:

相反,您希望在 before_validation on: create 中设置属性,如下所示:

before_validation :set_status_because_rails_cannot, on: :create

def set_status_because_rails_cannot
  self.status ||= 'P'
end

回答by Peter P.

As I see it, there are two problems that need addressing when needing a default value.

在我看来,当需要一个默认值时,有两个问题需要解决。

  1. You need the value present when a new object is initialized. Using after_initialize is not suitable because, as stated, it will be called during calls to #find which will lead to a performance hit.
  2. You need to persist the default value when saved
  1. 您需要在初始化新对象时存在的值。使用 after_initialize 是不合适的,因为如前所述,它将在调用 #find 期间调用,这将导致性能下降。
  2. 保存时需要保持默认值

Here is my solution:

这是我的解决方案:

# 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

I'd love to know why people think of this approach.

我很想知道为什么人们会想到这种方法。

回答by Joshua Partogi

I found a better way to do it now:

我现在找到了一个更好的方法:

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

In Ruby a method call is allowed to have no parentheses, therefore I should name the local variable into something else, otherwise Ruby will recognize it as a method call.

在 Ruby 中,方法调用允许没有括号,因此我应该将局部变量命名为其他名称,否则 Ruby 会将其识别为方法调用。