Rails宝典之第三式: 通过关联做查询

Rails宝典之第三式: 通过关联做查询 

我们来看一个has_many关联的例子: 
Java代码   收藏代码
  1. class Project < ActiveRecord::Base  
  2.   has_many :tasks  
  3. end  
  4.   
  5. class Task < ActiveRecord::Base  
  6.   belongs_to :project  
  7. end  
  8.   
  9. class ProjectsController < ApplicationController  
  10.   def show  
  11.     @project = Project.find(params[:id])  
  12.     @tasks = Task.find_all_by_project_id_and_complete(@project.id, false)  
  13.   end  
  14. end  

上面的Project类和Task类是一对多的关系,ProjctsController的show方法根据:id参数查询得到@project对象 
这时,我们可以使用find_all_by_project_id_and_complete动态方法来查询belongs_to @project对象的@tasks 
但是有更简洁的方法: 
Java代码   收藏代码
  1. class ProjectsController < ApplicationController  
  2.   def show  
  3.     @project = Project.find(params[:id])  
  4.     @tasks = @project.tasks.find_all_by_complete(false)  
  5.   end  
  6. 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等方法,比如: 
Java代码   收藏代码
  1. def create(attrs = {})  
  2.   record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) { @reflection.klass.create(attr) }  
  3.   @target ||= [] unless loaded?  
  4.   @target << record  
  5.   record  
  6. end  

这里不难发现,create方法实际利用反射上调用了Model(如Task)的create方法,并把创建的对象加入到@target中(如@project.tasks) 

好,现在来看AssociationCollection类的一个protected方法: 
Java代码   收藏代码
  1. def method_missing(method, *args, &block)  
  2.   if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))  
  3.     super  
  4.   else  
  5.     @reflection.klass.send(:with_scope, construct_scope) { @reflection.klass.send(method, *args, &block) }  
  6.   end  
  7. 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参数来约束 
想要得到的结果集: 
Java代码   收藏代码
  1. has_many :tasks, :finder_sql => %q/  
  2. select * from tasks where project_id = #{id} and complete == 0  
  3. /  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值