ActiveRecord :: Relation对象如何调用类方法?
class Project < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :project
def self.initial_tasks # class methods
# here return initial tasks
end
end
现在我们可以致电:
Project.first.tasks.initial_tasks # how it works
initial_tasks
是一个类方法,我们不能在对象上调用类方法。
Project.first.tasks
返回一个ActiveRecord :: Relation对象,那么它怎么能够调用initial_tasks
?
请解释。
答案 0 :(得分:32)
关于ActiveRecord::Relation
对象的类方法的应用程序没有太多文档,但我们可以通过查看ActiveRecord scopes的工作原理来理解这种行为。
首先,Rails模型范围将返回ActiveRecord::Relation
对象。来自文档:
模型上的类方法可自动在范围内使用。假设以下设置:
class Article < ActiveRecord::Base
scope :published, -> { where(published: true) }
scope :featured, -> { where(featured: true) }
def self.latest_article
order('published_at desc').first
end
def self.titles
pluck(:title)
end
end
首先,调用范围会返回ActiveRecord::Relation
对象:
Article.published.class
#=> ActiveRecord::Relation
Article.featured.class
#=> ActiveRecord::Relation
然后,您可以使用相应模型的类方法对ActiveRecord::Relation
对象进行操作:
Article.published.featured.latest_article
Article.featured.titles
这是理解类方法与ActiveRecord::Relation
之间关系的一种迂回方式,但要点是:
ActiveRecord::Relation
个对象ActiveRecord::Relation
个对象可以访问类方法答案 1 :(得分:24)
它非常容易探索。你这样做了:
class Project < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :project
def self.initial_tasks #class methods
1 / 0
end
end
然后致电Project.first.tasks.initial_tasks
,您就会得到:
Division by zero
...
.../gems/activerecord-4.1.0/lib/active_record/relation/delegation.rb:70:in `block in re
.../gems/activerecord-4.1.0/lib/active_record/associations/collection_proxy.rb:872:in `
.../gems/activerecord-4.1.0/lib/active_record/relation.rb:286:in `scoping'",
.../gems/activerecord-4.1.0/lib/active_record/associations/collection_proxy.rb:872:in `
.../gems/activerecord-4.1.0/lib/active_record/relation/delegation.rb:70:in `initial_tasks'",
基本上你需要的就是这些。易于探索但不太容易理解。
现在我将解释这意味着什么。
当您调用Project#tasks
方法时,不会返回您的ActiveRecord :: Relation对象。实际上它会返回一个运行时创建的类的实例,该类名为Task :: ActiveRecord_Associations_CollectionProxy,它继承自ActiveRecord :: Associations :: CollextionProxy,后者继承自ActiveRecord :: Relation。这个运行时创建的类与Task类链接,并包含动态定义的(通过method_missing)代理方法,这些方法委托对Task类方法的调用,并将关联范围与类级方法返回的类定义范围合并。
它如何运作(真的非平凡):
每次继承ActiveRecord :: Base时,DelegateCache都有DelegateCache.inherited
回调定义@relation_delegate_cache
属性。这意味着所有AR :: Base后代类都将具有此类属性。回调calls DelegateCache#initialize_relation_delegate_cache
方法,用于使用运行时创建的类填充缓存属性:
[
ActiveRecord::Relation,
ActiveRecord::Associations::CollectionProxy,
ActiveRecord::AssociationRelation
].each do |klass|
delegate = Class.new(klass) {
include ClassSpecificRelation
}
const_set klass.name.gsub('::', '_'), delegate
cache[klass] = delegate
end
这些类在这里得到了不寻常的名字a-la Task::ActiveRecord_Associations_CollectionProxy
前面提到过。
#initial_tasks
上调用Project.tasks
)。在此类调用它dynamically defines新的运行时类实例方法,它们委托给类级方法。现在,您将Task类链接到Task :: ActiveRecord_Associations_CollectionProxy类,其中包含代理对Task类级方法的调用的所有实例级方法,获取范围结果并将其与当前关联范围(here)合并。这是AR如何在运行时创建的类上使用动态定义的方法而不是在ActiveRecord :: Relation上使用低效的method_missing调用。
如果您不理解所有这些内容,我认为没问题。只需在关联上调用类级别方法:)
答案 2 :(得分:0)
在ActiveRecord :: Relation中,Relation表示整个表 你的班级邮寄地图是表格,
所以ActiveRecord :: Relation是数组或单个记录,它可以访问类方法。
答案 3 :(得分:0)
对于在实际访问类方法内的记录筛选列表中遇到麻烦的任何人,您只需调用all
即可返回所需的列表,而不是该表中所有通过{访问的记录{1}}。