在 Rails 中,ActiveRecord::Base.new
用于实例化尚未保存到数据库的新记录:
new_user = User.new(name: "Bob")
new_user.new_record? # => true
那么 Rails 如何实例化从数据库中检索到的记录呢?它是否使用相同的新方法,然后像@new_record
事后一样更改值?还是它对从数据库中检索到的记录使用某种特殊的实例化方法?
在 Rails 中,ActiveRecord::Base.new
用于实例化尚未保存到数据库的新记录:
new_user = User.new(name: "Bob")
new_user.new_record? # => true
那么 Rails 如何实例化从数据库中检索到的记录呢?它是否使用相同的新方法,然后像@new_record
事后一样更改值?还是它对从数据库中检索到的记录使用某种特殊的实例化方法?
新记录?方法可以在 ActiveRecord 框架的active_record/persistence.rb中找到,它看起来像这样:
def new_record?
@new_record
end
然后,如果您在构造函数中查看active_record/core.rb,您将看到:
def initialize(attributes = nil, options = {})
@attributes = self.class.initialize_attributes(self.class.column_defaults.deep_dup)
@columns_hash = self.class.column_types.dup
init_internals # here
ensure_proper_type
populate_with_current_scope_attributes
assign_attributes(attributes, options) if attributes
yield self if block_given?
run_callbacks :initialize if _initialize_callbacks.any?
end
如果我们更深入地研究代码:
def init_internals
pk = self.class.primary_key
@attributes[pk] = nil unless @attributes.key?(pk)
@aggregation_cache = {}
@association_cache = {}
@attributes_cache = {}
@previously_changed = {}
@changed_attributes = {}
@readonly = false
@destroyed = false
@marked_for_destruction = false
@new_record = true # here
@mass_assignment_options = nil
end
如您所见,@new_record 默认初始化为 true。
但是,在某些情况下,@new_record 属性设置为 true,就像克隆记录时一样:
user = User.first
new_user = user.clone
这将调用如下所示的initialize_dup方法:
def initialize_dup(other) # :nodoc:
# Code removed
@new_record = true
# Code removed
super
end
当然,当 ActiveRecord 从数据库中提取记录时。我不确定这部分,但我认为这个方法被称为:
def init_with(coder)
@attributes = self.class.initialize_attributes(coder['attributes'])
@columns_hash = self.class.column_types.merge(coder['column_types'] || {})
init_internals
@new_record = false
run_callbacks :find
run_callbacks :initialize
self
end
可以这样做:
post = Post.allocate
post.init_with('attributes' => { 'title' => 'hello world' })
在第一个语句中,它在堆上分配内存空间,而不像 new 那样调用构造函数。然后它调用特殊的构造函数init_with。
它是用instantiate
方法完成的,它使用低级allocate
方法而不是new
您可以在此处找到此方法。