如何在ActiveRecord中设置默认值?

时间:2022-09-11 17:35:33

How can I set default value in ActiveRecord?

如何在ActiveRecord中设置默认值?

I see a post from Pratik that describes an ugly, complicated chunk of code: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model

我在Pratik上看到一篇文章,描述了一段丑陋而复杂的代码:http://m.onkey.org/2007/24 /how-to-set-default- valued -in-your-model

class Item < ActiveRecord::Base  
  def initialize_with_defaults(attrs = nil, &block)
    initialize_without_defaults(attrs) do
      setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
        !attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
      setter.call('scheduler_type', 'hotseat')
      yield self if block_given?
    end
  end
  alias_method_chain :initialize, :defaults
end

I have seen the following examples googling around:

我曾见过以下谷歌搜索的例子:

  def initialize 
    super
    self.status = ACTIVE unless self.status
  end

and

  def after_initialize 
    return unless new_record?
    self.status = ACTIVE
  end

I've also seen people put it in their migration, but I'd rather see it defined in the model code.

我也看到人们把它放在他们的迁移中,但是我宁愿看到它在模型代码中定义。

Is there a canonical way to set default value for fields in ActiveRecord model?

在ActiveRecord模型中是否有一种规范的方法来设置字段的默认值?

25 个解决方案

#1


519  

There are several issues with each of the available methods, but I believe that defining an after_initialize callback is the way to go for the following reasons:

每个可用方法都有几个问题,但是我认为定义一个after_initialize回调是一种方式,原因如下:

  1. default_scope will initialize values for new models, but then that will become the scope on which you find the model. If you just want to initialize some numbers to 0 then this is not what you want.
  2. default_scope将初始化新模型的值,然后将成为找到模型的作用域。如果你只是想把一些数初始化为0那么这不是你想要的。
  3. Defining defaults in your migration also works part of the time... As has already been mentioned this will not work when you just call Model.new.
  4. 在您的迁移中定义默认值也在一定时间内生效…如前所述,当您仅仅调用Model.new时,这将不起作用。
  5. Overriding initialize can work, but don't forget to call super!
  6. 重写初始化可以工作,但是不要忘记调用super!
  7. Using a plugin like phusion's is getting a bit ridiculous. This is ruby, do we really need a plugin just to initialize some default values?
  8. 使用像phusion这样的插件有点荒唐。这是ruby,我们真的需要一个插件来初始化一些默认值吗?
  9. Overriding after_initialize is deprecated as of Rails 3. When I override after_initialize in rails 3.0.3 I get the following warning in the console:
  10. 重写after_initialize在Rails 3中不被使用。当我在rails 3.0.3中重写after_initialize时,我在控制台中会得到以下警告:

DEPRECATION WARNING: Base#after_initialize has been deprecated, please use Base.after_initialize :method instead. (called from /Users/me/myapp/app/models/my_model:15)

弃用警告:Base#after_initialize已经被弃用,请使用Base。after_initialize:方法代替。(从/用户/我/ myapp / app /模型/ my_model:15)

Therefore I'd say write an after_initialize callback, which lets you default attributes in addition to letting you set defaults on associations like so:

因此,我建议编写一个after_initialize回调,它允许您设置默认属性,并允许您设置如下关联的默认属性:

  class Person < ActiveRecord::Base
    has_one :address
    after_initialize :init

    def init
      self.number  ||= 0.0           #will set the default value only if it's nil
      self.address ||= build_address #let's you set a default association
    end
  end    

Now you have just one place to look for initialization of your models. I'm using this method until someone comes up with a better one.

现在您只有一个位置来查找模型的初始化。我在用这种方法,直到有人想出更好的方法。

Caveats:

警告:

  1. For boolean fields do:

    布尔字段:

    self.bool_field = true if self.bool_field.nil?

    自我。bool_field = true如果self。bool_fiel. nil?

    See Paul Russell's comment on this answer for more details

    有关更多细节,请参见Paul Russell对这个答案的评论

  2. If you're only selecting a subset of columns for a model (ie; using select in a query like Person.select(:firstname, :lastname).all) you will get a MissingAttributeError if your init method accesses a column that hasn't been included in the select clause. You can guard against this case like so:

    如果您只是为一个模型选择列的子集(例如;在类似Person的查询中使用select。选择(:firstname,:lastname).all)如果init方法访问select子句中未包含的列,则会出现MissingAttributeError。你可以这样做:

    self.number ||= 0.0 if self.has_attribute? :number

    自我。如果是self。has_attribute, ||= 0.0 ?数量:

    and for a boolean column...

    对于布尔列…

    self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?

    自我。bool_field = true if (self.has_attribute?bool_value)& & self.bool_field.nil ?

    Also note that the syntax is different prior to Rails 3.2 (see Cliff Darling's comment below)

    还要注意,在Rails 3.2之前,语法是不同的(参见克里夫·达林的评论)

#2


43  

We put the default values in the database through migrations (by specifying the :default option on each column definition) and let Active Record use these values to set the default for each attribute.

我们通过迁移(在每个列定义上指定:default选项)将默认值放入数据库,并让Active Record使用这些值为每个属性设置默认值。

IMHO, this approach is aligned with the principles of AR : convention over configuration, DRY, the table definition drives the model, not the other way around.

IMHO,这种方法与AR的原则是一致的:约定优于配置,干燥,表定义驱动模型,而不是相反。

Note that the defaults are still in the application (Ruby) code, though not in the model but in the migration(s).

注意,默认值仍然在应用程序(Ruby)代码中,虽然不是在模型中,而是在迁移中。

#3


38  

Some simple cases can be handled by defining a default in the database schema but that doesn't handle a number of trickier cases including calculated values and keys of other models. For these cases I do this:

一些简单的情况可以通过在数据库模式中定义一个默认值来处理,但是这并不能处理许多棘手的情况,包括计算值和其他模型的键。对于这些案例,我这样做:

after_initialize :defaults

def defaults
   unless persisted?
    self.extras||={}
    self.other_stuff||="This stuff"
    self.assoc = [OtherModel.find_by_name('special')]
  end
end

I've decided to use the after_initialize but I don't want it to be applied to objects that are found only those new or created. I think it is almost shocking that an after_new callback isn't provided for this obvious use case but I've made do by confirming whether the object is already persisted indicating that it isn't new.

我已经决定使用after_initialize,但我不希望它应用于仅找到那些新的或创建的对象。我认为,这个明显的用例没有提供after_new回调,这几乎令人震惊,但我已经通过确认对象是否已被持久化来表示它不是新的。

Having seen Brad Murray's answer this is even cleaner if the condition is moved to callback request:

看过布拉德·默里的回答后,如果条件被移动到回调请求,就更简洁了:

after_initialize :defaults, unless: :persisted?
              # ":if => :new_record?" is equivalent in this context

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
  self.assoc = [OtherModel.find_by_name('special')]
end

#4


22  

In Rails 5+, you can use the attribute method within your models, eg.:

在Rails 5+中,您可以在模型中使用属性方法,例如:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end

#5


17  

The after_initialize callback pattern can be improved by simply doing the following

只需执行以下操作,就可以改进after_initialize回调模式

after_initialize :some_method_goes_here, :if => :new_record?

This has a non-trivial benefit if your init code needs to deal with associations, as the following code triggers a subtle n+1 if you read the initial record without including the associated.

如果您的init代码需要处理关联,那么这有一个非平凡的好处,因为下面的代码会触发一个微妙的n+1,如果您不包括相关的初始记录的话。

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

#6


16  

The Phusion guys have some nice plugin for this.

Phusion的有些不错的插件。

#7


8  

I use the attribute-defaults gem

我使用属性-默认值gem

From the documentation: run sudo gem install attribute-defaults and add require 'attribute_defaults' to your app.

从文档中:运行sudo gem安装attribute-default,并将require 'attribute_defaults'添加到应用程序中。

class Foo < ActiveRecord::Base
  attr_default :age, 18
  attr_default :last_seen do
    Time.now
  end
end

Foo.new()           # => age: 18, last_seen => "2014-10-17 09:44:27"
Foo.new(:age => 25) # => age: 25, last_seen => "2014-10-17 09:44:28"

#8


7  

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

比所建议的答案更好/更清洁的方法是覆盖访问器,例如:

def status
  self['status'] || ACTIVE
end

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

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

#9


6  

Similar questions, but all have slightly different context: - How do I create a default value for attributes in Rails activerecord's model?

类似的问题,但都有稍微不同的上下文:-如何为Rails activerecord模型中的属性创建默认值?

Best Answer: Depends on What You Want!

最佳答案:取决于你想要什么!

If you want every object to start with a value: use after_initialize :init

如果希望每个对象都以值开头:使用after_initialize:init

You want the new.html form to have a default value upon opening the page? use https://*.com/a/5127684/1536309

你想要新。html表单在打开页面时具有默认值?使用https://*.com/a/5127684/1536309

class Person < ActiveRecord::Base
  has_one :address
  after_initialize :init

  def init
    self.number  ||= 0.0           #will set the default value only if it's nil
    self.address ||= build_address #let's you set a default association
  end
  ...
end 

If you want every object to have a value calculated from user input: use before_save :default_values You want user to enter X and then Y = X+'foo'? use:

如果希望每个对象都有一个从用户输入计算的值:使用before_save:default_values,您希望用户输入X,然后Y = X+'foo'?使用:

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

#10


4  

This is what constructors are for! Override the model's initialize method.

这就是构造函数的作用!重写模型的初始化方法。

Use the after_initialize method.

使用after_initialize方法。

#11


4  

Sup guys, I ended up doing the following:

吃晚饭的朋友们,我最后做了以下事情:

def after_initialize 
 self.extras||={}
 self.other_stuff||="This stuff"
end

Works like a charm!

就像一个魅力!

#12


4  

First things first: I do not disagree with Jeff's answer. It makes sense when your app is small and your logic simple. I am here trying to give an insight into how it can be a problem when building and maintaining a larger application. I do not recommend to use this approach first when building something small, but to keep it in mind as an alternative approach:

首先:我不同意杰夫的回答。当你的应用程序很小,逻辑很简单时,这是有意义的。在这里,我试图深入了解在构建和维护一个更大的应用程序时,它是如何成为一个问题的。我不建议在构建小的东西时首先使用这种方法,但是要记住它作为一种替代方法:


A question here is whether this default on records is business logic. If it is, I would be cautious to put it in the ORM model. Since the field ryw mentions is active, this does sound like business logic. E.g. the user is active.

这里的问题是,记录上的这个缺省值是否为业务逻辑。如果是的话,我会谨慎地把它放到ORM模型中。由于ryw提到的字段是活动的,这听起来确实像业务逻辑。用户是活跃的。

Why would I be wary to put business concerns in an ORM model?

为什么我要谨慎地把业务问题放在ORM模型中呢?

  1. It breaks SRP. Any class inheriting from ActiveRecord::Base is already doing a lot of different things, chief among them being data consistency (validations) and persistence (save). Putting business logic, however small it is, in with AR::Base breaks SRP.

    它打破了SRP。任何继承自ActiveRecord::Base的类都已经做了很多不同的事情,其中最主要的是数据一致性(验证)和持久性(保存)。将业务逻辑(无论多么小)与AR::Base break SRP放在一起。

  2. It is slower to test. If I want to test any form of logic happening in my ORM model, my tests have to initialise Rails in order to run. This wont be too much of a problem in thee beginning of your application, but will accumulate until your unit tests take a long time to run.

    它的测试速度较慢。如果我想测试ORM模型中发生的任何形式的逻辑,我的测试必须初始化Rails才能运行。在您的应用程序开始时,这对您来说不是什么大问题,但是在您的单元测试需要很长时间才能运行之前,这将会积累起来。

  3. It will break SRP even more down the line, and in concrete ways. Say our business now requires us to email users when there Item's become active? Now we are adding email logic to the Item ORM model, whose primary responsibility is modelling an Item. It should not care about email logic. This is a case of business side effects. These do not belong in the ORM model.

    它将以具体的方式打破SRP。假设我们的业务现在要求我们在项目变得活跃时给用户发电子邮件?现在,我们正在向项目ORM模型添加电子邮件逻辑,该模型的主要职责是对项目进行建模。它不应该关心电子邮件的逻辑。这是一个商业副作用的例子。这些不属于ORM模型。

  4. It is hard to diversify. I have seen mature Rails apps with things like a database backed init_type: string field, whose only purpose is to control the initialisation logic. This is polluting the database to fix a structural problem. There are better ways, I believe.

    很难实现多样化。我看到过一些成熟的Rails应用程序,它们支持数据库init_type: string字段,它的唯一目的是控制初始化逻辑。这将污染数据库以修复结构问题。我相信有更好的方法。

The PORO way: While this is a bit more code, it allows you to keep your ORM Models and Business Logic separate. The code here is simplified, but should show the idea:

PORO方法:虽然这是多一点的代码,但是它允许您将ORM模型和业务逻辑分开。这里的代码是简化的,但是应该表明:

class SellableItemFactory
  def self.new(attributes = {})
    record = Item.new(attributes)
    record.active = true if record.active.nil?
    record
  end
end

Then with this in place, the way to create a new Item would be

这样,创建一个新项目的方法就是这样

SellableItemFactory.new

And my tests could now simply verify that ItemFactory sets active on Item if it does not have a value. No Rails initialisation needed, no SRP breaking. When Item initialisation becomes more advanced (e.g. set a status field, a default type, etc.) the ItemFactory can have this added. If we end up with two types of defaults, we can create a new BusinesCaseItemFactory to do this.

我的测试现在可以简单地验证ItemFactory是否设置了活动项(如果它没有值)。没有必要的Rails初始化,没有SRP中断。当项目初始化变得更高级时(例如设置状态字段、默认类型等),ItemFactory可以添加这个。如果我们最终得到两种类型的默认值,我们可以创建一个新的BusinesCaseItemFactory来实现这一点。

NOTE: It could also be beneficial to use dependency injection here to allow the factory to build many active things, but I left it out for simplicity. Here it is: self.new(klass = Item, attributes = {})

注意:在这里使用依赖注入也可能是有益的,它允许工厂构建许多活动的东西,但是为了简单起见,我省略了它。这就是:自我。new(klass = Item, attributes = {})

#13


3  

This has been answered for a long time, but I need default values frequently and prefer not to put them in the database. I create a DefaultValues concern:

这已经得到了很长时间的响应,但是我经常需要默认值,并且不愿意将它们放在数据库中。我创建一个默认值关注点:

module DefaultValues
  extend ActiveSupport::Concern

  class_methods do
    def defaults(attr, to: nil, on: :initialize)
      method_name = "set_default_#{attr}"
      send "after_#{on}", method_name.to_sym

      define_method(method_name) do
        if send(attr)
          send(attr)
        else
          value = to.is_a?(Proc) ? to.call : to
          send("#{attr}=", value)
        end
      end

      private method_name
    end
  end
end

And then use it in my models like so:

然后在我的模型中使用如下:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end

#14


3  

I've also seen people put it in their migration, but I'd rather see it defined in the model code.

我也看到人们把它放在他们的迁移中,但是我宁愿看到它在模型代码中定义。

Is there a canonical way to set default value for fields in ActiveRecord model?

在ActiveRecord模型中是否有一种规范的方法来设置字段的默认值?

The canonical Rails way, before Rails 5, was actually to set it in the migration, and just look in the db/schema.rb for whenever wanting to see what default values are being set by the DB for any model.

在Rails 5之前,规范的Rails方法实际上是在迁移中设置它,并且只查看db/schema。每当需要查看DB为任何模型设置的默认值时的rb。

Contrary to what @Jeff Perrin answer states (which is a bit old), the migration approach will even apply the default when using Model.new, due to some Rails magic. Verified working in Rails 4.1.16.

与@Jeff Perrin回答状态(有点旧)相反,迁移方法甚至会在使用模型时应用默认值。新的,由于一些Rails的魔力。验证在Rails 4.1.16中工作。

The simplest thing is often the best. Less knowledge debt and potential points of confusion in the codebase. And it 'just works'.

最简单的东西往往是最好的。较少的知识债务和代码库中的潜在混淆点。这只是工作。

class AddStatusToItem < ActiveRecord::Migration
  def change
    add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
  end
end

The null: false disallows NULL values in the DB, and, as an added benefit, it also updates all pre-existing DB records is set with the default value for this field as well. You may exclude this parameter in the migration if you wish, but I found it very handy!

null: false不允许在DB中使用null值,并且,作为附加的好处,它还会更新所有已存在的DB记录,并设置该字段的默认值。如果您愿意,可以在迁移过程中排除这个参数,但是我发现它非常方便!

The canonical way in Rails 5+ is, as @Lucas Caton said:

正如@Lucas Caton所说,Rails 5+的规范方式是:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end

#15


1  

The problem with the after_initialize solutions is that you have to add an after_initialize to every single object you look up out of the DB, regardless of whether you access this attribute or not. I suggest a lazy-loaded approach.

after_initialize解决方案的问题是,无论您是否访问这个属性,都必须向从DB中查找的每个对象添加after_initialize。我建议采用延迟加载的方法。

The attribute methods (getters) are of course methods themselves, so you can override them and provide a default. Something like:

属性方法(getters)当然是方法本身,所以您可以重写它们并提供一个默认值。喜欢的东西:

Class Foo < ActiveRecord::Base
  # has a DB column/field atttribute called 'status'
  def status
    (val = read_attribute(:status)).nil? ? 'ACTIVE' : val
  end
end

Unless, like someone pointed out, you need to do Foo.find_by_status('ACTIVE'). In that case I think you'd really need to set the default in your database constraints, if the DB supports it.

除非,像有人指出的那样,您需要执行Foo.find_by_status('ACTIVE')。在这种情况下,我认为您确实需要在数据库约束中设置默认值,如果DB支持的话。

#16


1  

I ran into problems with after_initialize giving ActiveModel::MissingAttributeError errors when doing complex finds:

当执行复杂查找时,我遇到了after_initialize的问题:MissingAttributeError错误:

eg:

例如:

@bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)

"search" in the .where is hash of conditions

在.where中“search”是条件的散列

So I ended up doing it by overriding initialize in this way:

最后我重写了初始化

def initialize
  super
  default_values
end

private
 def default_values
     self.date_received ||= Date.current
 end

The super call is necessary to make sure the object initializing correctly from ActiveRecord::Base before doing my customize code, ie: default_values

在执行我的自定义代码(即:default_values)之前,需要进行超级调用,以确保从ActiveRecord::Base中正确初始化对象

#17


1  

class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end

#18


1  

I strongly suggest using the "default_value_for" gem: https://github.com/FooBarWidget/default_value_for

我强烈建议使用“default_value_for”gem: https://github.com/FooBarWidget/default_value_for。

There are some tricky scenarios that pretty much require overriding the initialize method, which that gem does.

有一些棘手的场景需要重写initialize方法,这正是gem所做的。

Examples:

例子:

Your db default is NULL, your model/ruby-defined default is "some string", but you actually want to set the value to nil for whatever reason: MyModel.new(my_attr: nil)

您的db默认值为NULL,您的模型/ruby定义的默认值是“一些字符串”,但是您实际上希望将值设置为nil,不管原因是什么:MyModel。新(my_attr:nil)

Most solutions here will fail to set the value to nil, and will instead set it to the default.

这里的大多数解决方案将无法将值设置为nil,而是将它设置为默认值。

OK, so instead of taking the ||= approach, you switch to my_attr_changed?...

那么,与其采用||=方法,不如切换到my_attr_changed?

BUT now imagine your db default is "some string", your model/ruby-defined default is "some other string", but under a certain scenario, you want to set the value to "some string" (the db default): MyModel.new(my_attr: 'some_string')

但是现在假设db默认值是“一些字符串”,模型/ruby定义的默认值是“一些其他字符串”,但是在某个场景下,您希望将值设置为“一些字符串”(db默认值):MyModel。新(my_attr:“some_string”)

This will result in my_attr_changed? being false because the value matches the db default, which in turn will fire your ruby-defined default code and set the value to "some other string" -- again, not what you desired.

这将导致my_attr_changed?之所以为false,是因为该值与db默认值相匹配,而db默认值将触发您的ruby定义的默认代码,并将该值设置为“其他字符串”——同样,这不是您想要的。


For those reasons I don't think this can properly be accomplished with just an after_initialize hook.

出于这些原因,我不认为仅使用after_initialize钩子就可以正确地完成这个任务。

Again, I think the "default_value_for" gem is taking the right approach: https://github.com/FooBarWidget/default_value_for

同样,我认为“default_value_for”gem采用了正确的方法:https://github.com/FooBarWidget/default_value_for。

#19


0  

Although doing that for setting default values is confusing and awkward in most cases, you can use :default_scope as well. Check out squil's comment here.

虽然在大多数情况下,设置默认值是令人困惑和尴尬的,但是您也可以使用:default_scope。看看squil的评论。

#20


0  

after_initialize method is deprecated, use the callback instead.

不赞成使用after_initialize方法,而应该使用回调。

after_initialize :defaults

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
end

however, using :default in your migrations is still the cleanest way.

然而,在迁移中使用:默认仍然是最干净的方式。

#21


0  

I've found that using a validation method provides a lot of control over setting defaults. You can even set defaults (or fail validation) for updates. You even set a different default value for inserts vs updates if you really wanted to. Note that the default won't be set until #valid? is called.

我发现使用验证方法可以提供对设置默认值的大量控制。您甚至可以为更新设置默认值(或失败验证)。您甚至可以为insert vs update设置不同的默认值。注意,在#有效之前不会设置默认值?被称为。

class MyModel
  validate :init_defaults

  private
  def init_defaults
    if new_record?
      self.some_int ||= 1
    elsif some_int.nil?
      errors.add(:some_int, "can't be blank on update")
    end
  end
end

Regarding defining an after_initialize method, there could be performance issues because after_initialize is also called by each object returned by :find : http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find

关于定义after_initialize方法,可能存在性能问题,因为after_initialize也会被每个由:find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find返回的对象调用

#22


0  

If the column happens to be a 'status' type column, and your model lends itself to the use of state machines, consider using the aasm gem, after which you can simply do

如果列恰好是一个“状态”类型的列,并且您的模型适合于使用状态机,请考虑使用aasm gem,然后您可以简单地使用它

  aasm column: "status" do
    state :available, initial: true
    state :used
    # transitions
  end

It still doesn't initialize the value for unsaved records, but it's a bit cleaner than rolling your own with init or whatever, and you reap the other benefits of aasm such as scopes for all your statuses.

它仍然没有初始化未保存记录的值,但是它比使用init或其他方式滚动自己的记录要干净一些,并且您可以获得aasm的其他好处,比如所有状态的作用域。

#23


0  

https://github.com/keithrowell/rails_default_value

https://github.com/keithrowell/rails_default_value

class Task < ActiveRecord::Base
  default :status => 'active'
end

#24


-1  

use default_scope in rails 3

在rails 3中使用default_scope。

api doc

api文档

ActiveRecord obscures the difference between defaulting defined in the database (schema) and defaulting done in the application (model). During initialization, it parses the database schema and notes any default values specified there. Later, when creating objects, it assigns those schema-specified default values without touching the database.

ActiveRecord模糊了数据库(模式)中定义的默认值和应用程序(模型)中执行的默认值之间的差异。在初始化过程中,它解析数据库模式并记录其中指定的任何默认值。稍后,在创建对象时,它会在不触及数据库的情况下分配这些模式指定的默认值。

discussion

讨论

#25


-2  

From the api docs http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Use the before_validation method in your model, it gives you the options of creating specific initialisation for create and update calls e.g. in this example (again code taken from the api docs example) the number field is initialised for a credit card. You can easily adapt this to set whatever values you want

在api文档http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html中,使用模型中的before_validation方法,它为创建和更新调用提供了创建特定初始化的选项。您可以轻松地调整它来设置您想要的任何值

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  before_validation(:on => :create) do
    self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

Surprised that his has not been suggested here

奇怪的是,这里没有人推荐他

#1


519  

There are several issues with each of the available methods, but I believe that defining an after_initialize callback is the way to go for the following reasons:

每个可用方法都有几个问题,但是我认为定义一个after_initialize回调是一种方式,原因如下:

  1. default_scope will initialize values for new models, but then that will become the scope on which you find the model. If you just want to initialize some numbers to 0 then this is not what you want.
  2. default_scope将初始化新模型的值,然后将成为找到模型的作用域。如果你只是想把一些数初始化为0那么这不是你想要的。
  3. Defining defaults in your migration also works part of the time... As has already been mentioned this will not work when you just call Model.new.
  4. 在您的迁移中定义默认值也在一定时间内生效…如前所述,当您仅仅调用Model.new时,这将不起作用。
  5. Overriding initialize can work, but don't forget to call super!
  6. 重写初始化可以工作,但是不要忘记调用super!
  7. Using a plugin like phusion's is getting a bit ridiculous. This is ruby, do we really need a plugin just to initialize some default values?
  8. 使用像phusion这样的插件有点荒唐。这是ruby,我们真的需要一个插件来初始化一些默认值吗?
  9. Overriding after_initialize is deprecated as of Rails 3. When I override after_initialize in rails 3.0.3 I get the following warning in the console:
  10. 重写after_initialize在Rails 3中不被使用。当我在rails 3.0.3中重写after_initialize时,我在控制台中会得到以下警告:

DEPRECATION WARNING: Base#after_initialize has been deprecated, please use Base.after_initialize :method instead. (called from /Users/me/myapp/app/models/my_model:15)

弃用警告:Base#after_initialize已经被弃用,请使用Base。after_initialize:方法代替。(从/用户/我/ myapp / app /模型/ my_model:15)

Therefore I'd say write an after_initialize callback, which lets you default attributes in addition to letting you set defaults on associations like so:

因此,我建议编写一个after_initialize回调,它允许您设置默认属性,并允许您设置如下关联的默认属性:

  class Person < ActiveRecord::Base
    has_one :address
    after_initialize :init

    def init
      self.number  ||= 0.0           #will set the default value only if it's nil
      self.address ||= build_address #let's you set a default association
    end
  end    

Now you have just one place to look for initialization of your models. I'm using this method until someone comes up with a better one.

现在您只有一个位置来查找模型的初始化。我在用这种方法,直到有人想出更好的方法。

Caveats:

警告:

  1. For boolean fields do:

    布尔字段:

    self.bool_field = true if self.bool_field.nil?

    自我。bool_field = true如果self。bool_fiel. nil?

    See Paul Russell's comment on this answer for more details

    有关更多细节,请参见Paul Russell对这个答案的评论

  2. If you're only selecting a subset of columns for a model (ie; using select in a query like Person.select(:firstname, :lastname).all) you will get a MissingAttributeError if your init method accesses a column that hasn't been included in the select clause. You can guard against this case like so:

    如果您只是为一个模型选择列的子集(例如;在类似Person的查询中使用select。选择(:firstname,:lastname).all)如果init方法访问select子句中未包含的列,则会出现MissingAttributeError。你可以这样做:

    self.number ||= 0.0 if self.has_attribute? :number

    自我。如果是self。has_attribute, ||= 0.0 ?数量:

    and for a boolean column...

    对于布尔列…

    self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?

    自我。bool_field = true if (self.has_attribute?bool_value)& & self.bool_field.nil ?

    Also note that the syntax is different prior to Rails 3.2 (see Cliff Darling's comment below)

    还要注意,在Rails 3.2之前,语法是不同的(参见克里夫·达林的评论)

#2


43  

We put the default values in the database through migrations (by specifying the :default option on each column definition) and let Active Record use these values to set the default for each attribute.

我们通过迁移(在每个列定义上指定:default选项)将默认值放入数据库,并让Active Record使用这些值为每个属性设置默认值。

IMHO, this approach is aligned with the principles of AR : convention over configuration, DRY, the table definition drives the model, not the other way around.

IMHO,这种方法与AR的原则是一致的:约定优于配置,干燥,表定义驱动模型,而不是相反。

Note that the defaults are still in the application (Ruby) code, though not in the model but in the migration(s).

注意,默认值仍然在应用程序(Ruby)代码中,虽然不是在模型中,而是在迁移中。

#3


38  

Some simple cases can be handled by defining a default in the database schema but that doesn't handle a number of trickier cases including calculated values and keys of other models. For these cases I do this:

一些简单的情况可以通过在数据库模式中定义一个默认值来处理,但是这并不能处理许多棘手的情况,包括计算值和其他模型的键。对于这些案例,我这样做:

after_initialize :defaults

def defaults
   unless persisted?
    self.extras||={}
    self.other_stuff||="This stuff"
    self.assoc = [OtherModel.find_by_name('special')]
  end
end

I've decided to use the after_initialize but I don't want it to be applied to objects that are found only those new or created. I think it is almost shocking that an after_new callback isn't provided for this obvious use case but I've made do by confirming whether the object is already persisted indicating that it isn't new.

我已经决定使用after_initialize,但我不希望它应用于仅找到那些新的或创建的对象。我认为,这个明显的用例没有提供after_new回调,这几乎令人震惊,但我已经通过确认对象是否已被持久化来表示它不是新的。

Having seen Brad Murray's answer this is even cleaner if the condition is moved to callback request:

看过布拉德·默里的回答后,如果条件被移动到回调请求,就更简洁了:

after_initialize :defaults, unless: :persisted?
              # ":if => :new_record?" is equivalent in this context

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
  self.assoc = [OtherModel.find_by_name('special')]
end

#4


22  

In Rails 5+, you can use the attribute method within your models, eg.:

在Rails 5+中,您可以在模型中使用属性方法,例如:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end

#5


17  

The after_initialize callback pattern can be improved by simply doing the following

只需执行以下操作,就可以改进after_initialize回调模式

after_initialize :some_method_goes_here, :if => :new_record?

This has a non-trivial benefit if your init code needs to deal with associations, as the following code triggers a subtle n+1 if you read the initial record without including the associated.

如果您的init代码需要处理关联,那么这有一个非平凡的好处,因为下面的代码会触发一个微妙的n+1,如果您不包括相关的初始记录的话。

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

#6


16  

The Phusion guys have some nice plugin for this.

Phusion的有些不错的插件。

#7


8  

I use the attribute-defaults gem

我使用属性-默认值gem

From the documentation: run sudo gem install attribute-defaults and add require 'attribute_defaults' to your app.

从文档中:运行sudo gem安装attribute-default,并将require 'attribute_defaults'添加到应用程序中。

class Foo < ActiveRecord::Base
  attr_default :age, 18
  attr_default :last_seen do
    Time.now
  end
end

Foo.new()           # => age: 18, last_seen => "2014-10-17 09:44:27"
Foo.new(:age => 25) # => age: 25, last_seen => "2014-10-17 09:44:28"

#8


7  

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

比所建议的答案更好/更清洁的方法是覆盖访问器,例如:

def status
  self['status'] || ACTIVE
end

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

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

#9


6  

Similar questions, but all have slightly different context: - How do I create a default value for attributes in Rails activerecord's model?

类似的问题,但都有稍微不同的上下文:-如何为Rails activerecord模型中的属性创建默认值?

Best Answer: Depends on What You Want!

最佳答案:取决于你想要什么!

If you want every object to start with a value: use after_initialize :init

如果希望每个对象都以值开头:使用after_initialize:init

You want the new.html form to have a default value upon opening the page? use https://*.com/a/5127684/1536309

你想要新。html表单在打开页面时具有默认值?使用https://*.com/a/5127684/1536309

class Person < ActiveRecord::Base
  has_one :address
  after_initialize :init

  def init
    self.number  ||= 0.0           #will set the default value only if it's nil
    self.address ||= build_address #let's you set a default association
  end
  ...
end 

If you want every object to have a value calculated from user input: use before_save :default_values You want user to enter X and then Y = X+'foo'? use:

如果希望每个对象都有一个从用户输入计算的值:使用before_save:default_values,您希望用户输入X,然后Y = X+'foo'?使用:

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

#10


4  

This is what constructors are for! Override the model's initialize method.

这就是构造函数的作用!重写模型的初始化方法。

Use the after_initialize method.

使用after_initialize方法。

#11


4  

Sup guys, I ended up doing the following:

吃晚饭的朋友们,我最后做了以下事情:

def after_initialize 
 self.extras||={}
 self.other_stuff||="This stuff"
end

Works like a charm!

就像一个魅力!

#12


4  

First things first: I do not disagree with Jeff's answer. It makes sense when your app is small and your logic simple. I am here trying to give an insight into how it can be a problem when building and maintaining a larger application. I do not recommend to use this approach first when building something small, but to keep it in mind as an alternative approach:

首先:我不同意杰夫的回答。当你的应用程序很小,逻辑很简单时,这是有意义的。在这里,我试图深入了解在构建和维护一个更大的应用程序时,它是如何成为一个问题的。我不建议在构建小的东西时首先使用这种方法,但是要记住它作为一种替代方法:


A question here is whether this default on records is business logic. If it is, I would be cautious to put it in the ORM model. Since the field ryw mentions is active, this does sound like business logic. E.g. the user is active.

这里的问题是,记录上的这个缺省值是否为业务逻辑。如果是的话,我会谨慎地把它放到ORM模型中。由于ryw提到的字段是活动的,这听起来确实像业务逻辑。用户是活跃的。

Why would I be wary to put business concerns in an ORM model?

为什么我要谨慎地把业务问题放在ORM模型中呢?

  1. It breaks SRP. Any class inheriting from ActiveRecord::Base is already doing a lot of different things, chief among them being data consistency (validations) and persistence (save). Putting business logic, however small it is, in with AR::Base breaks SRP.

    它打破了SRP。任何继承自ActiveRecord::Base的类都已经做了很多不同的事情,其中最主要的是数据一致性(验证)和持久性(保存)。将业务逻辑(无论多么小)与AR::Base break SRP放在一起。

  2. It is slower to test. If I want to test any form of logic happening in my ORM model, my tests have to initialise Rails in order to run. This wont be too much of a problem in thee beginning of your application, but will accumulate until your unit tests take a long time to run.

    它的测试速度较慢。如果我想测试ORM模型中发生的任何形式的逻辑,我的测试必须初始化Rails才能运行。在您的应用程序开始时,这对您来说不是什么大问题,但是在您的单元测试需要很长时间才能运行之前,这将会积累起来。

  3. It will break SRP even more down the line, and in concrete ways. Say our business now requires us to email users when there Item's become active? Now we are adding email logic to the Item ORM model, whose primary responsibility is modelling an Item. It should not care about email logic. This is a case of business side effects. These do not belong in the ORM model.

    它将以具体的方式打破SRP。假设我们的业务现在要求我们在项目变得活跃时给用户发电子邮件?现在,我们正在向项目ORM模型添加电子邮件逻辑,该模型的主要职责是对项目进行建模。它不应该关心电子邮件的逻辑。这是一个商业副作用的例子。这些不属于ORM模型。

  4. It is hard to diversify. I have seen mature Rails apps with things like a database backed init_type: string field, whose only purpose is to control the initialisation logic. This is polluting the database to fix a structural problem. There are better ways, I believe.

    很难实现多样化。我看到过一些成熟的Rails应用程序,它们支持数据库init_type: string字段,它的唯一目的是控制初始化逻辑。这将污染数据库以修复结构问题。我相信有更好的方法。

The PORO way: While this is a bit more code, it allows you to keep your ORM Models and Business Logic separate. The code here is simplified, but should show the idea:

PORO方法:虽然这是多一点的代码,但是它允许您将ORM模型和业务逻辑分开。这里的代码是简化的,但是应该表明:

class SellableItemFactory
  def self.new(attributes = {})
    record = Item.new(attributes)
    record.active = true if record.active.nil?
    record
  end
end

Then with this in place, the way to create a new Item would be

这样,创建一个新项目的方法就是这样

SellableItemFactory.new

And my tests could now simply verify that ItemFactory sets active on Item if it does not have a value. No Rails initialisation needed, no SRP breaking. When Item initialisation becomes more advanced (e.g. set a status field, a default type, etc.) the ItemFactory can have this added. If we end up with two types of defaults, we can create a new BusinesCaseItemFactory to do this.

我的测试现在可以简单地验证ItemFactory是否设置了活动项(如果它没有值)。没有必要的Rails初始化,没有SRP中断。当项目初始化变得更高级时(例如设置状态字段、默认类型等),ItemFactory可以添加这个。如果我们最终得到两种类型的默认值,我们可以创建一个新的BusinesCaseItemFactory来实现这一点。

NOTE: It could also be beneficial to use dependency injection here to allow the factory to build many active things, but I left it out for simplicity. Here it is: self.new(klass = Item, attributes = {})

注意:在这里使用依赖注入也可能是有益的,它允许工厂构建许多活动的东西,但是为了简单起见,我省略了它。这就是:自我。new(klass = Item, attributes = {})

#13


3  

This has been answered for a long time, but I need default values frequently and prefer not to put them in the database. I create a DefaultValues concern:

这已经得到了很长时间的响应,但是我经常需要默认值,并且不愿意将它们放在数据库中。我创建一个默认值关注点:

module DefaultValues
  extend ActiveSupport::Concern

  class_methods do
    def defaults(attr, to: nil, on: :initialize)
      method_name = "set_default_#{attr}"
      send "after_#{on}", method_name.to_sym

      define_method(method_name) do
        if send(attr)
          send(attr)
        else
          value = to.is_a?(Proc) ? to.call : to
          send("#{attr}=", value)
        end
      end

      private method_name
    end
  end
end

And then use it in my models like so:

然后在我的模型中使用如下:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end

#14


3  

I've also seen people put it in their migration, but I'd rather see it defined in the model code.

我也看到人们把它放在他们的迁移中,但是我宁愿看到它在模型代码中定义。

Is there a canonical way to set default value for fields in ActiveRecord model?

在ActiveRecord模型中是否有一种规范的方法来设置字段的默认值?

The canonical Rails way, before Rails 5, was actually to set it in the migration, and just look in the db/schema.rb for whenever wanting to see what default values are being set by the DB for any model.

在Rails 5之前,规范的Rails方法实际上是在迁移中设置它,并且只查看db/schema。每当需要查看DB为任何模型设置的默认值时的rb。

Contrary to what @Jeff Perrin answer states (which is a bit old), the migration approach will even apply the default when using Model.new, due to some Rails magic. Verified working in Rails 4.1.16.

与@Jeff Perrin回答状态(有点旧)相反,迁移方法甚至会在使用模型时应用默认值。新的,由于一些Rails的魔力。验证在Rails 4.1.16中工作。

The simplest thing is often the best. Less knowledge debt and potential points of confusion in the codebase. And it 'just works'.

最简单的东西往往是最好的。较少的知识债务和代码库中的潜在混淆点。这只是工作。

class AddStatusToItem < ActiveRecord::Migration
  def change
    add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
  end
end

The null: false disallows NULL values in the DB, and, as an added benefit, it also updates all pre-existing DB records is set with the default value for this field as well. You may exclude this parameter in the migration if you wish, but I found it very handy!

null: false不允许在DB中使用null值,并且,作为附加的好处,它还会更新所有已存在的DB记录,并设置该字段的默认值。如果您愿意,可以在迁移过程中排除这个参数,但是我发现它非常方便!

The canonical way in Rails 5+ is, as @Lucas Caton said:

正如@Lucas Caton所说,Rails 5+的规范方式是:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end

#15


1  

The problem with the after_initialize solutions is that you have to add an after_initialize to every single object you look up out of the DB, regardless of whether you access this attribute or not. I suggest a lazy-loaded approach.

after_initialize解决方案的问题是,无论您是否访问这个属性,都必须向从DB中查找的每个对象添加after_initialize。我建议采用延迟加载的方法。

The attribute methods (getters) are of course methods themselves, so you can override them and provide a default. Something like:

属性方法(getters)当然是方法本身,所以您可以重写它们并提供一个默认值。喜欢的东西:

Class Foo < ActiveRecord::Base
  # has a DB column/field atttribute called 'status'
  def status
    (val = read_attribute(:status)).nil? ? 'ACTIVE' : val
  end
end

Unless, like someone pointed out, you need to do Foo.find_by_status('ACTIVE'). In that case I think you'd really need to set the default in your database constraints, if the DB supports it.

除非,像有人指出的那样,您需要执行Foo.find_by_status('ACTIVE')。在这种情况下,我认为您确实需要在数据库约束中设置默认值,如果DB支持的话。

#16


1  

I ran into problems with after_initialize giving ActiveModel::MissingAttributeError errors when doing complex finds:

当执行复杂查找时,我遇到了after_initialize的问题:MissingAttributeError错误:

eg:

例如:

@bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)

"search" in the .where is hash of conditions

在.where中“search”是条件的散列

So I ended up doing it by overriding initialize in this way:

最后我重写了初始化

def initialize
  super
  default_values
end

private
 def default_values
     self.date_received ||= Date.current
 end

The super call is necessary to make sure the object initializing correctly from ActiveRecord::Base before doing my customize code, ie: default_values

在执行我的自定义代码(即:default_values)之前,需要进行超级调用,以确保从ActiveRecord::Base中正确初始化对象

#17


1  

class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end

#18


1  

I strongly suggest using the "default_value_for" gem: https://github.com/FooBarWidget/default_value_for

我强烈建议使用“default_value_for”gem: https://github.com/FooBarWidget/default_value_for。

There are some tricky scenarios that pretty much require overriding the initialize method, which that gem does.

有一些棘手的场景需要重写initialize方法,这正是gem所做的。

Examples:

例子:

Your db default is NULL, your model/ruby-defined default is "some string", but you actually want to set the value to nil for whatever reason: MyModel.new(my_attr: nil)

您的db默认值为NULL,您的模型/ruby定义的默认值是“一些字符串”,但是您实际上希望将值设置为nil,不管原因是什么:MyModel。新(my_attr:nil)

Most solutions here will fail to set the value to nil, and will instead set it to the default.

这里的大多数解决方案将无法将值设置为nil,而是将它设置为默认值。

OK, so instead of taking the ||= approach, you switch to my_attr_changed?...

那么,与其采用||=方法,不如切换到my_attr_changed?

BUT now imagine your db default is "some string", your model/ruby-defined default is "some other string", but under a certain scenario, you want to set the value to "some string" (the db default): MyModel.new(my_attr: 'some_string')

但是现在假设db默认值是“一些字符串”,模型/ruby定义的默认值是“一些其他字符串”,但是在某个场景下,您希望将值设置为“一些字符串”(db默认值):MyModel。新(my_attr:“some_string”)

This will result in my_attr_changed? being false because the value matches the db default, which in turn will fire your ruby-defined default code and set the value to "some other string" -- again, not what you desired.

这将导致my_attr_changed?之所以为false,是因为该值与db默认值相匹配,而db默认值将触发您的ruby定义的默认代码,并将该值设置为“其他字符串”——同样,这不是您想要的。


For those reasons I don't think this can properly be accomplished with just an after_initialize hook.

出于这些原因,我不认为仅使用after_initialize钩子就可以正确地完成这个任务。

Again, I think the "default_value_for" gem is taking the right approach: https://github.com/FooBarWidget/default_value_for

同样,我认为“default_value_for”gem采用了正确的方法:https://github.com/FooBarWidget/default_value_for。

#19


0  

Although doing that for setting default values is confusing and awkward in most cases, you can use :default_scope as well. Check out squil's comment here.

虽然在大多数情况下,设置默认值是令人困惑和尴尬的,但是您也可以使用:default_scope。看看squil的评论。

#20


0  

after_initialize method is deprecated, use the callback instead.

不赞成使用after_initialize方法,而应该使用回调。

after_initialize :defaults

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
end

however, using :default in your migrations is still the cleanest way.

然而,在迁移中使用:默认仍然是最干净的方式。

#21


0  

I've found that using a validation method provides a lot of control over setting defaults. You can even set defaults (or fail validation) for updates. You even set a different default value for inserts vs updates if you really wanted to. Note that the default won't be set until #valid? is called.

我发现使用验证方法可以提供对设置默认值的大量控制。您甚至可以为更新设置默认值(或失败验证)。您甚至可以为insert vs update设置不同的默认值。注意,在#有效之前不会设置默认值?被称为。

class MyModel
  validate :init_defaults

  private
  def init_defaults
    if new_record?
      self.some_int ||= 1
    elsif some_int.nil?
      errors.add(:some_int, "can't be blank on update")
    end
  end
end

Regarding defining an after_initialize method, there could be performance issues because after_initialize is also called by each object returned by :find : http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find

关于定义after_initialize方法,可能存在性能问题,因为after_initialize也会被每个由:find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find返回的对象调用

#22


0  

If the column happens to be a 'status' type column, and your model lends itself to the use of state machines, consider using the aasm gem, after which you can simply do

如果列恰好是一个“状态”类型的列,并且您的模型适合于使用状态机,请考虑使用aasm gem,然后您可以简单地使用它

  aasm column: "status" do
    state :available, initial: true
    state :used
    # transitions
  end

It still doesn't initialize the value for unsaved records, but it's a bit cleaner than rolling your own with init or whatever, and you reap the other benefits of aasm such as scopes for all your statuses.

它仍然没有初始化未保存记录的值,但是它比使用init或其他方式滚动自己的记录要干净一些,并且您可以获得aasm的其他好处,比如所有状态的作用域。

#23


0  

https://github.com/keithrowell/rails_default_value

https://github.com/keithrowell/rails_default_value

class Task < ActiveRecord::Base
  default :status => 'active'
end

#24


-1  

use default_scope in rails 3

在rails 3中使用default_scope。

api doc

api文档

ActiveRecord obscures the difference between defaulting defined in the database (schema) and defaulting done in the application (model). During initialization, it parses the database schema and notes any default values specified there. Later, when creating objects, it assigns those schema-specified default values without touching the database.

ActiveRecord模糊了数据库(模式)中定义的默认值和应用程序(模型)中执行的默认值之间的差异。在初始化过程中,它解析数据库模式并记录其中指定的任何默认值。稍后,在创建对象时,它会在不触及数据库的情况下分配这些模式指定的默认值。

discussion

讨论

#25


-2  

From the api docs http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Use the before_validation method in your model, it gives you the options of creating specific initialisation for create and update calls e.g. in this example (again code taken from the api docs example) the number field is initialised for a credit card. You can easily adapt this to set whatever values you want

在api文档http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html中,使用模型中的before_validation方法,它为创建和更新调用提供了创建特定初始化的选项。您可以轻松地调整它来设置您想要的任何值

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  before_validation(:on => :create) do
    self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

Surprised that his has not been suggested here

奇怪的是,这里没有人推荐他