Ruby on rails开发从头来(五十六)- ActiveRecord基础(一对多关联关系)


一对多关联可以使我们表示一组对象,例如,一个order可以包含有任意多个line item,在数据库中,所有的line item记录都通过外键关联到特定的order。

在Active Record中,通过在父对象中的has_many来定义到子对象的关联,在子对象中使用belongs_to来指定父对象。我们已经在上一篇中了解了belongs_to声明,实际上,在一对多的情况下,和一对一是相同的,所以我们来了解has_many声明。


has_many声明

has_many声明了一个属性,其行为就像一组子对象,可以把它看作数组来访问,查询特定的对象,或者添加新对象。例如:

order = Order.new

params[:products_to_buy].each do |prd_id, qty|

product = Product.find(prd_id)

order.line_items << LineItem.new(:product => product,

:quantity => qty)

end

追加操作符(>>)所作的不仅仅是向order的line_items列表中追加一个对象,并且和设置了line_item对象外键值为order对象主键值,而且在order对象保存的时候,会同时保存line_item对象。

我们可以像对数组一样对has_many关联进行循环:

order = Order.find(123)

total = 0.0

order.line_items.each do |li|

total += li.quantity * li.unit_price

end

和has_one一样,我们可以改变Active Record的默认形式,我们可以给has_many一组设定,:class_name,:foreign_key,:conditions,:order和:dependent和has_one中的是一样的,has_many还有:exclusively_dependent,:finder_sql,:counter_sql。

has_one和has_many都支持:dependent,这告诉Rails在删除主表中记录的同时删除从表中对应的记录。也就是对所有外键为删除的主记录的id的子对象调用它们destory方法。

无论如何,如果从表仅仅和主表关联,而没有其他表关联,而且没有任何钩子方法在删除的时候执行操作,你可以使用:exclusively_dependent来代替:dependent,在这种情况下,对于所有的子记录会使用一条sql语句来删除,这样执行速度会快一点。

你还可以通过使用:finder_sql和:counter_sql设定来复写Active Record用来对子记录进行查询和计数的sql。在:conditions设定不足够的情况下,这两个设定就很有用了。例如:

class Order < ActiveRecord::Base

has_many :rails_line_items,

:class_name => "LineItem",

:finder_sql => "select l.* from line_items l, products p " +

" where l.product_id = p.id " +

" and p.title like '%rails%'"

end

:counter_sql设定用来复写Active Record用作计算行数的sql,如果:finder_sql被指定了,但是:counter_sql没有指定,Active Record将会根据查询sql来替换计算行数的sql。

:order设定指定了从数据库中选出的记录的排序所使用的sql,如果你在遍历记录集的时候需要有特定的排序,你就需要指定:order,也就是设定sql语句的order by部分,默认情况下,所有的字段都是升序。例如:

class Order < ActiveRecord::Base

has_many :line_items,

:order => "quantity, unit_price DESC"

end

现在再回到has_one声明,我们前面提到过也支持:order,但是一条父记录最多只有一条子记录,那么为什么要支持:order来允许指定排序条件呢?想象一下这样的场景,一个用户可能有多条订单,但是,如果我们想查看该用户的最新一条订单呢?这时候,我们就可以使用has_one声明了:

class Customer < ActiveRecord::Base

has_many :orders

has_one :most_recent_order,

:class_name => 'Order',

:order => 'created_at DESC'

end

上面的代码创建了一个新属性:most_recent_order,将会引用到该用户的最新一条订单,我们可以这样使用它:

cust = Customer.find_by_name("Dave Thomas")

puts "Dave last ordered on #{cust.most_recent_order.created_at}"

实际上,Active Record执行了下面的sql:

SELECT * FROM orders

WHERE customer_id = ?

ORDER BY created_at DESC

LIMIT 1

根据前面我们学习的find方法,可以看到只取了排序后的第一条记录。


has_many添加的方法

就像belongs_to和has_one,has_many也添加了一组属性相关的方法在它所在的类中,下面我们来看看这些方法,我们的声明是这样

class Customer < ActiveRecord::Base

has_many :orders

end

l         orders(force_reload=false)=

返回一个和用户关联的订单的数组,结果集是被缓存的,对于相同的查询,不会再次从数据库提取数据,除非force_reload = true。

l         orders <<order

添加订单到指定用户的订单的列表中。

l         orders.push(order1, ...)

添加一个或多个订单对象到用户的订单列表中,concat()是该方法的别名。

l         orders.delete(order1, ...)

从用户的订单列表中删除一个或多个订单,但是不会删除数据库中对应的记录,只是将它们的customer_id外键设置为null,断开和用户的关联。

l         orders.clear

分离用户和订单,就像delete(),但是如果订单被指明为:dependent,数据库中对应的记录就会被删除。

l         orders.find(options...)

发出一个find()调用,但是仅返回用户关联的订单。

l         orders.build(attributes={})

构造一个新的order对象,用给定的属性初始化,并且关联到customer,该对象没有保存。

l         orders.create(attributes={})

构造并且保存一个新的order对象,用给定的属性初始化,并且关联到customer。


上一篇 下一篇

评论



分享

最新加入

最新评论

隋中缘: @嘉嘉tiffany 性感乳神。 查看原文 06月13日 16:16