Rails3的ActiveRecord 查询API
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
相关最佳实践
- rails3项目最新发布脚本 by jerry
- ActionController:: InvalidAuthenticityToken with Destroy action by kart
- 解决Mysql::Error: query: not connected by hywhy
- rails3中永久性授权cookies by jerry
- 统计一周的信息 by 51515
- rails3常用的插件推荐 by jerry
- rails3.0新的验证方法 by aoner
- actionmailer 3.0 结合ar_mailer_rails3 异步发送邮件 by jerry
- database.yml的简便写法 by jerry
- 用cancan来进行权限管理 by jerry
- 用RVM在ubuntu10.04搭建ruby 1.9.2和rails3的开发环境 by jerry