`

215: Rails 3 中的高级查询

阅读更多

在这一集我们将接着第202集的视频,展示Rails 3中将要使用的高级查询,第202集Railscast【视频,文本】视频中我们展示了Rails 3中使用的查询。
用类方法代替Scopes
在我们要来展示的应用程序中包括两个模型: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">]


关联

在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会自动加上。

通过Named Scopes创建记录

我们可以通过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语句进行的限定。
Arel

最后我们以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相关的知识

分享到:
评论

相关推荐

    rails查询学习笔记

    描述中虽然没有具体信息,但我们可以推测这可能是一篇关于Rails中查询技巧和最佳实践的学习笔记,可能包括如何使用ActiveRecord查询接口进行复杂的数据库操作,如选择、过滤、排序、分组等。 标签 "源码" 暗示了这...

    Beginning Rails 3

    - **内容概述**:本书全面介绍了 Rails 3 的基础知识和高级主题,覆盖了从安装配置到部署上线的整个开发流程。 通过上述知识点的介绍,《Beginning Rails 3》不仅是一本针对初学者的指南,也为有经验的开发者提供了...

    Flexible Rails: Flex3 on Rails2

    ### Flexible Rails: Flex3 on Rails2 #### 关于Flexible Rails 本书《Flexible Rails: Flex 3 on Rails 2》由Peter Armstrong撰写,旨在探讨如何结合使用Flex 3和Rails 2来开发高效的富互联网应用程序(Rich ...

    Ruby on Rails Guides v2 - Ruby on Rails 4.2.5

    - **学习路径**:继续深入学习Rails的高级特性,如Active Record模式、表单构建器等。 - **社区资源**:加入Rails官方论坛、Stack Overflow等社区获取帮助和支持。 #### 十一、文档编写技巧 - **重要性**:良好的...

    Apress.Beginning.Rails.3

    ### 关于《Beginning Rails 3》的关键知识点 #### 书籍基本信息 - **书名**:《Beginning Rails 3》 - **作者**:Cloves Carneiro Jr. 和 Rida Al Barazi - **出版社**:Apress - **出版日期**:2010年 - **ISBN-13...

    make_taggable:Rails的高级标记

    bundle add make_taggable安装后安装迁移rails make_taggable_engine:install:migrations 查看生成的迁移,然后迁移: rails db:migrate对于MySql用户为了使MySQL可以很好地使用特殊字符,可以在初始化文件中设置...

    Beginning.Rails.3

    - 对Rails 3中的高级特性和最佳实践有所了解。 综上所述,《Beginning Rails 3》是一本非常适合Ruby on Rails初学者的入门书籍,不仅覆盖了Rails 3的基础知识,还包含了大量实用案例和技巧,能够帮助读者快速成长为...

    ruby on rails(开发文档)

    Ruby on Rails,简称Rails,是一...Rails的开发文档详尽且全面,涵盖了从基础概念到高级特性的所有内容,对于开发者来说是一份宝贵的参考资料。通过深入学习和实践,你可以掌握构建高效、可维护的Web应用的技术和技巧。

    Advanced Rails

    3. **ActiveRecord高级用法**:ActiveRecord是Rails的ORM(对象关系映射)工具,用于数据库操作。书中会涵盖关联(如has_many, belongs_to, has_one, through)、查询API(如scopes, joins, group, having)以及事务...

    Rails 4 in Action, Second Edition.pdf

    - **Active Record Query Interface**:Rails 4在Active Record中引入了一个新的查询接口,使数据库操作更加直观和高效。 - **Secure Cookies**:为了提高安全性,Rails 4引入了一种新的安全cookie机制,可以更好地...

    应用Rails进行敏捷Web开发中文第三版

    在Rails 2.2.2中,ActiveRecord提供了强大的查询API,允许开发者用简洁的代码执行复杂的数据库操作。此外,ActionController处理HTTP请求,并将数据传递给视图进行渲染,而ActionView则提供了模板系统,用于创建动态...

    Ruby on Rails: Up and Running

    3. **MVC架构**:MVC模式是Rails组织代码的基础。模型(Model)处理数据和业务逻辑,视图(View)负责展示数据,而控制器(Controller)协调模型和视图之间的交互。理解这三者的作用和交互方式对于构建可维护的应用...

    The Rails 3 Way, 2nd Edition

    1. **性能优化**:Rails 3在性能方面进行了大量改进,比如内存管理、数据库查询效率等。 2. **模块化**:Rails 3将核心框架进一步模块化,使得开发者可以根据项目需求选择性地加载特定组件。 3. **RESTful设计**:...

    Rails.Recipes(Rails.3.Edition,2012) 英文版PDF

    - **书籍定位**: 《Rails Recipes: Rails 3 Edition》是一本面向初级到中级Ruby on Rails开发者的指南。它包含了70个最常见的编程难题解决方案,旨在帮助开发者解决实际工作中可能遇到的问题。 - **内容更新**: 本书...

Global site tag (gtag.js) - Google Analytics