新手问题 ActiveRecord 真的是 ORM 吗?

zacker330 · 2014年06月21日 · 最后由 yumu01 回复于 2014年06月22日 · 4880 次阅读

小弟不才,Rails 新手,ActiveRecord 新手。

希望能有一个简单易懂的解释,谢谢。

#1 楼 @Rei 谢谢了。

请问 rails 是不是把表结构 (其实是 model 的属性) 的定义放在 db/schema.rb 里,然后把 model 之间的关联定义放在 model 里。。。。这样实际上是把结构和关联放在不同的地方了。。。。这让我觉得很别扭。

#2 楼 @zacker330 清空你的知识,接收它。

#3 楼 @Rei 我疑问,不代表我不接受。

我只是想知道,为什么要这样设计?还有,你们要修改 model 之间的关系时,是不是又要改 model,又要去另一个文件改表结构。。

#3 楼 @Rei 让他看看 java 的 hibernate,他就懂了

#5 楼 @i5ting 我用过 hibernate.只不过,我用的更多的是 jpa。

ActiveRecord 算不上是 ORM,有个插件可以再 model 里面定义表结构的,貌似叫 datamaper

#5 楼 @i5ting 是我不懂楼主疑问在哪里。或者楼主说说以前用的工具是怎么做的?

Martin Flower 的定义很明确啊

An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.

楼主的思维方式可能要改变。你是从数据库开始思考问题的。而一个应用应该是以 model 为中心思考。储存在数据库里只是 model 的一个存储方式而已,model 的存储也可以在内网其他应用,也可以是外部 API,也可以是一个文本文件,可以存在于很多地方。

#8 楼 @Rei 在我的眼里,ORM 是关系和结构都定义在一个地方,而不是分开定义。所以,我这么一个 Active Record 新手对 ActiveRecord 是不是 ORM 有疑问。

#9 楼 @billy 呵呵。我很明白老马的意思。所以,才不明白 Active Record 的一些设计。

敢问,你在写 Rails 的 model 的时候,你不是从数据库开始思考问题的?你必须在 schema.rb 里定义数据库的字段,然后又跑到 model.rb 里加个关联。

@zacker330 我还真的不是从数据库开始思考的诶 :)

打比方如果写或者维护 ruby-china,那脑子就全部都是 Post, User, Comment。之间的关系都是 User has many comments, comment belongs to user。而不是 comments 表有一个 user_id 字段等等。

又比如说 repository model(我不知道具体的写法,只是假想),User 有一个 github repos 的数据,那么我就知道 User has many repos。至于这个 repos 是存在数据库里,还是直接从 github 获取,都不影响别的 model 和它直接的关系。

另外一个,schema.rb 是不能自己修改的。推荐的方法是通过 migration 来修改。

#12 楼 @billy

假设,现在你要加一个 event 的类,它和 User 是多对一。如果我没猜错,是不是应该:

rails g model event user_id:integer content:string

然后,打开 event.rb 这个 Model,再向里面加上:

belongs user

请问,这个过程中,你没有从数据库开始考虑吗?你必须要考虑给 event 表加上一个外键。

如果我猜错了,请告诉我更好的实践。谢谢了。

还有,rails 的 migration 我很喜欢~

Rails 开发不是直接改 db/schema.rb ,而是写 migration 脚本(http://docs-china.com/rails/migrations.html)。ActiveRecord 不需要声明属性,它自动读取数据库现有的属性。关系不会自动生成,可能是不想做得太多。

我搜了一下 hibernate 有个 SchemaUpdate 工具,自动根据 model 属性增删数据库表字段,不清楚是不是真的在生产环境用这个工具。Rails 用的 migration 的好处是显示声明,可以纳入版本控制,可回滚操作,可以在模式迁移的同时写上数据迁移。

#12 楼 @billy 我是同时考虑数据库结构和 Model 的,没有从哪个开始思考之分。

#14 楼 @Rei 嗯嗯。不直接修改 schema.rb 是好的,尤其在生产环境。但是,开发过程,我会经常调整 model 模型,所以,我在开发过程,不会用那个 migration。上线后,才考虑使用 migration。

而 ActiveRecord 的关系不会自动生成,如果是为了防止程序员使用太多“关联”,我觉得这个理由太那个了吧。。。。

hibernate 的那个工具很少用的,在我的工作生涯中。用的地方就只是在最初生成建表语句的时候用。ORM 生成的表是没有索引的,所以,最终还是要手工处理一些数据库的东西。但是建模的过程(在 rails 里就是创建各种 model 的过程)是不需要考虑数据库的。

#16 楼 @zacker330 开发过程如果没有协作开发需求,可以修改同一个 migration,然后 rake db:migration:redo STEP=n。多人开发也可以,不过可能协作者会需要经常 reset 加抱怨。

schema.rb 是快速导入数据库模式的时候用的,比如 test 环境和 production 环境,另外可以让开发者完整看到目前数据库的模式。每次 migration schema.rb 都会重新生成,千万不要手动改这个文件。

@zacker330 没错,如果 events 初步定在存在数据库里面,大致是这样。也有可能我在跑第一个命令时不写数据字段或写得不到位,然后再打开 migration 文件修改,这个不影响流程。

我没有说不需要考虑存储,我是说以 model 为中心思考问题。你现在需要拿到 events,就可以

user.events

而不是

User.find_by_sql("SELECT * FROM events ....")

以后也许考虑 performance 或其他原因,把 Event 放到其他服务。比如一个 Node.js 的 API。但你所做的只需要迁移 Event 自身,不需要过多考虑其他 model 直接的关系。之前的user.events仍然有效,但User.find_by_sql...就会失效了。

@Rei 这个是风格不同。我第一位考虑的是 model 和 model 的 API。

Mongodb 是无模式的,不需要模式迁移,所以 mongoid 可以直接把属性写在 model 里,但是数据迁移始终需要 migration。

ActiveRecord 就是既然要写 migration 了,那就不用在 model 里面写属性定义了;而不是在 model 里面定义一次属性,然后再写一次 migration。

#16 楼 @zacker330 我意思是 ActiveRecord 不想增加太多默认行为,有可能对开发形成干扰。

#17 楼 @Rei migration 在我看来适合增量开发,比如在已经有的基础上开发新功能,然后维护。而我一个全新的项目也用 migration?比如,一个订单系统还没有上线,需求每天都在变。今天要求 order 这个 model 要加个字段 A,第二天又加字段 B、C,第三天又加一个字段 D 等等,这样下去,我们是不是有很多个 migration 了?那么,我这个 model 最终有哪些字段,我总不可以一个一个 mirgration 的打开来总结出这个 model 的全部字段吧?对了,去数据库看表结构是个好办法。但是,这样不麻烦吗????请问,有没更好的实践,不这么麻烦?还是我猜错了?请指正。如果,我的假设是对的,那么,不到维护或开发新功能阶段,我想不到为什么要使用 migration.

#18 楼 @billy 我明白你的意思。你说的是模型行为,当然不用考虑数据库。而建模的过程,除了考虑模型的行为,还必须要考虑它的内部结构(有什么属性)。而 ORM 应该能让我在建模的过程,不需要考虑数据库。而 ActiveRecord 这点没有做好。

#22 楼 @zacker330 开发阶段一个 model 一个 migration,上线之后增量 migration。看数据库最终结构看 schema.rb。

#23 楼 @Rei 好。谢谢了。 经过大家这么一讨论,我明白一些了。

不要用已有的经验去揣测未知的东西,尤其是学新知识的时候。

你最开始的疑惑在于 Rails 为什么把表结构定义放在 db/schema.rb 里,因为你有个概念“ORM 是关系和结构都定义在一个地方”,至少是觉得 ORM 需要定义关系和结构。但 Rails 的 model 恰恰不需要你定义结构。你完全可以自己用 SQL 生成数据表,然后建立一个空 model,这完全可行。不需要建立 migration,不需要改 db/schema.rb

CREATE TABLE projects ( name varchar(30) );
class Project < ActiveRecord::Base
end

project = Project.new
project.name    # 不需要定义 name 属性,ActiveRecord 帮你生成

如果你执着于 ORM 需要你定义结构这种概念,那肯定会百思不得其解,然后到处去找 Rails 又生成了什么文件去定义结构…… 也许 Google 一番后你会知道这是因为 Rails 会按约定去扫描对应的数据表 projects 从而自动生成 model 属性。而这样设计的初衷正是为了让开发者不需要因为更新数据表而频繁修改 model。

然后你会发现不是 ORM 需要你定义结构,只是你以前用过的一个或者一些 ORM 需要你定义结构而已。但这时你已经绕了一个圈子了。

至于 db/schema.rb 和 migration 文件,那是给你方便地修改数据库结构的工具。楼上的各位已经讨论的很清楚了,就不掺合了。

最后引用一段话,来自 Rails Guide 的 ActiveRecord 章节:

When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particularly true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you'll need to write very little configuration (in some case no configuration at all) when creating Active Record models. The idea is that if you configure your applications in the very same way most of the time then this should be the default way. Thus, explicit configuration would be needed only in those cases where you can't follow the standard convention.

建议你学习 Rails 以 Rails Guide 为主,应该可以解答大部分的新手问题。

#10 楼 @zacker330 schema.rb 里面的东西不是用来定义 model 的属性的,只是个数据库字段缓存文件. 数据库字段映射成 model 属性是在 model 中完成的,只是这不是一个显式的过程,model 自动到数据库中找到字段并且映射。

belongs_to 不强制使用外键确实是个痛点,你可以回顾 activerecord 历史,就可以看到 activerecord 有很多不合理的地方。相比之下,sequel 这个 gem 设计的更好,activerecord 4 更是从中抄袭了很大一部分。

#25 楼 @darkbaby123 不要用已有的经验去揣测未知的东西,尤其是学新知识的时候。 实际上,用已有的知识去揣测新新知是很自然的方式。这就是对比学习。只要不固执的认为自己以前的全是对的,也不排斥新知识,这种方式,我觉得是可行的。 我看了几本 Rails 的书,都是在说怎么做,而没有说为什么。这让我有很多疑问。来这里问,人们也条件反射式的认为我在排斥。。。

@zacker330 你说的对,只要不固执的认为自己以前的全是对的,也不排斥新知识。 不知道你以前看的什么书学 Rails,如果觉得那些书都不能解答你的问题,就去看 Rails Guide 吧。关于你这个问题(ActiveRecord 和 ORM)里面都有很详细的解释,包括 ActiveRecord 的设计哲学。

@zacker330 假设,现在你要加一个 event 的类,它和 User 是多对一。就从这个例子讲下我的理解,首先做这个事情之前,我们知道这里有两个 model User and Event 那么 以数据库结构还是以 Model 为主呢? 在这点上我很同意@Rei的说法,我们两者都要结合考虑,在数据库方面我们知道他们的关系那么 event 这张表就有 user_id 这个字段,其余字段安需要加变好。接下来我们便要创建 model 了,值得一提的是,创建 model 的命令 rails g model Event user_id:integer content:string 一般 user_id 这样的字段很少会直接放在后面,甚至你可以以直接简单的rails g model Event,所有的字段在 migration 里面自己增删,这是非常方便的,你可以想象当你有很多字段时候通过命令创建我想这不是很好的方式。接下来两张表的关系,便可以用 has_many, belong_to 等方法确认关系。 注意:创建 model 名字要大写

楼主看看 07 年这篇 http://blog.aizatto.com/2007/05/21/activerecord-without-rails/ “Rails 中的 ActiveRecord”是可以脱离 Rails 的独立使用的 ORM,只是在 Rails 中使用会是 best practice,而 Rails 的 migration 只是 Rails 管理数据库变化的一个方式。

@rainchen ,ActiveRecord 是的确可以脱离 rails 单独使用,这在其他框架下也经常这么做。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号