理解ActiveRecord的关联
初学Rails时,觉得ActiveRecord很神奇,只要在model类中写上has_many, belongs_to等声明,就可方便地引用关联对象.这些关联声明还有很多选项,但开始并不理解(尤其对从未使用过ruby语言的人),只知道模仿着教程中的例子(以下的教程都指那本经典书:Agile web development with Rails)使用默认的选项.但是做实际项目时会发现默认选项不够用了,该怎么办?通过下面一个实际项目开发中的例子,就能真正理解model类的关联是怎么回事了.
场景:一个活动发布网站,要实现计划的发布和用户报名功能.报名还要分为已批准和待批准两种状态.
建模:(为表述方便对各表只列举最重要和有代表性的几个字段)很自然地首先会建立两个数据库表:users,plans.
users包含字段 id, name, password
plans包含字段 id, content, created_at (该记录创建时间,按 Rails 的约定该字段能自动维护), user_id (关联到users表,表示该计划的作者)
为了建立最基本的关联,只需要在模型文件中增加一个has_many和belongs_to:
# user.rb
class User < ActiveRecord::Base
has_many :plans
end
# plan.rb
class Plan < ActiveRecord::Base
belongs_to :user
end
增加这些声明的好处是什么?就是将来我得到一个plan对象时,可以用plan.user访问它的作者的信息;得到一个user对象时,可以用user.plans访问他发布的所有计划.多么简单!现在我要实现报名的功能了.
所谓报名,就是在plan和user之间再建立一种关联.这次是典型的多对多关联,因为一个计划可以有多个用户报名,而一个用户也可以报名参加多个计划.
多对多关联?教程上不是有介绍吗?马上模仿着建立一个关联表plan_user.包含字段 plan_id, user_id.
再按教程修改user.rb和plan.rb,增加相应的has_many:
# plan.rb
class Plan < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many : users
end
# user.rb
class User < ActiveRecord::Base
has_many :plans
has_and_belongs_to_many : plans
end
等等,User.rb里面怎么出现两个plans了,以后使用user.plans时到底是哪一个?静下来想一想,原来第一个has_many :plans表示一个用户发布的多个计划,第二个表示一个用户参加的多个计划.它们关联的对象虽然相同,但在业务逻辑上的意义是不同的.可是在代码中如何表示这种不同呢?我开始查关联的选项,发现有这么几个可用的选项:
:class_name, :join_table. 也就是说我可以改变关联的名称,但仍然关联到同一个表,修改如下:
# user.rb
class User < ActiveRecord::Base
has_many :plans
has_and_belongs_to_many :plans_participated, :class_name => "lan"
end
这样一来,我就可以用user.plans来访问用户发布的计划,用user.plans_participated来访问用户参加的计划了.类似地,我修改了plan.rb中关联的名称以便我可以用plan.participants而不是plan.users来访问一个计划的所有参与者:
# plan.rb
class Plan < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :participants, :class_name => "User"
end
做到这里,我们稍微总结回味一下就不难发现,其实关于关联的各种声明就相当于给你的模型类动态增加属性,而has_many,belongs_to等声明其实是来自于ActiveRecord基类的一些方法,后面的选项是传给这些方法的参数.这些方法一被执行,就会给你的模型类动态增加一些属性.(在 Ruby的类定义里可以直接执行一些语句,就象HTML中写在<script>中的脚本一样,每当页面被装载就会被执行)
当我们写下 has__many :plans"这句话,就是在调用has_many方法,给User类增加了一个名为plans,类型为集合的一个属性.这个属性的返回值是来自于数据库表的,Rails的底层有足够信息知道该生成一条什么样的sql语句来返回相应的结果.甚至你还可以利用:finder_sql选项指定一条sql语句来替换默认的sql.这就是所谓关联声明的实质,也就是一些来自于基类的方法.一个方法调用是为了给类增加属性,动态地改变一个对象的行为?听起来有点陌生,其实在非脚本语言如C#里面事件机制也是类似的,button.clicked += new EventHandler(OnClick);这句话不就是通过对一个属性赋值而给button对象动态增加了一个方法供调用吗?
当然,Rails替你做的还不止是增加了属性,它还给你的模型类增加了一些操作关联表的方法,包括push,delete,find等(详见教程),省得你再去为plan_user表单独建立一个模型类了.
现在我的任务还没完成.我发现报名功能仅记录plan_id和user_id还不够,我还需要记录报名的时间,报名是否被批准,用户留言等信息.这些信息当然应该记录在报名表中.于是给plan_user表增加了几个字段created_at,confirmed,memo. 但是为了读写这几个字段,我还是得建立一个单独的模型类.不然更新这些信息时比较麻烦.加上这么些报名信息,其实报名表就应该是一个单独的数据实体了,在教程里David也提到:when a Join wants to be a model.说得就是这个意思,当一个多对多关联有很多属性时就应该做为一个model对待.于是我打算建立一个模型类.并且按模型类的约定把表名改成 joins,类名就叫Join (这里的join我是表示报名,而不是关联的意思):
#join.rb
class Join < ActiveRecord::Base
belongs_to :plan
belongs_to :user
end
这样我就有很多关于报名的方法可用了!而且将来要增加的批准方法也有地方放了.可是plan和user模型中的声明已经不再符合默认规则了,不要紧,利用一下:join_table选项:
class Plan < ActiveRecord::Base
belongs_to :user
has_many :joins
has_and_belongs_to_many :participants, :class_name => "User", :join_table => "joins"
end
class User < ActiveRecord::Base
has_many :plans
has_many :joins
has_and_belongs_to_many :plans_participated, :class_name => "lan", :join_table => "joins"
end
现在我想增加一个方法,得到所有已批准的报名.也就是所有confirmed = true的记录.难道要自己写一个方法,执行一个sql吗?不用.只要修改关联的声明,加一个 :condition => "confirmed = true"即可.但是我还想同时保留访问所有报名的功能怎么办?没问题,既然一个关联只不过是一个方法调用来增加一个属性,那为什么不可以调用多次,增加多个属性?
class Plan < ActiveRecord::Base
belongs_to :user
has_many :joins
has_many :confirmed_joins, :conditions => "confirmed = true"
has_many :unconfirmed_joins, :conditions => "confirmed = false"
has_and_belongs_to_many :participants, :class_name => "User", :join_table => "joins"
end
以后的用法举例如下:
用plan.user得到计划的发布者对象.
用plan.participants得到一个计划的所有参与者对象,进而访问参与者的姓名等信息.
用plan.confirmed_joins得到所有已批准的报名对象,进而访问报名时间,留言等信息.
到现在为止,我要的操作都通过关联声明来实现了,没有自己编写一个方法.而且对于关联表join,同时利用了单独模型类的好处和多对多关联的好处.真是鱼和熊掌兼得呀.
最后的结论就是,一个关联实际上就是基类的一个方法,你可以多次调用,任意组合.
分享到:
相关推荐
ActiveRecord还支持关联,如一对一(has_one),一对多(has_many),多对一(belongs_to)和多对多(has_and_belongs_to_many)关系。例如,如果`users`表和`posts`表有关系,可以这样定义: ```ruby class User ...
#### 四、深入理解ActiveRecord 1. **模型类**: 每个模型类代表一个数据库表,模型类的实例则代表表中的记录。 2. **属性访问**: 可以直接通过对象的属性来访问数据库字段,如`user.name`。 3. **查询方法**: 提供了...
例如,`[ActiveRecord]`用于标记一个类为ActiveRecord实体,`[PrimaryKey]`用于标识主键字段,`[BelongsTo]`或`[HasMany]`用于定义关联关系。 3. **数据库操作方法**:这些方法包括`Save()`(用于创建或更新记录)...
另外,理解何时使用Session和Transaction是至关重要的,以确保数据的一致性和完整性。 10. **调试与性能优化** 调试NHibernate可以通过启用SQL日志查看生成的SQL语句,分析性能瓶颈。优化通常涉及减少数据库往返...
ActiveRecord 是一种设计模式,源自 Ruby on Rails,它将业务对象与数据库记录关联起来,使得开发者可以直接操作对象,而无需编写大量的 SQL 语句。Castle.ActiveRecord 将这种理念带到了 .NET 平台,通过注解或者...
ActiveRecord是Ruby on Rails框架中的一个核心组件,它实现了对象关系映射(ORM)系统,...在提供的压缩包"ActiveRecord简单实例代码"中,你可能会看到这些概念的具体应用,从而更好地理解和掌握ActiveRecord的用法。
今天把这个问题讲明白了,看看yii2 ActiveRecord是怎么个多表关联以及如何去优化这个关联。 场景需求: 假设我们有一张用户表user和一张用户渠道表auth,两张数据表通过user.id和auth.uid进行一对一关联。现需要在...
Castle ActiveRecord 是一个面向对象的持久化框架,它基于著名的设计模式——Active Record,该模式源自Ruby on Rails。这个手册是专为那些想要在.NET...手册将详细解释每个知识点,并提供示例代码帮助你理解和实践。
ASP(Active Server Pages)是微软开发的一种服务器端脚本环境,用于创建动态网页或Web应用程序。...如果你熟悉ASP和面向对象编程,那么这个资源可以帮助你更好地理解和应用ActiveRecord模式在实际项目中。
1. **扩展ActiveRecord模型**:定义新的字段和关联,以支持两个时态。 2. **数据库迁移**:创建相应的数据库表结构,包含系统时态和业务时态字段。 3. **模型方法**:编写保存和查询方法,处理时态逻辑,例如插入、...
6. **查询优化**:通过预加载关联数据来减少数据库查询次数,提高性能。 在使用`ActsAsTree`时,你需要在你的ActiveRecord模型中包含`ActsAsTree`模块,并根据需要配置相关属性,如下所示: ```ruby class ...
这样的链式调用允许你在查询的不同阶段添加或修改条件,使代码更清晰,更易于理解。 总的来说,Rails 3 的 ActiveRecord 查询API引入了一种更模块化、更面向对象的方式来处理数据库查询,提高了代码的可读性和可...
通过阅读这些内容,开发者可以更深入地理解其工作原理,自定义配置,以及如何解决可能出现的问题。 总的来说,pg_search gem是Ruby on Rails开发中的一个重要工具,它让开发者能够利用PostgreSQL的全文搜索能力,...
使用Closure Tree,你需要首先在你的模型中包含`has_closure_tree`方法,这将为模型添加必要的字段和关联。例如: ```ruby class Category < ActiveRecord::Base has_closure_tree :order => :name end ``` 在这...
1. **模型类(Model)**:这是与数据库表关联的PHP类,包含了表的字段映射、规则验证和行为等特性。 2. **查询构建器(Query Builder)**:提供了一组用于构造SQL查询的方法,如`select()`, `where()`, `join()`, `...
简化的快照和ActiveRecord模型的还原以及具有透明白盒实现的关联。 主要特点: 创建和还原父记录和任何指定的子记录的快照 可预测和明确的行为为您的还原逻辑提供了非常需要的清晰度 快照仅根据请求创建,我们不...
它允许你定义自定义的翻译存储和查找策略,以及处理关联和反向关联。此外,Globalize与流行的Rails gems如PaperTrail、Ransack和Formtastic等兼容,使得在多语言环境中工作变得更加方便。 总之,Globalize是一个...
通过阅读这些源码,我们可以深入理解ActiveRecord如何自动映射数据库表,如何处理复杂的关联关系,以及如何根据数据库变化动态调整模型。 总的来说,ActiveRecordGenerator_src_2007-10-26.zip提供了对ActiveRecord...
这种库通常会处理SQL查询,将其转化为更易于理解和使用的数据结构。在这个特定情况下,它将查询结果转换为“分析哈希”,这是一个专门为数据分析和可视化准备的数据结构。 “分析哈希”是一种特殊形式的哈希(即...