以下面的关联声明为例:
class Post
has_many :comments
end
通过声明has_many:comments,ActiveRecord添加了几个我特别感兴趣的方法 comments ,它返回注释数组。我浏览了代码,以下似乎是回调序列:
def has_many(association_id, options = {}, &extension)
reflection = create_has_many_reflection(association_id, options, &extension)
configure_dependency_for_has_many(reflection)
add_association_callbacks(reflection.name, reflection.options)
if options[:through]
collection_accessor_methods(reflection, HasManyThroughAssociation)
else
collection_accessor_methods(reflection, HasManyAssociation)
end
end
def collection_accessor_methods(reflection, association_proxy_class, writer = true)
collection_reader_method(reflection, association_proxy_class)
if writer
define_method("#{reflection.name}=") do |new_value|
# Loads proxy class instance (defined in collection_reader_method) if not already loaded
association = send(reflection.name)
association.replace(new_value)
association
end
define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
ids = (new_value || []).reject { |nid| nid.blank? }
send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
end
end
end
def collection_reader_method(reflection, association_proxy_class)
define_method(reflection.name) do |*params|
force_reload = params.first unless params.empty?
association = association_instance_get(reflection.name)
unless association
association = association_proxy_class.new(self, reflection)
association_instance_set(reflection.name, association)
end
association.reload if force_reload
association
end
define_method("#{reflection.name.to_s.singularize}_ids") do
if send(reflection.name).loaded? || reflection.options[:finder_sql]
send(reflection.name).map(&:id)
else
send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
end
end
end
在这个回调序列中,当我执行@ post.comments时,究竟是用于检索注释的实际SQL?
答案 0 :(得分:0)
您在此处:获取相关对象的所有ID的标准AR查询
send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
但确定Activerecord很混乱...... has_many的重新实现(更好的没有eval)可能对你有用:
def has_many(children)
send(:define_method, children){ eval(children.to_s.singularize.capitalize).all( :conditions => { self.class.name.downcase => name }) }
end
答案 1 :(得分:0)
在关联阅读器中的行
association = association_proxy_class.new(self, reflection)
最后将负责执行find,当实例变量被“询问”并且“看到”@loaded为false时。
答案 2 :(得分:0)
您需要深入了解HasManyAssociation的定义。
colletion_reader_method在Post类上定义了一个名为comments的方法。当调用comments方法时,它确保存储类HasManyAssociation的代理对象(您需要深入了解association_instance_set方法以查看它存储的确切位置),然后返回此代理对象。
我假设在代理上调用方法时会出现SQL,例如,调用each,all或使用[]访问索引。
答案 3 :(得分:0)
我不是100%确定我理解你在寻找什么。
sql生成不在AR中的一个位置。一些数据库特定的东西在数据库“connection_adapters”中。
如果您正在寻找在数据库中找到记录的方式,请查看ActiveRecord :: Base模块中的“construct_finder_sql”和“add_joins”方法。
def construct_finder_sql(options)
scope = scope(:find)
sql = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} "
sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
add_joins!(sql, options[:joins], scope)
...
和
def add_joins!(sql, joins, scope = :auto)
scope = scope(:find) if :auto == scope
merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
case merged_joins
when Symbol, Hash, Array
if array_of_strings?(merged_joins)
sql << merged_joins.join(' ') + " "
else
join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
sql << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} "
end
when String
sql << " #{merged_joins} "
end
end
我希望这有帮助!