`
hot88zh
  • 浏览: 182921 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

has_many :through

阅读更多
原E文地址:http://blog.hasmanythrough.com/2006/04/20/many-to-many-dance-off



我已注意到使用 Rails 关联在创建多对多关系的两种方式上有些混乱。出现混乱是可以理解的,因为has_many :through 还有些新,写它的文章并不多。

Has_and_belongs_to_many 出现的较早,确定的角色与资料超出了你为什么使用多对多关系的假设。事实上,许多人似乎并没有抓住本质上的区别。

就像我们从观看经典影片所学到的,告诉我们两个预期选择之间的区别最好方式是它们的舞蹈。你得观察每个人的表演。那么我们必须在两者之间选择哪个演员呢?

一、连接表:简单的关联

表:

Ruby代码
create_table "dancers_movies", :id => false do |t|  
  t.column "dancer_id", :integer, :null => false 
  t.column "movie_id",  :integer, :null => false 
end 

create_table "dancers_movies", :id => false do |t|
  t.column "dancer_id", :integer, :null => false
  t.column "movie_id",  :integer, :null => false
end

模型:

Ruby代码
class Dancer < ActiveRecord::Base  
  has_and_belongs_to_many :movies 
end 
 
class Movie < ActiveRecord::Base  
  has_and_belongs_to_many :dancers 
end 

class Dancer < ActiveRecord::Base
  has_and_belongs_to_many :movies
end

class Movie < ActiveRecord::Base
  has_and_belongs_to_many :dancers
endhas_and_belongs_to_many 关联的设置很简单。连接表只有被连接模型的外键,没有主键或其它属性。(其它属性使用暂时的 push_with_attributes 来支持,但该特征被废弃了。)此处没有给连接表使用的模型类。

二、连接模型:富关联(Rich Associations)
表:

Ruby代码
create_table "appearances", do |t|  
  t.column "dancer_id",      :integer, :null => false 
  t.column "movie_id",       :integer, :null => false 
  t.column "character_name", :string 
  t.column "dance_numbers",  :integer 
end 

create_table "appearances", do |t|
  t.column "dancer_id",      :integer, :null => false
  t.column "movie_id",       :integer, :null => false
  t.column "character_name", :string
  t.column "dance_numbers",  :integer
end

模型:

Ruby代码
class Appearance < ActiveRecord::Base  
  belongs_to :dancer 
  belongs_to :movie 
end 
 
class Dancer < ActiveRecord::Base  
  has_many :appearances, :dependent => true 
  has_many :movies, :through => :appearances 
end 
 
class Movie < ActiveRecord::Base  
  has_many :appearances, :dependent => true 
  has_many :dancers, :through => :appearances 
end 

class Appearance < ActiveRecord::Base
  belongs_to :dancer
  belongs_to :movie
end

class Dancer < ActiveRecord::Base
  has_many :appearances, :dependent => true
  has_many :movies, :through => :appearances
end

class Movie < ActiveRecord::Base
  has_many :appearances, :dependent => true
  has_many :dancers, :through => :appearances
end

has_many :through 关联对这个简单例子来说很容易设定的,但是当使用其它特征如 polymorphism 时要有些技巧。用于连接模型的表有个主键并包含了类似其它模型的属性。

三、Checking out the moves

下面是对两种操作基本特征的比较

关联Association has_and_belongs_to_many has_many :through
AKA habtm关联 through association遍历关联
结构Structure          连接表(Join Table) 连接模型(Join Model)
主键Primary Key            no                    yes
富关联Rich Association      no                    yes
代理集合Proxy Collection     yes                   no
Distinct 选择                  yes                   yes
自引用Self-Referential       yes                  yes
早期加载Eager Loading        yes                   yes
多态性Polymorphism           no                   yes
多向连接N-way Joins          no                    yes


该表格内包含了很多东西,现在我们分别看一下:

(1)、结构:
has_and_belongs_to_many 使用了简单的连接表,表的每一行记录只有两个外键。没有为连接表使用模型类的连接记录从不会被直接访问。
has_many :through 更新连接表为一个完整的模型。它使用一个模型类来表现表内的条目。

(2)、主键:
连接表没有主键。我听说一些人,如从外键对创建一个主键,但 Rails 从不为任何东西使用主键。我不能肯定你为什么要得到创建的主键,虽然它可能依据你数据库给你一些性能上的好处。 (我不是一个 DBA ,所以对于主键不想说得太多。)
连接模型有键,就像其它模型。这意味着你可以直接地访问及管理记录。

(3)富关联:
在 Rails 1.1 以前,你使用 push_with_attributes 来存储额外的属性到你的 habtm 连接表内。这样做会带来很多问题,包括随后不能更新属性。Push_with_attributes 现在不再使用了。如果你希望一个带有额外属性的富关联的话,使用连接模型。

(4)、代理集合:
使用 habtm 的一个好处是,关联是个代理集合。这意味着你可以使用关联的 << 方法在连接表内创建条目,就像 has_many 关联。因为连接模型记录有这些额外属性,所以用同样方式来自动创建连接表条目会更复杂。所以你必须手工创建连接模型条目。(更完整的解释,可参阅我的 Why aren't join models proxy collections? 文章。)

(5)、Distinct 选择:
有时候,连接表(或模型)在同样的记录之间可能有多个引用。例如,一个人可能是一本书的作者及插图绘制者。如果你有多个引用,数据库会返回给你所有与你查询相关多条记录。选项 :uniq 告诉关联过滤重复的对象,以便你只得到每个记录的一条显示。这类似于在 SQL 中使用 DISTINCT 关键字,但这是在 Ruby 内而不是数据库内移除重复发生的记录。这篇文章原只由 habtm 支持的 :uniq 才能查到,但现在通过 through 关联也可以查到。

(6)、自引用:
Habtm 与 through 关联都可是自引用的。Users 的朋友用户就是一个自引用关系的例子。你可以通过在关联上使用带有 :foreign_key 与 :association_foreign_key 选项的 habtm 来完成。同样也可以使用 through 关联,因为它的做法不太明显,所以稍后我会写出如何处理它。

(7)、早期加载
Habtm 与 through 关联都通过 :include 选项来支持被关联对象的早期加载。

(8)、多态性:
连接模型与 through 关联可以用 polymorphic 模型类型工作。

(9)、多向连接
一个 habtm 关联只可以连接两个模型。但有时候你需要表现多个模型的关联。例如登记的可能是一位飞行家,一个乘客,及一个座位的分配。使用 through 关联,你可以创建一个连接模型,它连接你需要的多个模型。技巧部分是如何构建可方便地得到被关联对象的查询。

四、And the winner is...

has_and_belongs_to_many 的使用范围狭小。但 has_many :through 则很广泛。

Seriously, there's no way to pick a winner here. Like any engineering decision, choosing a join table or a join model is a matter of picking the right tool for the job (or the right dancer for the part). Now that you've seen our players go head to head, you can make a better choice about who should get that part.



五、回帖部分:

1、David on 2006年4月21日

关于 distinct 选择 ---- 我认为你可以附加 :select 'DISTINCT *' 给关联,类似于: has_many foos, :through => bars, :select 'DISTINCT *'
分享到:
评论

相关推荐

    many-to-many-through:这是使用has_many的许多示例教程

    has_many:通过 这是关于如何使用has_many :throght创建多对多关联的完整示例has_many :throght在此示例中,我使用staff模型和client模型,其中人员有很多客户,而客户有很多员工 操作说明 在下面的终端中输入 $ ...

    many-many-through

    通过表多对多 最简单的经验法则是,如果需要将关系模型作为独立实体使用,则应设置has_many :through关系。 示例包括: ... has_many :movies, through: :subscriptions end class Movie &lt; Applicati

    dpl_has_many_through_hwr:DPL作业复习-has_many_through

    入门项目 设置和运行步骤 ...cd project-name bundle install 在database.yml中,将数据库what_ever_you_named_your_start_project的名称更改为project-name rails db:create db:migrate db:seed ...

    rails查询学习笔记

    4. **Associations**:Rails的关联功能允许模型之间建立联系,如`has_many`、`belongs_to`、`has_one`、`has_many :through`等,它们简化了多表操作。 5. **Count、Sum、Average等聚合函数**:Rails提供了计算记录...

    Ruby on Rails中的ActiveRecord编程指南

     偏好 has_many :through 胜于 has_and_belongs_to_many。 使用 has_many :through 允许在 join 模型有附加的属性及验证   # 使用 has_and_belongs_to_many class User &lt; ActiveRecord::Base has_and_...

    amoeba:Ruby宝石,允许复制ActiveRecord对象及其关联的子对象,可通过模型上的DSL配置

    has_many :through has_and_belongs_to_many 一个简单的DSL,用于配置要复制的字段。 DSL可以应用于您的Rails模型或即时使用。 支持STI(单表继承)子代继承其父变形虫设置。 多种配置样式,例如包含,排除和不

    Ruby-Rails-Donut-Review-App

    您的模型必须: •包括至少一个has_many,至少一个belongs_to和至少两个has_many:through关系•包括用has_many:through关联实现的多对多关系。 联接表必须包含一个用户可提交的属性-也就是说,该应用程序的用户...

    Ruby-Unread管理ActiveRecord对象的已读未读状态

    has_many :unread_messages, through: :read_marks, source: :readable, source_type: 'Message' end ``` 现在,当用户查看一条消息时,我们可以调用`read`方法标记消息为已读: ```ruby current_user.read(@...

    加入有趣的轨道v-000

    下一步是在这三个模型之间建立一个has_many :through关系,以便我们可以跟踪谁在开车谁。 它将像这样工作: Taxi -&lt; Rides&gt;- Passengers 这样,出租车可以有很多乘客,而乘客可以有很多出租车! 每个表只有一个...

    Ruby-ParanoiaRails345的actsasparanoid一个重实现

    例如,`has_many :through` 关联可能需要额外配置来处理软删除。 在 Rubysherpas-paranoia-e1f7f33 这个版本中,可能包含了 Paranoia 的源代码和相关文档,你可以通过查看源码来了解其内部实现和可能的自定义选项。...

    Ruby on Rails入门经典.rar

    6. **ActiveRecord associations**:定义模型之间的关系,如属主-从(has_many/belongs_to)、一对一(has_one/belongs_to)和多对多(has_and_belongs_to_many或has_many :through)。 7. **AJAX**:Rails支持无...

    scaffolding+will_paginate+提供will_paginate下载

    3. **模型中启用分页**:在需要分页的模型中,使用`has_many :through`或`has_and_belongs_to_many`关联时,需要在关联上添加`:finder_sql`选项,以便`will_paginate`可以正确处理分页。 4. **控制器中设置分页参数...

    will_paginate分页插件

    2. 在模型(model)中,使用 `has_many :through` 或其他关联方法查询数据,然后调用 `.paginate` 方法。 3. 在视图(view)中,使用 `will_paginate @items` 生成分页链接。 4. 在控制器(controller)中,接收页码...

    ecto_rut:Ecto Model快捷方式使您的生活更轻松!

    many_to_many :posts, MyApp.Post, join_through: MyApp.UserPost end defmodule MyApp.Post do ... many_to_many :users, MyApp.User, join_through: MyApp.UserPost end ``` 5. **变更集**:Ecto的变更集...

    query_methods_extend

    has_many :books , through : :categories end // Store ( id : integer , address : string ) class Category &lt; ActiveRecord :: Base belongs_to :store has_many :books end // Category ( id : integer , ...

    displaying-has-many-through-rails-onl01-seng-pt-032320

    对我们来说幸运的是has_many, through Active Record和has_many, through来显示这种类型的数据的界面也很容易。 课 has_many,通过 假设您正在制作博客,并希望使用户能够注册并评论您的帖子。 帖子和评论之间有...

    SinatraStreetFighter:欢迎使用我简化的Street Fighter模拟版本。 您可以选择一个角色,添加战斗动作以及史诗般的战斗开始的最后阶段!

    特征 楷模用户,角色,移动,舞台用户has_many:字符字符belongs_to :用户字符has_many :动作字符has_many :stages, through: ::moves 阶段has_and_belongs_to_many :移动阶段has_many :characters, ...

    Courier:用于将 JSON 资源同步到核心数据的 Rubymotion 包装器

    信使 用于将 JSON 资源同步到核心数据的 Rubymotion 包装器。 -- ###Setup Gemfile gem 'afmotion' ... bundle exec rake pod:... has_many :players , through : [ :teams , :players ] attr_accessor :unpersis

    polo:Polo遍历您的数据库并创建示例快照,因此您可以在开发中使用现实世界的数据

    马球 Polo遍历您的数据库并创建示例快照,因此您可以在任何环境中使用真实数据。 Polo接受一个ActiveRecord::Base种子对象,并遍历所有列入白名单的ActiveRecord::... has_many :ingredients , through : :rec

Global site tag (gtag.js) - Google Analytics