2.3 微博资源
我们已经生成并浏览了用户资源,现在要生成微博资源。阅读本节时,我推荐你和 2.2 节对比一下。你会发现两个资源在很多方面都是一致的。通过这样重复生成资源,我们可以更好地理解 Rails 中的 REST 架构。在这样的早期阶段看一下用户资源和微博资源的相同之处,也是本章的主要目的之一。
2.3.1 概览微博资源
和用户资源一样,我们使用 rails generate scaffold
命令生成微博资源的代码,不过这一次要实现图 2.3 中的数据模型:[8]
$ rails generate scaffold Micropost content:text user_id:integer
invoke active_record
create db/migrate/20140821012832_create_microposts.rb
create app/models/micropost.rb
invoke test_unit
create test/models/micropost_test.rb
create test/fixtures/microposts.yml
invoke resource_route
route resources :microposts
invoke scaffold_controller
create app/controllers/microposts_controller.rb
invoke erb
create app/views/microposts
create app/views/microposts/index.html.erb
create app/views/microposts/edit.html.erb
create app/views/microposts/show.html.erb
create app/views/microposts/new.html.erb
create app/views/microposts/_form.html.erb
invoke test_unit
create test/controllers/microposts_controller_test.rb
invoke helper
create app/helpers/microposts_helper.rb
invoke test_unit
create test/helpers/microposts_helper_test.rb
invoke jbuilder
create app/views/microposts/index.json.jbuilder
create app/views/microposts/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/microposts.js.coffee
invoke scss
create app/assets/stylesheets/microposts.css.scss
invoke scss
identical app/assets/stylesheets/scaffolds.css.scss
如果看到 Spring 相关的错误,再次执行这个命令即可。
然后,和 2.2 节一样,我们要执行迁移,更新数据库,使用新建的数据模型:
$ bundle exec rake db:migrate == CreateMicroposts: migrating ===============================================
-- create_table(:microposts)
-> 0.0023s
== CreateMicroposts: migrated (0.0026s) ======================================
现在我们就可以使用类似 2.2.1 节中介绍的方法来创建微博了。你可能猜到了,脚手架还会更新 Rails 的路由文件,为微博资源加入一条规则,如代码清单 2.8 所示。[9]和用户资源类似,resources :micropsts
把微博相关的 URL 映射到 MicropostsController
,如表 2.3 所示。
代码清单 2.8:Rails 的路由,有一条针对微博资源的新规则
config/routes.rb
Rails.application.routes.draw do
resources :microposts resources :users
.
.
.
end
表 2.3:代码清单 2.8 中微博资源生成的符合 REST 架构的路由
HTTP 请求 | URL | 动作 | 作用 |
---|---|---|---|
GET |
/microposts | index |
列出所有微博 |
GET |
/microposts/1 | show |
显示 ID 为 1 的微博 |
GET |
/microposts/new | new |
显示创建新微博的页面 |
POST |
/microposts | create |
创建新微博 |
GET |
/microposts/1/edit | edit |
显示编辑 ID 为 1 的微博页码 |
PATCH |
/microposts/1 | update |
更新 ID 为 1 的微博 |
DELETE |
/microposts/1 | destroy |
删除 ID 为 1 的微博 |
MicropostsController
的代码简化后如代码清单 2.9 所示。注意,除了把 UsersController
换成 MicropostsController
之外,这段代码和代码清单 2.4 没什么区别。这说明了两个资源在 REST 架构中的共同之处。
代码清单 2.9:简化后的 MicropostsController
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
.
.
.
def index
.
.
.
end
def show
.
.
.
end
def new
.
.
.
end
def edit
.
.
.
end
def create
.
.
.
end
def update
.
.
.
end
def destroy
.
.
.
end
end
我们在发布微博的页面(/microposts/new)输入一些内容,发布一篇微博,如图 2.12 所示。
既然已经打开这个页面了,那就多发布几篇微博,并且确保至少把一篇微博的 user_id
设为 1
,把微博赋予 2.2.1 节中创建的第一个用户。结果应该和图 2.13 类似。
图 2.12:发布微博的页面图 2.13:微博索引页(/microposts)
2.3.2 限制微博内容的长度
如果要称得上“微博”这个名字,就要限制内容的长度。在 Rails 中实现这种限制很简单,使用验证(validation)功能即可。要限制微博的长度最大字数为 140 个字符(就像 Twitter 一样),我们可以使用长度验证。在文本编辑器或 IDE 中打开 app/models/micropost.rb
,写入代码清单 2.10 中的代码。
代码清单 2.10:限制微博的长度最多为 140 个字符
app/models/micropost.rb
class Micropost < ActiveRecord::Base
validates :content, length: { maximum: 140 } end
这段代码看起来可能很神秘,我们会在 6.2 节详细介绍验证。如果我们在发布微博的页面输入超过 140 个字符的内容,就能看出这个验证的作用了。如图 2.14 所示,Rails 会渲染错误信息,提示微博的内容太长了。(7.3.3 节会详细介绍错误信息。)
图 2.14:发布微博失败时显示的错误消息
2.3.3 一个用户拥有多篇微博
Rails 最强大的功能之一是,可以在不同的数据模型之间建立关联(association)。对本例中的用户模型而言,每个用户可以拥有多篇微博。我们可以更新用户模型(参见代码清单 2.11)和微博模型(参见代码清单 2.12)的代码实现这种关联。
代码清单 2.11:一个用户拥有多篇微博
app/models/user.rb
class User < ActiveRecord::Base
has_many :microposts end
代码清单 2.12:一篇微博属于一个用户
app/models/micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user validates :content, length: { maximum: 140 }
end
我们可以把这种关联用图 2.15 中的图标表示出来。因为 microposts
表中有 user_id
这一列,所以 Rails(通过 Active Record)能把微博和各个用户关联起来。
图 2.15:微博和用户之间的关联
在第 11 章和第 12 章,我们会使用微博和用户之间的关联显示一个用户的所有微博,还会生成一个和 Twitter 类似的微博列表。现在,我们可以在控制台(console)中检查用户与微博之间的关联。控制台是和 Rails 应用交互很有用的工具。在命令行中执行 rails console
命令,启动控制台。然后输入 User.first
,从数据库中读取第一个用户,并把得到的数据赋值给 first_user
变量:[10]
$ rails console >> first_user = User.first
=> #<User id: 1, name: "Michael Hartl", email: "[email protected]",
created_at: "2014-07-21 02:01:31", updated_at: "2014-07-21 02:01:31">
>> first_user.microposts
=> [#<Micropost id: 1, content: "First micropost!", user_id: 1, created_at:
"2014-07-21 02:37:37", updated_at: "2014-07-21 02:37:37">, #<Micropost id: 2,
content: "Second micropost", user_id: 1, created_at: "2014-07-21 02:38:54",
updated_at: "2014-07-21 02:38:54">]
>> micropost = first_user.microposts.first # Micropost.first would also work.
=> #<Micropost id: 1, content: "First micropost!", user_id: 1, created_at:
"2014-07-21 02:37:37", updated_at: "2014-07-21 02:37:37">
>> micropost.user
=> #<User id: 1, name: "Michael Hartl", email: "[email protected]",
created_at: "2014-07-21 02:01:31", updated_at: "2014-07-21 02:01:31">
>> exit
我在这段代码的最后一行加上了 exit
,告诉你如何退出终端。在大多数系统中也可以按 Ctrl-D 键。[11] 我们使用 first_user.microposts
获取这个用户发布的微博。Active Record 会自动返回 user_id
和 first_user
的 ID(1
)相同的所有微博。在第 11 章和第 12 章中,我们会更深入地学习关联的这种用法。
2.3.4 继承体系
接下来简要介绍 Rails 中控制器和模型的类继承。 如果你有面向对象编程(Object-oriented Programming,简称 OOP)的经验,能更好地理解这些内容。如果你未接触过 OOP 的话,可以跳过这一节。一般来说,如果你不熟悉类的概念(4.4 节中会介绍),我建议你以后再回过头来读这一节。
我们先介绍模型的继承结构。对比一下代码清单 2.13 和代码清单 2.14 ,可以看出,User
和 Micropost
都(通过 <
符号)继承自 ActiveRecord::Base
,这是 Active Record 为模型提供的基类。图 2.16 列出了这种继承关系。通过继承 ActiveRecord::Base
,模型对象才能与数据库通讯,才能把数据库中的列看做 Ruby 中的属性,等等。
代码清单 2.13:User
类中的继承
app/models/user.rb
class User < ActiveRecord::Base
.
.
.
end
代码清单 2.14:Mcropost
类中的继承
app/models/micropost.rb
class Micropost < ActiveRecord::Base
.
.
.
end
图 2.16:用户模型和微博模型中的继承体系
控制器的继承结构稍微复杂一些。对比代码清单 2.15 和代码清单 2.16,可以看出,UsersController
和 Microposts Controller
都继承自 ApplicationController
。如代码清单 2.17 所示,ApplicationController
继承自 ActionController::Base
。ActionController::Base
是 Rails 中 Action Pack 库为控制器提供的基类。这些类之间的关系如图 2.17 所示。
代码清单 2.15:UsersController
类中的继承
app/controllers/users_controller.rb
class UsersController < ApplicationController
.
.
.
end
代码清单 2.16:MicropostsController
类中的继承
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
.
.
.
end
代码清单 2.17:ApplicationController
类中的继承
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
.
.
.
end
图 2.17:UsersController
和 MicropostsController
中的继承体系
和模型的继承类似,通过继承 ActionController::Base
,UsersController
和 MicropostsController
获得了很多功能。例如,处理模型对象的能力,过滤输入的 HTTP 请求,以及把视图渲染成 HTML。Rails 应用中的所有控制器都继承 ApplicationController
,所以其中定义的规则会自动运用于应用中的的每个动作。例如,8.4 节会介绍如何在 ApplicationController
中引入辅助方法,为整个应用的所有控制器都加上登录和退出功能。
2.3.5 部署这个玩具应用
完成微博资源之后,是时候把代码推送到 Bitbucket 的仓库中了:
$ git status
$ git add -A
$ git commit -m "Finish toy app"
$ git push
通常情况下,你应该经常做一些很小的提交,不过对于本章来说,最后做一次大提交也无妨。
然后,你也可以按照 1.5 节介绍的方法,把这个应用部署到 Heroku:
$ git push heroku
执行这个命令的前提是,你已经按照 2.1 节中的说明创建了 Heroku 应用。否则,应该先执行 hreoku create
,然后再执行 git push heroku master
。
然后还要执行下面的命令迁移生产环境的数据库,这样应用才能使用数据库:
$ heroku run rake db:migrate
这个命令会按照用户和微博的数据模型更新 Heroku 中的数据库。迁移数据库之后,就可以在生产环境中使用这个应用了,如图 2.18 所示,而且这个应用使用 PostgreSQL 数据库。
图 2.18:运行在生产环境中的玩具应用