215: Rails 3 中的高级查询

在这一集我们将接着第202集的视频,展示Rails 3中将要使用的高级查询,第202集Railscast【视频,文本】视频中我们展示了Rails 3中使用的查询。
[size=x-large]用类方法代替Scopes[/size]
在我们要来展示的应用程序中包括两个模型:Product 和 Category,其中,Product属于Category,并且在product模型中有一个named scope: discontinued, 用来表示已经停产和价钱低于一个给定的值的产品。
#/app/models/product.rb
class Product < ActiveRecord::Base
belongs_to :category
scope :discontinued, where(:discontinued => true)
scope :cheaper_than, lambda { |price| where("price < ?", price) }
end


其中,在第二个named scope的描述中我们用到了lambda,如果你使用过named scope特别是有需要大量传入参数或者是scope本身逻辑复杂的情况下,都会考虑要把scope抽出来写到方法里,虽然,我们当前的例子并没有什么复杂的,为了演示我们同样抽成方法如下:
#/app/models/product.rb
class Product < ActiveRecord::Base
belongs_to :category
scope :discontinued, where(:discontinued => true)

def self.cheaper_than(price)
where("price < ?", price)
end
end


如上抽出方法的scope和原来的scope有同样的功能,虽然在Rails 2中同样支持这样的操作,然而,在rails 3中这样的方法抽象会有更完善的功能。假设我们有另外一个scope Cheap表示价钱低于5的产品,我们就可以重用之前为了使用scope而创建的方法,构造如下:
#/app/models/products.rb
scope :cheap, cheaper_than(5)


然而,这里有一个潜在的陷阱,就是我们的scope方法的定义要放到其他的类定义方法的后面,也就是说带方法的scope会在普通scope的位置靠后。
在rails 控制台我们可以看到scope对应的SQL如下:
ruby-1.8.7-p249 > Product.cheap.to_sql
#=> "SELECT \"products\".* FROM \"products\" WHERE (price < 5)"


直接调用定义的scope会显示符合scope条件的产品。
>Product.cheap
#=> [#<Product id: 1, name: "Top", price: 4.99, discontinued: nil, category_id: 3, created_at: "2010-05-24 21:01:59", updated_at: "2010-05-24 21:01:59">, #<Product id: 2, name: "Milk", price: 2.99, discontinued: nil, category_id: 2, created_at: "2010-05-24 21:02:38", updated_at: "2010-05-24 21:02:38">]


[size=x-large]关联[/size]

在Rails控制台下我们可以展示另外一个通过关联使用scope的技巧。我们之前提到Product和category有belongs_to的关联,所以,也就是说我们可以使用joins来返回SQL的join表的查询
ruby-1.8.7-p249 > Category.joins(:products).to_sql
#=> "SELECT \"categories\".* FROM \"categories\" INNER JOIN \"products\" ON \"products\".\"category_id\" = \"categories\".\"id\""


下面是关于符合特定scope的查询的写法,比如我们希望查询至少有一个产品的价值是低于5的所有种类。那么我们可以有下面的两种写法,一种是用merge如下:
> Category.joins(:products).merge(Product.cheap)
#=> [#<Category id: 3, name: "Games", created_at: "2010-05-24 21:00:57", updated_at: "2010-05-25 18:30:18">, #<Category id: 2, name: "Groceries", created_at: "2010-05-24 21:00:50", updated_at: "2010-05-25 18:30:39">]


同样的我们也可以使用和merge有相同含义的&来表达如下:
> Category.joins(:products) & Product.cheap
#=> [#<Category id: 3, name: "Games", created_at: "2010-05-24 21:00:57", updated_at: "2010-05-25 18:30:18">, #<Category id: 2, name: "Groceries", created_at: "2010-05-24 21:00:50", updated_at: "2010-05-25 18:30:39">]


使用这样的方法我们可以关联一些不在自己model的检索条件,比如我们希望查询所有产品的价值都小于5的类别如下,并且我们可以使用to_mysql方法查看对应的mysql的语句。
> (Category.joins(:products) & Product.cheap).to_sql
#=> "SELECT \"categories\".* FROM \"categories\" INNER JOIN \"products\" ON \"products\".\"category_id\" = \"categories\".\"id\" WHERE (price < 5)"


更为强大的是,我们可以在named scope定义中使用是定义好的scope就如同我们刚刚用到的。比如,创建一个包括所有cheap 产品的类别,如下:
#/app/models/category.rb
class Category < ActiveRecord::Base
has_many :products
scope :with_cheap_products, joins(:products) & Product.cheap
end


这儿named scope会返回和我们之前的查询相同的数据:
> Category.with_cheap_products
# => [#<Category id: 3, name: "Games", created_at: "2010-05-24 21:00:57", updated_at: "2010-05-25 18:30:18">, #<Category id: 2, name: "Groceries", created_at: "2010-05-24 21:00:50", updated_at: "2010-05-25 18:30:39">]


有一个值得注意的问题是关于多表关联查询中的表名问题,这一点可以通过查看上面我们建立的scope的sql看到:实际上在where查询中我们并没有写明特定的表名.
> Category.with_cheap_products.to_sql
# => "SELECT \"categories\".* FROM \"categories\" INNER JOIN \"products\" ON \"products\".\"category_id\" = \"categories\".\"id\" WHERE (price < 5)"


当两个关联的表都有相关的字段的时候,这就会是问题。所以,我们应该在Product 模型从新定义并且指定表名,这样就不怎么出错了。
/app/models/product.rb
def self.cheaper_than(price)
where("products.price < ?", price)
end


也就是说,在我们自己写SQL条件的时候,为了防止不同的表之间有重复字段的问题就应该在关联查询中写明表名。当然如果是按照Rails的哈希条件写,比如在scope里我们定义查询,那么就不要我们自己指明属于哪个表,Rails会自动加上。

[size=x-large]通过Named Scopes创建记录[/size]

我们可以通过named scopes创建新的记录,例如在Product模型上有一个叫discontinued的named scope
> Product.discontinued
# => [#<Product id: 3, name: "Some DVD", price: 13.49, discontinued: true, category_id: 1, created_at: "2010-05-25 19:45:05", updated_at: "2010-05-25 19:45:05">]


因为named scope是用的Hash所以,我们可以通过调用Build方法创建discontinued属性为真的记录。
> p = Product.discontinued.build
# => #<Product id: nil, name: nil, price: nil, discontinued: true, category_id: nil, created_at: nil, updated_at: nil>


这种创建的方式类似于通过Rails的关联方式的创建(译者晓夜注:例如has_many的关联)这样默认的情况下就会有一些限定通过外键设置好。我们通过scope创建记录是用过where语句进行的限定。
[size=x-large]Arel[/size]

最后我们以Arel对的介绍结束本文。Arel是一种简化的检索方式,在Rails 3中使用很简单,你只需要取一个model的arel_table属性。如下:
> t = Product.arel_table


变量t代表product表,我们可以通过如下方式访问字段属性:
>t[:price]
# => <Attribute price>


通过对属性调用方法等于执行条件查询,例如,我们要找到价值为2.99美元的所有商品:
> t[:price].eq(2.99)
=> #<Arel::Predicates::Equality:0x1040dd9f0 @operand1=<Attribute price>, @operand2=2.99>


这将返回一个predicate对象代表着查询条件。还有一些其他条件我们可以用,比如matches代表Like查询。我们也可以通过to_sql来查看这个查询对应的sql语句
> t[:name].matches('%lore').to_sql
# => "\"products\".\"name\" LIKE '%lore'"


同时我们可以使用or方法将两个predicate对象连在一起进行组合查询,比如查询价值2.99美元或者产品名是lore的产品:
t[:price].eq(2.99).or(t[:name].matches('%lore'))


这将生成如下Sql语句,我们可以通过to_sql看到:
> t[:price].eq(2.99).or(t[:name].matches('%lore')).to_sql
# => "(\"products\".\"price\" = 2.99 OR \"products\".\"name\" LIKE '%lore')"


因为predicate对象对应一个查询条件,那么我们就可以把predicate作为一个参数传给ActiveRecord的where方法。例如把上面的predicate传给Product.where,就会返回价值是2.99美元或者名字以lore结尾的记录集。
>   Product.where(t[:price].eq(2.99).or(t[:name].matches('%lore')))
# => [#<Product id: 2, name: "Milk", price: 2.99, discontinued: nil, category_id: 2, created_at: "2010-05-24 21:02:38", updated_at: "2010-05-24 21:02:38">, #<Product id: 4, name: "Knight Lore", price: 2.99, discontinued: nil, category_id: nil, created_at: "2010-05-26 19:36:02", updated_at: "2010-05-26 19:36:02">]


这里我们只是介绍了Arel最基本的知识。实际上,Arel还可以做更多的事。现在也有很多基于Arel的方便的插件,例如MetaWhere,就可以提供如下方式查询:
Product.where(:price.eq => 2.99, :name.matches => '%lore')  


如果需要更采用灵活的方式进行更加复杂的查询,你可以再进一步了解Arel和MetaWhere相关的知识
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值