一对多关系

一对多关系允许你描述一个对象集合。例如,一个定单可能有多个关联的商品项目。在
数据库中,对于一特定定单的所有商品项目行包含一个外键列来引用这个定单。
在Active Record中,父对象(逻辑上是包含子对象的一个集合)使用has_many来声明对
子表的关系,子表用belongs_to来表明它的父类。在我们的例子中,
类LineItem belongs_to:order和orders表has_many :line_items。
我们已经在前面说了belongs_to()关系声明。它和在一对一关系的处理上是一样的。只
是has_many声明添加了一些功能给它的model。

[color=red]has_many() 声明[/color]
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列表中去。它还通过设置它们的外键
给这个定单的id 来将商品项目连接回到这个定单中,并且当父类定单被保存时,商品项目
也会自动保存。
我们可以迭代具有has_many关系的子类--属性的行为像个数组。
order = Order.find(123)
total = 0.0
order.line_items.each do |li|
total += li.quantity * li.unit_price
end

和has_one一样,你也可以提供给has_many一组哈希表选项来改变Active Record的默
认行为。选项:class_name,:foreign_key,:conditions,:class_name,

:order和:dependent
工作与在has_one内是一样的。
has_many还有:exclusively_dependent,:finder_sql,和:counter_sql。我们也讨论:order选项,我们只是列出它,但是在has_one中讨论它。

has_one和has_many都支持:dependent选项。当你要销毁父表中的一个记录行
时,:dependent就会告诉Rails销毁子表中的记录行。它通过遍历子表,在带有外键信息的
每一个记录行上调用destroy(),并删除父表中的记录。

然而,如果子表只是被父表用到(也就是说,没有其他的依赖关系),并且它没有完成任
何删除操作这样的钩子方法,你可以使用:exclusively_dependent来代替:dependent选项。
如果这个选项被设置了,子表的记录行就要在一个SQL语句中来执行删除。
最后,你可以覆写Active Record用来获取和计数子
表记录行的SQL,这要通过设置:finder_sql,:counter_sql两个选项来做到。在我们碰到where子句中使用:condition不够用时,这两个选项是用的上了。例如,你可以为一个特定产品创建所有商品项目的一个集合。
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中用来计数的查询语句。如果:finder_sql
被指定而且:counter_sql没有被指定,Activer Record是使用select count(*)来替换
finder SQL语句中的select部分,来合成counter的sql语句。

: order选项是指定一个SQL片断,它将为从数据库中加载的行定义一个次序后加入到
集合中。如果你需要一种特殊顺序来遍历集合,你就要指定: order选项。你给出的SQL片
断是简单的文本,它将出现在一个select语句的order by子句之后。它是由包含了一个或
多个列名字组成的列表。这个集合将按列表中的第一个列排序。如果两个记录行有相同的列
值,决定它们次序的是列表中的第二个列名,以此类推。默认的排序是升序,可以使用DESC
来降序排序。

下面代码可以被用于指定一个次序的商品项目,它以数量次序被排序(最小的数量在前
面)。
class Order < ActiveRecord::Base
has_many :line_items,
:order => "quantity, unit_price DESC"
end
如果两个商品项目有同样的数量,那么单价最高的被放到前面。
回过头想想,我们讨论过的has_one是,我们也提到它也支持一个: order选项。它看
起来有点奇怪-如果一个父类只和一个子类关联,那当获取那个子类时,指定的是哪个order
呢?
Active Record 可以不在现在的基础数据库上创建has_one关系。例如,一个customer
可能有很多orders:这是一个has_many关系。但是customer将只有一个最近的order。我
们可以使用has_one和: order的结合来表示这个关系:
class Customer < ActiveRecord::Base
has_many :orders
has_one :most_recent_order,
:class_name => 'Order',
:order => 'created_at DESC'
end
这段代码在customer的model中创建一个新的属性,most_recent_order。它将指向最
近created_at时间戳的order。我们可以用这个属性来查找customer的最近order。
cust = Customer.find_by_name("Dave Thomas")
puts "Dave last ordered on #{cust.most_recent_order.created_at}"

这里的代码能起作用,是因为Active Record实际上是使用这样的SQL语句来获取有
has_one关联的数据。
SELECT * FROM orders
WHERE customer_id = ?
ORDER BY created_at DESC
LIMIT 1
limit子句意思是只返回一个记录行,以符合has_one声明。order by子句确保记录行
是最近的记录。
由 has_many() 添加的更多方法
就像belongs_to和has_one一样,has_many给它的主类添加了许多属性方法。同样,
这些方法的名字以属性的名字开头。在下面描述中,我们将列出通过声明来添加的方法。
class Customer < ActiveRecord::Base
has_many :orders
end
1、orders(force_reload=false) 返回一个与这个customer关联的次序的数组(如果没
有的话,它可能为空)。结果被缓存,并且如果次序先前已被获取过,则数据库将不会再次查
询。除非传递true做为一参数。
2、orders <<order 添加次序给与这个customer关联的次序列表。
3、orders.push(order1, ...) 添加一个或多个order对象给与这个customer关联的
order列表。concat()是这个方法的一个别名。
4、orders.delete(order1, ...) 从与这个customer关联的order列表中删除一个或多
个order。这并不会删除数据库内的order对象—它简单地设置它们的customer_id外键为
空,中止它们的关联。
5、orders.clear 从这个customer分离所有order。像delete(),这会中止关联,但从
数据库中删除order,如果它们被标记成:dependent。
6、orders.find(options...) Issues a regular find( ) call, but the results are
constrained only to return orders associated with this customer. Works with the
id, the :all, and the :first forms.
7、orders.build(attributes={}) 构造一个新的order对象,使用给定属性初始化,并
连接给customer。它不会被保存。
8、orders.create(attributes={}) 构造并保存一个新的order对象,使用给定属性来
初始化,并连接给customer。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值