Dow*_*ker 5 ruby-on-rails rails-activerecord ruby-on-rails-5
我有一些属性需要有默认值。我已经设置了迁移以在数据库中设置默认值,如下所示:
class AddDefaultsToModel < ActiveRecord::Migration[5.2]
def change
change_column :posts, :post_type, :string, default: 'draft'
change_column :posts, :date_time, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
end
end
Run Code Online (Sandbox Code Playgroud)
直接添加到数据库时,默认值效果很好。但是,如果我在 Rails 中构建一个新模型,一个属性会按预期工作,而另一个则不会:
post = Post.new
post.post_type # draft (as expected)
post.date_time # nil (expecting the current date and time)
Run Code Online (Sandbox Code Playgroud)
这种行为是故意的吗?我是否也必须在模型中设置默认值?为什么Post#post_type有效但无效Post#date_time?
ActiveRecord 不理解您的默认值的含义date_time,因此它根本不提供date_time默认值。然后,当您将该行插入数据库时(即post.save),数据库将使用当前时间戳作为值date_time(当然假设没有人接触过date_time)。Rails 不会知道date_time插入后有一个值,因此您会得到如下行为:
post = Post.new
post.date_time # `nil` because it hasn't been set at all
# set some other values on `post`...
post.save # INSERT INTO posts (columns other than date_time...) values (...)
post.date_time # `nil` even thought there is a value in the database
post.reload # Pull everything out of the database
post.date_time # Now we have a timestamp
Run Code Online (Sandbox Code Playgroud)
您有一些选择:
post.reload保存后调用post以获取数据库使用的默认时间戳。
使用after_initialize钩子自行设置默认值:
class Post < ApplicationRecord
after_initialize if: :new_record? do
self.date_time = Time.now
end
end
Run Code Online (Sandbox Code Playgroud)使用属性 API手动设置默认值:
class Post < ApplicationRecord
attribute :date_time, :datetime, default: ->{ Time.now }
end
Run Code Online (Sandbox Code Playgroud)
您需要使用 lambda(或 Proc),以便Time.now在正确的时间执行。