浏览 3195 次
锁定老帖子 主题:Rails宝典之第三式: 通过关联做查询
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-07-26
我们来看一个has_many关联的例子: class Project < ActiveRecord::Base has_many :tasks end class Task < ActiveRecord::Base belongs_to :project end class ProjectsController < ApplicationController def show @project = Project.find(params[:id]) @tasks = Task.find_all_by_project_id_and_complete(@project.id, false) end end 上面的Project类和Task类是一对多的关系,ProjctsController的show方法根据:id参数查询得到@project对象 这时,我们可以使用find_all_by_project_id_and_complete动态方法来查询belongs_to @project对象的@tasks 但是有更简洁的方法: class ProjectsController < ApplicationController def show @project = Project.find(params[:id]) @tasks = @project.tasks.find_all_by_complete(false) end end 为什么可以在@project.tasks上调用find_all_by_complete这种ActiveRecord Model才有的动态查询方法呢? 让我们来回顾一下ActiveRecord的关联: Rails源码研究之ActiveRecord:二,Associations 我们看到,associations.rb定义了has_many、belongs_to等这些用来表示Model之间关联的方法 -- 也构成了Rails的数据库关联的DSL 然后,利用反射得到has_many、belongs_to等方法后面的参数所代表的Model类 最后,对这些Model类的操作被代理到具体的HasManyAssociation、BelongsToAssociation等类 HasManyAssociation、BelongsToAssociation等类里面定义了find、build等方法 并且,不难发现,HasManyAssociation、BelongsToAssociation等类都继承于AssociationCollection这个父类 看看AssociationCollection类,里面定义了<<、delete_all、sum、delete、create等方法,比如: def create(attrs = {}) record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.create(attr) } @target ||= [] unless loaded? @target << record record end 这里不难发现,create方法实际利用反射上调用了Model(如Task)的create方法,并把创建的对象加入到@target中(如@project.tasks) 好,现在来看AssociationCollection类的一个protected方法: def method_missing(method, *args, &block) if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) super else @reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.send(method, *args, &block) } end end 现在明白为什么在@project.tasks上可以调用create、<<、build、find、find_all_by_complete等方法了吧! @target是我们的@project.tasks,它是一个belongs_to @project的Task对象Array 而@reflection.klass为Task 当我们调用@project.tasks.find_all_by_complete时: 如果@project.tasks定义了find_all_by_complete方法,或者Task类没有定义find_all_by_complete方法并且 Class(这里的Class是指Associations模块下的Class)定义了find_all_by_complete方法,则调用父类的同名方法; 否则,调用Task类的同名方法。 这里find_all_by_complete显然符合后面的情况,即Task类定义了该方法(见Rails宝典之第二式: 动态find_by方法) 回到这个例子,除了在@project.tasks基础上调用基本的ActiveRecord Model层面的方法外,我们还可以通过指定find_sql参数来约束 想要得到的结果集: has_many :tasks, :finder_sql => %q/ select * from tasks where project_id = #{id} and complete == 0 / 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |