rails笔记 activerecord 关系
actuverecord realation
convention 约定
对应关系:
- 普通对象: Person -> people table -> person_id
- join_table: tablenamea_tablenameb 为table名(按照字母顺序排列)
关系
注意belongs_to对应的表必须有外键, rails认为一个表belongs_to他外键引用的表
-
one-to-one
class Order < ActiveRecord::Base has_one :invoices . . . class Invoice < ActiveRecord::Base belongs_to :order . . .
-
one-to-many: belongs_to
class Order < ActiveRecord::Base has_many :line_items . . . class LineItem < ActiveRecord::Base belongs_to :order . . .
-
many-to-many
class Product < ActiveRecord::Base has_and_belongs_to_many :categories . . . class Category < ActiveRecord::Base has_and_belongs_to_many :products . . .
one-to-one
下面两个操作在逻辑在等价
- order.invoce=invo
- auto invo.save
注意1
当把子对象赋给父的时候(order.invoce=invo),子对象如果未存储会自动存储(auto invo.save),但是把父赋给子的时候(invo.order=ord)不会自动存储, 因为此时存储order没有意义, 对应关系是保存在invoces表中的,不过在稍后的invo.save中, 如果父没有创建,还是会自动创建的
注意2 当使用上门的自动存储的时候,由于调用的save方法,所以即使出错也不会报告,所以最好还是自己来invo.save!(比如invo有内部验证的时候),然后在建立连接关系
belongs_to扩展参数,如下例子
class LineItem < ActiveRecord::Base
belongs_to :paid_order,
:class_name => "Order",
:foreign_key => "order_id",
:conditions => "paid_on is not null"
end
- :class_name :对应的类
- :foreign_key :指定外键名
- :conditions : 用来筛选外键表中的记录(此时foreign_key不能唯一确定记录,或者有不需要的记录)
has_one 也有类似的功能, 多了两个
-
:denpendent
删除父亲的时候自动删除孤儿(参见hibernate) ???实现失败,加上不加上都不会删除,数据库报外键约束错误
-
:order
排序,虽然是has_one, 但是这个one可以通过排序动态得到(比如最高分的选手)
hash_one and belongs_to 增加的方法
belongs_to和has_one都会给对象添加下面的方法,假设对应的是product对象
-
product(force_reload=false)
Return the associated product (or nil if no associated product exists).The result is cached, and the database will not be queried again if this order had previously been fetched unless true is passed as a parameter.
-
product=(obj)
Associate this line item with the given product, setting the foreign key in this line item to the product’s primary key. If the product has not been saved, it will be when the line item is saved, and the keys will be linked at that time.
-
build_product(attributes={})
Construct a new product object, initialized using the given attributes.This line item will be linked to it. The product will not yet have been saved.
-
create_product(attributes={})
Build a new product object, link this line item to it, and save the product.
one-to-many
has_many 扩展声明(未列出的与has_one相同)
-
:dependent
会把关联他的对象一个一个load出来调用它们的destory
-
:exclusively_dependent
一个sql删除:denpent需要删除的对象,但是需要保证这些对象真是孤儿而且没有callback
-
:finder_sql
构建动态的子对象群
-
:counter_sql
finder_sql调用前call他得到数量, 如果不给出,会自动通过finder_sql添加"count(*)"来计算
-
:order
排序
has_many 增加的方法
-
orders(force_reload=false)
Returns an array of orders associated with this customer (which may be empty if there are none). The result is cached, and the database will not be queried again if orders had previously been fetched unless true is passed as a parameter.
-
orders <
Adds order to the list of orders associated with this customer.orders.push(order1, ...) Adds one or more order objects to the list of orders associated with this customer. concat( ) is an alias for this method.
-
orders.delete(order1, ...)
Deletes one or more order objects from the list of orders associated with this customer. This does not delete the order objects from the database—it simply sets their customer_id foreign keys to null, breaking their association.
-
orders.clear
Disassociates all orders from this customer. Like delete( ), this breaks the association but deletes the orders from the database only if they were marked as :dependent.
-
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.
-
orders.build(attributes={})
Constructs a new order object, initialized using the given attributes and linked to the customer. It is not saved.
-
orders.create(attributes={})
Constructs and save a new order object, initialized using the given attributes and linked to the customer.
many-to-many
在many-to-many中,必须有一个jiontable, 用来保存对应关系,表名的规则是按照字母顺序连接,如 table categories_products join table不能有id ,一方面不需要, 另外一方面会在自动include中覆盖父对象的id
join table额外保存的属性,按道理不应该额外保存属性,额外的属性越明显,就越说明此处的many-to-many关系设计得有问题,适当时候要抽象出一个新model来分别belongs_to两个父亲
relation 中的延迟读取
默认relation关系中都是延迟读取的,如果需要一次性遍历多个记录操作, 可以采用:include指令,下面例子中,include读取三个表只需要一个sql语句(采用left join,需要数据库支持) for post in Post.find(:all, :include => [:author, :comments]) puts "Post: #{post.title}" puts "Written by: #{post.author.name}" puts "Last comment on: #{post.comments.first.created_on}" end
同时要注意,有:include和:condition一起的时候,字段名要全名,如下 ost.find(:all, :conditions => "posts.title like '%ruby%'", :include => [:author, :comments]) # ...
counter_cache
belongs_to :xxx,:counter_cache=>true ,同时父对象表中需要如下的新字段 line_items_count int default 0,注意默认值必须为0(或者其他机制保证初始化为0),否则每次都是null 可以避免每次count都去数据库count(*),这里的cache会在数据增删的时候自动维护
注意 以上的自动更新时, 直接给儿子们更新父亲时是不起作用的, 必须使用father.children(:refresh)才能更新但是使用father.children<< child就没有问题 ???