zoukankan      html  css  js  c++  java
  • Rails 3中的Active Record的查询变化



    基本的演示例子 

    首先,我们使用一个基本的例子对照演示。旧的ActiveRecord如何调用和转换成新的Rails 3下的ActiveRecord如何实现查询调用。假定我们有两个models,Article和Comment,他们之间有has_many:comments的关系。那么,当我们需要查询最近十条记录时会执行如下语句: 

    Ruby代码  收藏代码
    1. Article.find(:all:order => "published_at desc":limit => 10)    



    当我们把上面的语句转换能Rails 3时,最基本的原则是把查询中的哈希参数替换成对应的方法,以上语句转换如下:

    Ruby代码  收藏代码
    1. Article.order("published_at desc").limit(10)    



    显然新的语法更简洁,可是,往往也不是所有的哈希参数都可以一点不变的转换成对应的方法,例如: 

    Ruby代码  收藏代码
    1. Article.find(:all:conditions => ["published_at <= ?"Time.now], :include => :comments)    



    事实上,这条基本转换的规则(find的hash参数直接转换成同名的方法)只有两个例外,幸运的是上面的例子都包含了他们。上条语句是想要查询所有发布时间早于当前时间的文章和评论,在Rails 3里将会转成如下: 

    Ruby代码  收藏代码
    1. Article.where("published_at <= ?"Time.now).includes(:comments)    



    例外就是conditions变成了where,而include变成了复数形式includes。除此之外,所有其他在find中调用的方法,都可以完全和哈希参数同名。 
    本节最后,让我们看这样一个小例子,查询最近发表的一篇文章: 

    Ruby代码  收藏代码
    1. Article.find(:first:order => "published_at desc")    



    在Rails 3中我们可以转换如下: 

    Ruby代码  收藏代码
    1. Article.order("published_at desc").first()    



    值得说明的是,我们要把first放到最后,而且,鉴于我们是降序查询,那么,写成如下也可以: 

    Ruby代码  收藏代码
    1. Article.order("published_at").last()    



    这样写的执行效率和上面是一样的,不过,看起来更简洁。 
    在Rails 3.0里我们可以同时使用上面的两种查询语法。从3.1开始老的查询语法开始作废,到3.2老的查询语法将被完全移除。所以,为了将来考虑早点把老的语法替换掉是值得的。 
    或者,大家会怀疑为什么要破坏向下兼容的进行这样的语法兼容性修改。进行这样的语法升级的目的在于下面我们将要解释的Lazy Loading。 
    Lazy Loading 

    为了演示Lazy loading我们将使用Rails的控制台,查询所有的文章得到数组对象,如下: 

    Ruby代码  收藏代码
    1. ruby-1.9.1-p378 > Article.all => [#<Article id: 1, name: "It's Ancient", published_at: nil, hidden: false,   
    2. created_at: "2010-02-22 20:35:42", updated_at: "2010-02-22 20:35:42">,   
    3. #<Article id: 2, name: "Can't See Me", published_at: nil, hidden: false,   
    4. created_at: "2010-02-22 20:37:03", updated_at: "2010-02-22 20:37:03">,   
    5. #<Article id: 3, name: "To the Future!", published_at: nil, hidden: false,   
    6. created_at: "2010-02-22 20:38:17", updated_at: "2010-02-22 20:38:17">]  



    如果,我们希望把得到的文章按照文章名字进行排序,可以使用如下方法: 

    Ruby代码  收藏代码
    1. ruby-1.9.1-p378 > articles = Article.order("name")  
    2.  => #<ActiveRecord::Relation:0x00000101669b90 @table=#<Arel::Table:0x000001023e9af8   
    3. @name="articles"@options={:engine=>#<Arel::Sql::Engine:0x000001023a15c8 @ar=ActiveRecord::Base,  
    4. @adapter_name="SQLite">}, @engine=#<Arel::Sql::Engine:0x000001023a15c8 @ar=ActiveRecord::Base,  
    5. @adapter_name="SQLite">, …  



    这时我们会发现查询返回值不是Article的数组对象,而是一个ActiveRecord::Relation的对象。这个对象保存了我们希望进行的查询条件信息,但是数据库并没有实际执行这个查询操作。这就是我们在本篇里所说的Lazy Loading,即除非真正使用这些数据,否则实际的查询就不进行。例如,我们进行迭代或者是调用显示第一条记录,那么,这个数据库的查询就会真正执行,如下: 

    Ruby代码  收藏代码
    1. ruby-1.9.1-p378 > articles.first  
    2.  => #<Article id: 2, name: "Can't See Me", published_at: nil, hidden: false,   
    3. created_at: "2010-02-22 20:37:03", updated_at: "2010-02-22 20:37:03">  



    那么,让我们看看Lazy Loading对于我们的应用有什么作用。如果我们用scaffold来生产Model那么我们应该有会得到一个对应的articles的控制器,并且包含最基本的7个方法。其中,也包括index的方法是用Article.all来立即得到数据: 

    Ruby代码  收藏代码
    1. #/app/controllers/articles_controller.rb  
    2.     def index    
    3.       @articles = Article.all    
    4.         
    5.       respond_to do |format|    
    6.         format.html # index.html.erb    
    7.         format.xml  { render :xml => @articles }    
    8.       end    
    9.     end    



    然而,如果这时我们使用的是Rails 3的语法并且对article使用了任何一种find的参数,例如按照文章名排序。这时Article.order(“name”)就会返回ActiveRecord::Relation的对象并且,实际的数据库查询不会在controller调用的时候执行。而是,在view页面进行迭代输出的时候执行。(译者晓夜注:这就是Lazy Loading的标准含义,减少触发时的等待时间,也就是降低返回空白页的等待可能。当然,下文还提到了Lazy Loading别的好处。) 

    Ruby代码  收藏代码
    1. #/app/views/articles/index.html.erb  
    2.     <% @articles.each do |article| %>    
    3.       <tr>    
    4.         <td><%= article.name %></td>    
    5.         <td><%= article.published_at %></td>    
    6.         <td><%= article.hidden %></td>    
    7.         <td><%= link_to 'Show', article %></td>    
    8.         <td><%= link_to 'Edit', edit_article_path(article) %></td>    
    9.         <td><%= link_to 'Destroy', article, :confirm => 'Are you sure?':method => :delete %></td>    
    10.       </tr>    
    11.     <% end %>    



    这时我们就会看到按照文章名排序的列表。 

     


    Lazy Loading的好处也在于当view开启了fragment caching的时候,这时,数据库因为缓存,有些时候查询就不用实际执行。新的查询语法更容易构建查询条件,例如,我们希望检索只有设置了隐藏属性(hidden==1)的记录,那么,我们可以如下修改index方法: 

    Ruby代码  收藏代码
    1. #/app/controllers/articles_controller.rb  
    2.     def index    
    3.       @articles = Article.order('name')    
    4.             
    5.       if params[:hidden]    
    6.         @articles = @articles.where(:hidden =>(params[:hidden] == "1"))    
    7.       end    
    8.         
    9.       respond_to do |format|    
    10.         format.html # index.html.erb    
    11.         format.xml  { render :xml => @articles }    
    12.       end    
    13.     end    



    这时,该方法会检查是否有hidden的参数传入,并且根据参数情况显示如下: 

     


    同样的情况,如果参数传入为0那么会列出如下: 

     


    也就是说,利用上面的原理,就可以把复杂的查询条件串联起来,并且,这样的查询条件修改的时候,实际的查询并没有在数据库中操作。 
    Named Scopes
     

    下面我们看看在Rails 3中named scopes的变化。假如,在Article下有两个named scopes,visible用来表示隐藏的文章,published用来表示已经发布的文章。 

    Ruby代码  收藏代码
    1. #/app/models/article.rb  
    2.     class Article < ActiveRecord::Base    
    3.       named_scope :visible:conditions => ["hidden != ?"true]    
    4.       named_scope :published, lambda { {:conditions => ["published_at <= ?"Time.zone.now]} }    
    5.     end    



    上面的语句是Rails 2中定义named scoped的方式,在Rails 3中named scoped的语法稍微有点不同。首先是用scoped代替named scoped,其次,和find的变化相同,不再使用find的哈希参数,而是改用同名的方法,而conditions会变成where如下: 

    Ruby代码  收藏代码
    1. #/app/models/article.rb  
    2.     class Article < ActiveRecord::Base    
    3.       scope :visible, where("hidden != ?"true)    
    4.       scope :published, lambda { where("published_at <= ?"Time.zone.now) }    
    5.     end    

    在上两集的Railscast里我们演示了如何配置Rails 3的配置环境和创建新的Rails 3应用程序。在这一集里我们将开始演示Rails 3的一些新的功能,这里从ActiveRecord的新检索接口开始。Pratik Naik有篇blog文章是关于这个主题的很值得阅读。 


    基本的演示例子 


    首先,我们使用一个基本的例子对照演示。旧的ActiveRecord如何调用和转换成新的Rails 3下的ActiveRecord如何实现查询调用。假定我们有两个models,Article和Comment,他们之间有has_many:comments的关系。那么,当我们需要查询最近十条记录时会执行如下语句: 

    Ruby代码  

    Article.find(:all, :order => "published_at desc", :limit => 10)    



    当我们把上面的语句转换能Rails 3时,最基本的原则是把查询中的哈希参数替换成对应的方法,以上语句转换如下:

    Ruby代码  

    Article.order("published_at desc").limit(10)    



    显然新的语法更简洁,可是,往往也不是所有的哈希参数都可以一点不变的转换成对应的方法,例如: 

    Ruby代码  

    Article.find(:all, :conditions => ["published_at <= ?", Time.now], :include => :comments)    



    事实上,这条基本转换的规则(find的hash参数直接转换成同名的方法)只有两个例外,幸运的是上面的例子都包含了他们。上条语句是想要查询所有发布时间早于当前时间的文章和评论,在Rails 3里将会转成如下: 

    Ruby代码  

    Article.where("published_at <= ?", Time.now).includes(:comments)    



    例外就是conditions变成了where,而include变成了复数形式includes。除此之外,所有其他在find中调用的方法,都可以完全和哈希参数同名。 

    本节最后,让我们看这样一个小例子,查询最近发表的一篇文章: 

    Ruby代码  

    Article.find(:first, :order => "published_at desc")    



    在Rails 3中我们可以转换如下: 

    Ruby代码  

    Article.order("published_at desc").first()    



    值得说明的是,我们要把first放到最后,而且,鉴于我们是降序查询,那么,写成如下也可以: 

    Ruby代码  

    Article.order("published_at").last()    



    这样写的执行效率和上面是一样的,不过,看起来更简洁。 

    在Rails 3.0里我们可以同时使用上面的两种查询语法。从3.1开始老的查询语法开始作废,到3.2老的查询语法将被完全移除。所以,为了将来考虑早点把老的语法替换掉是值得的。 

    或者,大家会怀疑为什么要破坏向下兼容的进行这样的语法兼容性修改。进行这样的语法升级的目的在于下面我们将要解释的Lazy Loading。 

    Lazy Loading 


    为了演示Lazy loading我们将使用Rails的控制台,查询所有的文章得到数组对象,如下: 

    Ruby代码  

    ruby-1.9.1-p378 > Article.all => [#<Article id: 1, name: "It's Ancient", published_at: nil, hidden: false,   

    created_at: "2010-02-22 20:35:42", updated_at: "2010-02-22 20:35:42">,   

    #<Article id: 2, name: "Can't See Me", published_at: nil, hidden: false,   

    created_at: "2010-02-22 20:37:03", updated_at: "2010-02-22 20:37:03">,   

    #<Article id: 3, name: "To the Future!", published_at: nil, hidden: false,   

    created_at: "2010-02-22 20:38:17", updated_at: "2010-02-22 20:38:17">]  



    如果,我们希望把得到的文章按照文章名字进行排序,可以使用如下方法: 

    Ruby代码  

    ruby-1.9.1-p378 > articles = Article.order("name")  

     => #<ActiveRecord::Relation:0x00000101669b90 @table=#<Arel::Table:0x000001023e9af8   

    @name="articles", @options={:engine=>#<Arel::Sql::Engine:0x000001023a15c8 @ar=ActiveRecord::Base,  

    @adapter_name="SQLite">}, @engine=#<Arel::Sql::Engine:0x000001023a15c8 @ar=ActiveRecord::Base,  

    @adapter_name="SQLite">, …  



    这时我们会发现查询返回值不是Article的数组对象,而是一个ActiveRecord::Relation的对象。这个对象保存了我们希望进行的查询条件信息,但是数据库并没有实际执行这个查询操作。这就是我们在本篇里所说的Lazy Loading,即除非真正使用这些数据,否则实际的查询就不进行。例如,我们进行迭代或者是调用显示第一条记录,那么,这个数据库的查询就会真正执行,如下: 

    Ruby代码  

    ruby-1.9.1-p378 > articles.first  

     => #<Article id: 2, name: "Can't See Me", published_at: nil, hidden: false,   

    created_at: "2010-02-22 20:37:03", updated_at: "2010-02-22 20:37:03">  



    那么,让我们看看Lazy Loading对于我们的应用有什么作用。如果我们用scaffold来生产Model那么我们应该有会得到一个对应的articles的控制器,并且包含最基本的7个方法。其中,也包括index的方法是用Article.all来立即得到数据: 

    Ruby代码  

    #/app/controllers/articles_controller.rb  

        def index    

          @articles = Article.all    

            

          respond_to do |format|    

            format.html # index.html.erb    

            format.xml  { render :xml => @articles }    

          end    

        end    



    然而,如果这时我们使用的是Rails 3的语法并且对article使用了任何一种find的参数,例如按照文章名排序。这时Article.order(“name”)就会返回ActiveRecord::Relation的对象并且,实际的数据库查询不会在controller调用的时候执行。而是,在view页面进行迭代输出的时候执行。(译者晓夜注:这就是Lazy Loading的标准含义,减少触发时的等待时间,也就是降低返回空白页的等待可能。当然,下文还提到了Lazy Loading别的好处。) 

    Ruby代码  

    #/app/views/articles/index.html.erb  

        <% @articles.each do |article| %>    

          <tr>    

            <td><%= article.name %></td>    

            <td><%= article.published_at %></td>    

            <td><%= article.hidden %></td>    

            <td><%= link_to 'Show', article %></td>    

            <td><%= link_to 'Edit', edit_article_path(article) %></td>    

            <td><%= link_to 'Destroy', article, :confirm => 'Are you sure?', :method => :delete %></td>    

          </tr>    

        <% end %>    



    这时我们就会看到按照文章名排序的列表。 


     



    Lazy Loading的好处也在于当view开启了fragment caching的时候,这时,数据库因为缓存,有些时候查询就不用实际执行。新的查询语法更容易构建查询条件,例如,我们希望检索只有设置了隐藏属性(hidden==1)的记录,那么,我们可以如下修改index方法: 

    Ruby代码  

    #/app/controllers/articles_controller.rb  

        def index    

          @articles = Article.order('name')    

                

          if params[:hidden]    

            @articles = @articles.where(:hidden =>(params[:hidden] == "1"))    

          end    

            

          respond_to do |format|    

            format.html # index.html.erb    

            format.xml  { render :xml => @articles }    

          end    

        end    



    这时,该方法会检查是否有hidden的参数传入,并且根据参数情况显示如下: 


     



    同样的情况,如果参数传入为0那么会列出如下: 


     



    也就是说,利用上面的原理,就可以把复杂的查询条件串联起来,并且,这样的查询条件修改的时候,实际的查询并没有在数据库中操作。 

    Named Scopes 


    下面我们看看在Rails 3中named scopes的变化。假如,在Article下有两个named scopes,visible用来表示隐藏的文章,published用来表示已经发布的文章。 

    Ruby代码  

    #/app/models/article.rb  

        class Article < ActiveRecord::Base    

          named_scope :visible, :conditions => ["hidden != ?", true]    

          named_scope :published, lambda { {:conditions => ["published_at <= ?", Time.zone.now]} }    

        end    



    上面的语句是Rails 2中定义named scoped的方式,在Rails 3中named scoped的语法稍微有点不同。首先是用scoped代替named scoped,其次,和find的变化相同,不再使用find的哈希参数,而是改用同名的方法,而conditions会变成where如下: 

    Ruby代码  

    #/app/models/article.rb  

        class Article < ActiveRecord::Base    

          scope :visible, where("hidden != ?", true)    

          scope :published, lambda { where("published_at <= ?", Time.zone.now) }    

        end    



    另外一个关于scope的新功能是可以通过scope创建scope,例如,我们希望创建一个叫做recent的scope,表示发布过并且没有隐藏的文章列表,并且文章列表按照发布的降序排列。那么,我们就可以通过使用visible和pulished来实现如下: 

    Ruby代码  

    scope :recent, visible.published.order("published_at desc")    



    如上所示,我们就是通过将两个scopes连起来,并且添加一个排序的方法来创建scope的。而且,我们也可以通过在控制台调试我们的scope,发现当我们调用Article.recent时,会返回一个ActiveRecord::NamedScope::Scope的对象,而这个对象会和ActiveRecord::Relation有相似的功能,即lazy loading不是当即查询。如下: 

    Ruby代码  

    ruby-1.9.1-p378 > Article.recent  

     => #<ActiveRecord::NamedScope::Scope:0x0000010318bd08 @table=#<Arel::Table:0x00000102740ea8   

     @name="articles", @options={:engine=>#<Arel::Sql::Engine:0x00000102651900 @ar=ActiveRecord::Base>},   

     @engine=#<Arel::Sql::Engine:0x00000102651900 @ar=ActiveRecord::Base>>, …  



    当我们查询所有时,我们将会看到对应的Article对象返回。(译者晓夜注:就是说在用的时候返回) 

    Ruby代码  

    ruby-1.9.1-p378 > Article.recent.all  

     => [#<Article id: 1, name: "It's Ancient", published_at: "2010-01-01",   

     hidden: false, created_at: "2010-02-22 20:35:42", updated_at: "2010-02-22 23:00:16">]  



    有用的工具 


    最后,我们将使用一点有用的工具方法结束本篇的介绍。如果,我们得到了一个类似Relation 或者 Scope的对象(即缓存查询,并没有实际返回数据库查询结果的对象)我们可以通过to_sql来查看对应的查询条件,如下: 

    Ruby代码  

    ruby-1.9.1-p378 > Article.recent.to_sql  

     => "SELECT     \"articles\".* FROM       \"articles\"   

     WHERE     (hidden != 't') AND (published_at <= '2010-02-22 22:47:12.023289')   

     ORDER BY  published_at desc"  



    上面的查询将返回没有隐藏的最近发布的文章。这就是Rails 3中ActiveRecord查询部分的改进。同时Rails 3中对于controller部分也有一些语法变化,使得语法更简练强大。学习使用新的语法,你会发现这是值得的。 


    另外一个关于scope的新功能是可以通过scope创建scope,例如,我们希望创建一个叫做recent的scope,表示发布过并且没有隐藏的文章列表,并且文章列表按照发布的降序排列。那么,我们就可以通过使用visible和pulished来实现如下: 

    Ruby代码  收藏代码
    1. scope :recent, visible.published.order("published_at desc")    



    如上所示,我们就是通过将两个scopes连起来,并且添加一个排序的方法来创建scope的。而且,我们也可以通过在控制台调试我们的scope,发现当我们调用Article.recent时,会返回一个ActiveRecord::NamedScope::Scope的对象,而这个对象会和ActiveRecord::Relation有相似的功能,即lazy loading不是当即查询。如下: 

    Ruby代码  收藏代码
    1. ruby-1.9.1-p378 > Article.recent  
    2.  => #<ActiveRecord::NamedScope::Scope:0x0000010318bd08 @table=#<Arel::Table:0x00000102740ea8   
    3.  @name="articles"@options={:engine=>#<Arel::Sql::Engine:0x00000102651900 @ar=ActiveRecord::Base>},   
    4.  @engine=#<Arel::Sql::Engine:0x00000102651900 @ar=ActiveRecord::Base>>, …  



    当我们查询所有时,我们将会看到对应的Article对象返回。(译者晓夜注:就是说在用的时候返回) 

    Ruby代码  收藏代码
    1. ruby-1.9.1-p378 > Article.recent.all  
    2.  => [#<Article id: 1, name: "It's Ancient", published_at: "2010-01-01",   
    3.  hidden: false, created_at: "2010-02-22 20:35:42", updated_at: "2010-02-22 23:00:16">]  



    有用的工具 

    最后,我们将使用一点有用的工具方法结束本篇的介绍。如果,我们得到了一个类似Relation 或者 Scope的对象(即缓存查询,并没有实际返回数据库查询结果的对象)我们可以通过to_sql来查看对应的查询条件,如下: 

    Ruby代码  收藏代码
    1. ruby-1.9.1-p378 > Article.recent.to_sql  
    2.  => "SELECT     \"articles\".* FROM       \"articles\"   
    3.  WHERE     (hidden != 't') AND (published_at <= '2010-02-22 22:47:12.023289')   
    4.  ORDER BY  published_at desc"  



    上面的查询将返回没有隐藏的最近发布的文章。这就是Rails 3中ActiveRecord查询部分的改进。同时Rails 3中对于controller部分也有一些语法变化,使得语法更简练强大。学习使用新的语法,你会发现这是值得的。 

  • 相关阅读:
    C# 迭代器.NET实现—IEnumerable和IEnumerator
    Excel、CSV文件处理
    C# 读写ini文件
    SCARA——OpenGL入门学习五六(三维变换、动画)
    GDI与OpenGL与DirectX之间的区别
    SCARA——OpenGL入门学习四(颜色)
    SCARA——OpenGL入门学习三
    SQLite数据库表是否存在
    数据库SQL语句单引号、双引号的用法
    C# 委托与事件
  • 原文地址:https://www.cnblogs.com/bendanchenzhicheng/p/2166836.html
Copyright © 2011-2022 走看看