activerecord查询语法for rails3

 

Rails3的ActiveRecord 查询API

jerry 分享于 about 1 month 前,  3 条回复208 次浏览

Rails3ActiveRecord中修改了较多的代码,很多以前Rails2中普遍使用的用法在Rails3中已经不推荐使用或者已经被移除, 所以此文让大家知道如何去写好Rails3的代码。 本文是翻译自Pratik Naik博客的一篇文章。

众所周知,很多rails2的active record查询方法在rails3众已经不推荐使用,也就是说,过时了。那么,哪些写法在rails3.1众已经不推荐使用了呢?

这些不推荐使用的写法在rails3.1 release版中依然有效,但是将会在rails3.2中全面的移除,考虑到牵涉了比较多的代码修改,所以将会有一个官方的插件来支持他们。

简而言之,通过包含hash的任何ActiveRecord的类方法,都已经不推荐了,比如:

:conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :having, :from, :lock  

目前ActiveRecord提供了以下finder方法:

  • find(id_or_array_of_ids, options)
  • find(:first, options)
  • find(:all, options)
  • first(options)
  • all(options)
  • update_all(updates, conditions, options)

还有以下的计算方法:

  • count(column, options)
  • average(column, options)
  • minimum(column, options)
  • maximum(column, options)
  • sum(column, options)
  • calculate(operation, column, options)

进入rails3的世界,以上的方法都是不推荐使用的。这些方法将会在rails3.2中完整的移除。此外,find(:first) , find(:all) 也已经不推荐使用了,小小的例外是 count()方法依然接受 :distinct 参数。

下面是一些在 rails3中过时用法的例子:

User.find(:all, :limit => 1)
User.find(:all)
User.find(:first)
User.first(:conditions => {:name => 'lifo'})
User.all(:joins => :items)  

但是以下的用法还是推荐使用的:

User.find(1)
User.find(1,2,3)
User.find_by_name('lifo')  

此外,在rails2中使用率相当大的named_scope 也已经不推荐了

named_scope :red, :conditions => { :colour => 'red' }
named_scope :red, lambda {|colour| {:conditions => { :colour => colour }} }  

with_scope, with_exclusive_scope 和 default_scope 也不推荐使用。

with_scope(:find => {:conditions => {:name => 'lifo'}) { ... }
with_exclusive_scope(:find => {:limit =>1}) { ... }
default_scope :order => "id DESC"  

动态 scoped_by_ 也将会废弃。

red_items = Item.scoped_by_colour('red')
red_old_items = Item.scoped_by_colour_and_age('red', 2)

新的API

Rails3的 ActiveRecord 有以下的finder方法:

  • where (:conditions)
  • having (:conditions)
  • select
  • group
  • order
  • limit
  • offset
  • joins
  • includes (:include)
  • lock
  • readonly
  • from

Chainability

上述的方法都会返回一个关系,从概念上讲,这种关系其实都是一个 named scope类型。所有的这些方法都很好的在关系对象中定义了。

lifo = User.where(:name => 'lifo')
new_users = User.order('users.id DESC').limit(20).includes(:items)  

你可以在现有的关系中应用更多的finder方法

cars = Car.where(:colour => 'black')
rich_ppls_cars = cars.order('cars.price DESC').limit(10)

Quacks like a Model

Quacks relation 在涉及到主要的CRUD方法的时候其实就像一个模型,你可以调用以下任何方法。

  • new(attributes)
  • create(attributes)
  • create!(attributes)
  • find(id_or_array)
  • destroy(id_or_array)
  • destroy_all
  • delete(id_or_array)
  • delete_all
  • update(ids, updates)
  • update_all(updates)
  • exists?

那么,下面的代码是我们所希望看到的:

red_items = Item.where(:colour => 'red')
red_items.find(1)
item = red_items.new
item.colour #=> 'red'

red_items.exists? #=> true
red_items.update_all :colour => 'black'
red_items.exists? #=> false  

请注意,调用任何的CRUD方法将会重置关系,也就是说,将会删除记录的缓存,例如 relation.size。

Lazy Loading 延迟加载

我们可能会从上面的例子清楚的知道,关系是延迟加载的,也就是调用了一个枚举的方法,这和 associations 和 named_scopes 是如何协作的比较相似。

cars = Car.where(:colour => 'black') # No Query
cars.each {|c| puts c.name } # Fires "select * from cars where ..."  

这是非常有用的片段缓存,在你的controller,可以这么写

def index
  @recent_items = Item.limit(10).order('created_at DESC')
end  

然后在你的view中这么写

<% cache('recent_items') do %>
  <% @recent_items.each do |item| %>
    ...
  <% end %>
<% end %>

在上述例子中,@recent_items 在视图中调用了@recent_items.each后才被加载,作为控制器实际上并没有触发任何查询,在不需要任何特殊工作的时候,片段缓存变得非常有效。

Force loading – all, first & last

有时候,你可能不需要延迟加载,那么,你可以强制进行加载。

cars = Car.where(:colour => 'black').all  

需要注意的是.all后返回的是一个数组,而不是一个关系。这和rails 2.3下 named_scopes and associations是相类似的。

同样,first 和last 总是会返回一个 ActiveRecord对象或者nil.

cars = Car.order('created_at ASC')
oldest_car = cars.first
newest_car = cars.last  

named_scope -> scopes

named_scope在Rails3.0中已经不推荐使用了,你只要将 “named_”这部分去掉就可以了。也就是说 named_scope已经被重新命名成scope了。

老的写法是:

class Item
  named_scope :red, :conditions => { :colour => 'red' }
  named_scope :since, lambda {|time| {:conditions => ["created_at > ?", time] }}
end  

新的写法是:

class Item
  scope :red, :conditions => { :colour => 'red' }
  scope :since, lambda {|time| {:conditions => ["created_at > ?", time] }}
end

然而,以上使用的conditions的hash将会在Rails3.1中移除,所以我们需要这么写:

class Item
  scope :red, where(:colour => 'red')
  scope :since, lambda {|time| where("created_at > ?", time) }
end  

在内部,命名规范是建立在关系之上的,使它更简单的使用和混用finder方法。

red_items = Item.red
available_red_items = red_items.where("quantity > ?", 0)
old_red_items = Item.red.since(10.days.ago)  

Model.scoped

如果你想建立一个复杂的关系查询,从一个空白的关系出发,那么Model.scoped将会是你用到的。

cars = Car.scoped
rich_ppls_cars = cars.order('cars.price DESC').limit(10)
white_cars = cars.where(:colour => 'red')  

说到内部,ActiveRecord::Base有以下delegations:

delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped  

以上的可能会给你一个更好的方向去理解ActiveRecord::Base类内部是如何工作的。包括动态的finder方法,比如 find_by_name. find_all_by_name_and_colour 等。

with_scope and with_exclusive_scope

with_scope和with_exclusive_scope 现在已被较好的实施在了关系之上:

with_scope(where(:name => 'lifo')) do
  ...
end  

或者使用一个 named scope:

with_exclusive_scope(Item.red) do
  ...
end  

本文是翻译自Pratik Naik 博客的一篇文章。Active Record Query Interface 3.0

回复 (3 条)

添加回复

  1. 如果你还未注册, 请填写你的名字。

  2. 你可以实用以下html标签:

    <a href="" title=""> <b> <blockquote> <pre> <code> <em> <i> <strong>

    代码块请使用 <pre><code>...</code></pre>.

你可能感兴趣的:(ActiveRecord)