多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 2.3 微博资源 我们已经生成并浏览了用户资源,现在要生成微博资源。阅读本节时,我推荐你和 [2.2 节](#the-users-resource)对比一下。你会发现两个资源在很多方面都是一致的。通过这样重复生成资源,我们可以更好地理解 Rails 中的 REST 架构。在这样的早期阶段看一下用户资源和微博资源的相同之处,也是本章的主要目的之一。 ## 2.3.1 概览微博资源 和用户资源一样,我们使用 `rails generate scaffold` 命令生成微博资源的代码,不过这一次要实现[图 2.3](#fig-demo-micropost-model) 中的数据模型:[[8](#fn-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 节](#the-users-resource)一样,我们要执行迁移,更新数据库,使用新建的数据模型: ``` $ bundle exec rake db:migrate == CreateMicroposts: migrating =============================================== -- create_table(:microposts) -> 0.0023s == CreateMicroposts: migrated (0.0026s) ====================================== ``` 现在我们就可以使用类似 [2.2.1 节](#a-user-tour)中介绍的方法来创建微博了。你可能猜到了,脚手架还会更新 Rails 的路由文件,为微博资源加入一条规则,如[代码清单 2.8](#listing-demo-microposts-resource) 所示。[[9](#fn-9)]和用户资源类似,`resources :micropsts` 把微博相关的 URL 映射到 `MicropostsController`,如[表 2.3](#table-demo-restful-microposts) 所示。 ##### 代码清单 2.8:Rails 的路由,有一条针对微博资源的新规则 config/routes.rb ``` Rails.application.routes.draw do resources :microposts resources :users . . . end ``` 表 2.3:[代码清单 2.8](#listing-demo-microposts-resource) 中微博资源生成的符合 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](#listing-demo-microposts-controller) 所示。注意,除了把 `UsersController` 换成 `MicropostsController` 之外,这段代码和[代码清单 2.4](#listing-demo-users-controller) 没什么区别。这说明了两个资源在 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](http://localhost:3000/microposts/new))输入一些内容,发布一篇微博,如[图 2.12](#fig-demo-new-micropost) 所示。 既然已经打开这个页面了,那就多发布几篇微博,并且确保至少把一篇微博的 `user_id` 设为 `1`,把微博赋予 [2.2.1 节](#a-user-tour)中创建的第一个用户。结果应该和[图 2.13](#fig-demo-micropost-index) 类似。 ![demo new micropost 3rd edition](https://box.kancloud.cn/2016-05-11_5732bcb5624cb.png)图 2.12:发布微博的页面![demo micropost index 3rd edition](https://box.kancloud.cn/2016-05-11_5732bcf16fc97.png)图 2.13:微博索引页([/microposts](http://localhost:3000/microposts)) ## 2.3.2 限制微博内容的长度 如果要称得上“微博”这个名字,就要限制内容的长度。在 Rails 中实现这种限制很简单,使用验证(validation)功能即可。要限制微博的长度最大字数为 140 个字符(就像 Twitter 一样),我们可以使用长度验证。在文本编辑器或 IDE 中打开 `app/models/micropost.rb`,写入[代码清单 2.10](#listing-demo-length-validation) 中的代码。 ##### 代码清单 2.10:限制微博的长度最多为 140 个字符 app/models/micropost.rb ``` class Micropost < ActiveRecord::Base validates :content, length: { maximum: 140 } end ``` 这段代码看起来可能很神秘,我们会在 [6.2 节](chapter6.html#user-validations)详细介绍验证。如果我们在发布微博的页面输入超过 140 个字符的内容,就能看出这个验证的作用了。如[图 2.14](#fig-micropost-length-error) 所示,Rails 会渲染错误信息,提示微博的内容太长了。([7.3.3 节](chapter7.html#signup-error-messages)会详细介绍错误信息。) ![micropost length error 3rd edition](https://box.kancloud.cn/2016-05-11_5732bcf185ff4.png)图 2.14:发布微博失败时显示的错误消息 ## 2.3.3 一个用户拥有多篇微博 Rails 最强大的功能之一是,可以在不同的数据模型之间建立关联(association)。对本例中的用户模型而言,每个用户可以拥有多篇微博。我们可以更新用户模型(参见[代码清单 2.11](#listing-demo-user-has-many-microposts))和微博模型(参见[代码清单 2.12](#listing-demo-micropost-belongs-to-user))的代码实现这种关联。 ##### 代码清单 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](#fig-micropost-user-association) 中的图标表示出来。因为 `microposts` 表中有 `user_id` 这一列,所以 Rails(通过 Active Record)能把微博和各个用户关联起来。 ![micropost user association](https://box.kancloud.cn/2016-05-11_5732bcf19aab3.png)图 2.15:微博和用户之间的关联 在[第 11 章](chapter11.html#user-microposts)和[第 12 章](chapter12.html#following-users),我们会使用微博和用户之间的关联显示一个用户的所有微博,还会生成一个和 Twitter 类似的微博列表。现在,我们可以在控制台(console)中检查用户与微博之间的关联。控制台是和 Rails 应用交互很有用的工具。在命令行中执行 `rails console` 命令,启动控制台。然后输入 `User.first`,从数据库中读取第一个用户,并把得到的数据赋值给 `first_user` 变量:[[10](#fn-10)] ``` $ rails console >> first_user = User.first => #<User id: 1, name: "Michael Hartl", email: "michael@example.org", 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: "michael@example.org", created_at: "2014-07-21 02:01:31", updated_at: "2014-07-21 02:01:31"> >> exit ``` 我在这段代码的最后一行加上了 `exit`,告诉你如何退出终端。在大多数系统中也可以按 Ctrl-D 键。[[11](#fn-11)] 我们使用 `first_user.microposts` 获取这个用户发布的微博。Active Record 会自动返回 `user_id` 和 `first_user` 的 ID(`1`)相同的所有微博。在[第 11 章](chapter11.html#user-microposts)和[第 12 章](chapter12.html#following-users)中,我们会更深入地学习关联的这种用法。 ## 2.3.4 继承体系 接下来简要介绍 Rails 中控制器和模型的类继承。 如果你有面向对象编程(Object-oriented Programming,简称 OOP)的经验,能更好地理解这些内容。如果你未接触过 OOP 的话,可以跳过这一节。一般来说,如果你不熟悉类的概念([4.4 节](chapter4.html#ruby-classes)中会介绍),我建议你以后再回过头来读这一节。 我们先介绍模型的继承结构。对比一下[代码清单 2.13](#listing-demo-user-class) 和[代码清单 2.14](#listing-demo-micropost-class) ,可以看出,`User` 和 `Micropost` 都(通过 `&lt;` 符号)继承自 `ActiveRecord::Base`,这是 Active Record 为模型提供的基类。[图 2.16](#fig-demo-model-inheritance) 列出了这种继承关系。通过继承 `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 ``` ![demo model inheritance](https://box.kancloud.cn/2016-05-11_5732bcf1afbb0.png)图 2.16:用户模型和微博模型中的继承体系 控制器的继承结构稍微复杂一些。对比[代码清单 2.15](#listing-demo-users-controller-class) 和[代码清单 2.16](#listing-demo-microposts-controller-class),可以看出,`UsersController` 和 `Microposts Controller` 都继承自 `ApplicationController`。如[代码清单 2.17](#listing-toy-application-controller-class) 所示,`ApplicationController` 继承自 `ActionController::Base`。`ActionController::Base` 是 Rails 中 Action Pack 库为控制器提供的基类。这些类之间的关系如[图 2.17](#fig-demo-controller-inheritance) 所示。 ##### 代码清单 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 ``` ![demo controller inheritance](https://box.kancloud.cn/2016-05-11_5732bcf1c0398.png)图 2.17:`UsersController` 和 `MicropostsController` 中的继承体系 和模型的继承类似,通过继承 `ActionController::Base`,`UsersController` 和 `MicropostsController` 获得了很多功能。例如,处理模型对象的能力,过滤输入的 HTTP 请求,以及把视图渲染成 HTML。Rails 应用中的所有控制器都继承 `ApplicationController`,所以其中定义的规则会自动运用于应用中的的每个动作。例如,[8.4 节](chapter8.html#remember-me)会介绍如何在 `ApplicationController` 中引入辅助方法,为整个应用的所有控制器都加上登录和退出功能。 ## 2.3.5 部署这个玩具应用 完成微博资源之后,是时候把代码推送到 Bitbucket 的仓库中了: ``` $ git status $ git add -A $ git commit -m "Finish toy app" $ git push ``` 通常情况下,你应该经常做一些很小的提交,不过对于本章来说,最后做一次大提交也无妨。 然后,你也可以按照 [1.5 节](chapter1.html#deploying)介绍的方法,把这个应用部署到 Heroku: ``` $ git push heroku ``` 执行这个命令的前提是,你已经按照 [2.1 节](#planning-the-application)中的说明创建了 Heroku 应用。否则,应该先执行 `hreoku create`,然后再执行 `git push heroku master`。 然后还要执行下面的命令迁移生产环境的数据库,这样应用才能使用数据库: ``` $ heroku run rake db:migrate ``` 这个命令会按照用户和微博的数据模型更新 Heroku 中的数据库。迁移数据库之后,就可以在生产环境中使用这个应用了,如[图 2.18](#fig-toy-app-production) 所示,而且这个应用使用 PostgreSQL 数据库。 ![toy app production](https://box.kancloud.cn/2016-05-11_5732bcf1d899f.png)图 2.18:运行在生产环境中的玩具应用