Web后端 深入浅出 ActiveRecord (1)

MyDataFlow · 发布于 2017年12月14日 · 368 次阅读
96

前言

在Rails中编写各种model的时候,只需要继承下ActiveRecord::Base,便可以自动得到所有的字段了。虽然都知道是从数据库直接拿字段描述,并映射到model上的。但是具体是如何实现的,一直让人不是很清楚。这里就分析下如何自动定义字段的。

如何自定义字段

何时触发字段定义

在active_record/base.rb中可以清晰的看到,ActiveRecord::Base是被定义为Class。在这个Class中并没有定义任何方法,都是通过extend和include将所需要的模块包含到其中。在这个过程会触发被包含的模块的included和extened方法。
从代码定义中可以看到自动定义字段的动作的代码是从active_record/core.rb触发的。


def allocate
  define_attribute_methods
  super  
end
def initialize(attributes = nil)

  self.class.define_attribute_methods
  @attributes = self.class._default_attributes.deep_dup

  init_internals
  initialize_internals_callback

  assign_attributes(attributes) if attributes
  ## if there is a block
  ## yield self to the block 
  yield self if block_given?
  _run_initialize_callbacks
end

ActiveRecord::Core通过extend的方式将包含了ActiveSupport::Concern。这样在ActiveRecord::Base对ActiveRecord::Core进行include的时候,会将ActiveRecord::Core中的ClassMethods直接注入到ActiveRecord::Base中,作为ActiveRecord::Base的类方法。ActiveRecord::Core考虑的非常周到,在allocate和initialise的方法中都会掉用define_attribute_methods方法来自动定义字段

define_attribute_methods方法

define_attribute_methods并没有定义在ActiveRecord::Core中,而是定义在active_record/attribute_methods.rb和active_model/attribute_methods.rb中。而ActiveRecord::AttributeMethods直接通过include的方式将ActiveModel::AttributeMethods包含在其中


# ActiveRecord::AttributeMethods
# Generates all the attribute related methods for columns in the database
# accessors, mutators and query methods.
def define_attribute_methods # :nodoc:
  return false if @attribute_methods_generated
  # Use a mutex; we don't want two threads simultaneously trying to define
  # attribute methods.
  generated_attribute_methods.synchronize do
    return false if @attribute_methods_generated
    ## base_class 是Base的直接子类或者非虚类的直接子类
    superclass.define_attribute_methods unless self == base_class
    super(attribute_names)
    @attribute_methods_generated = true
  end
end

ActiveRecord::AttributeMethods的代码最后会去调用ActiveModel::AttributeMethods中的define_attribute_methods进行字段定义。从前面可以知道define_attribute_methods是一个类方法,它自然而然会去调用类方法的attribute_names而非实例方法的attribute_names。
通过attribute_names这个类方法的调用,会引起active_record/model_schema.rb中的ActiveRecord::ModelSchema 进行load_schema。

def load_schema!
  @columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
  @columns_hash.each do |name, column|
    define_attribute(
      name,
      connection.lookup_cast_type_from_column(column),
      default: column.default,
      user_provided_default: false
    )
  end
end

这里可以看出ActiveRecord::ModelSchema会使用@columns_hash这个类实例变量进行缓存,后面会使用链接中的SchemaCache来获取相关的字段。

总结

基本上已经可以看清楚,ActiveRecord是如何进行字段自动定义的。但是如果每次派生类new的时候都去数据库进行查询的话,数据库会被累死的,因此在后面的文章中将介绍SchemaCache和数据库的链接是如何得到字段定义和属性的。

暂无回复。
需要 登录/注册 后方可回复, 如果你还没有账号请点击这里 注册