こんちには、opiyoです。
人生の生き残りをかけて始めた「Railsブートキャンプ」ですが、今日はRailsチュートリアル第2章をやっていこうと思います。
第1章をまとめたのはこちらからどうぞ。
モデル(Model)の作り方
ユーザーモデル
テーブル:users
カラム:id:integer, name:string, email:string
マイクロポストモデル
テーブル:microposts
カラム:id:integer, content:text, user_id:integer
user_idを使って、1人のユーザーに対して複数のマイクロポストが関連付けるという構図になる。
テーブル名は複数形になるので注意!
scaffold
作成、一覧、編集、削除ができる機能を簡単に作ってくれる
$ rails g scaffold User name:string email:string # テーブル名は単数系だからね! Expected string default value for '--jbuilder'; got true (boolean) invoke active_record create db/migrate/20170614093157_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml invoke resource_route route resources :users invoke scaffold_controller create app/controllers/users_controller.rb invoke erb create app/views/users create app/views/users/index.html.erb create app/views/users/edit.html.erb create app/views/users/show.html.erb create app/views/users/new.html.erb create app/views/users/_form.html.erb invoke test_unit create test/controllers/users_controller_test.rb invoke helper create app/helpers/users_helper.rb invoke test_unit invoke jbuilder create app/views/users/index.json.jbuilder create app/views/users/show.json.jbuilder invoke assets invoke coffee create app/assets/javascripts/users.coffee invoke scss create app/assets/stylesheets/users.scss invoke scss create app/assets/stylesheets/scaffolds.scss
migrate
データベースを更新し、usersデータモデルを作成します。
db/migrate
ディレクトリにあるファイルを見て何をするか判断している。
$ rails db:migrate == 20170614093157 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0019s == 20170614093157 CreateUsers: migrated (0.0022s) =============================
name
とemail
を持った、users
テーブルを作るってことが分かりますね。
class CreateUsers < ActiveRecord::Migration[5.0] def change create_table :users do |t| t.string :name t.string :email t.timestamps end end end
2.2.1 演習
<問題1> CSSを知っている読者へ: 新しいユーザーを作成し、ブラウザのHTMLインスペクター機能を使って「User was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
<回答1>場所は、bodyタグ直下にあってこんな感じ
<p id="notice">User was successfully created.</p>
リロードすると、pタグは残るが中身のメッセージが非表示になる
<p id="notice"></p>
<問題2> emailを入力せず、名前だけを入力しようとした場合、どうなるでしょうか?
<回答2> 名前のみ登録される
<問題3> 「@example.com」のような間違ったメールアドレスを入力して更新しようとした場合、どうなるでしょうか?
<回答3> 問題なく登録される
<問題4> 上記の演習で作成したユーザーを削除してみてください。ユーザーを削除したとき、Railsはどんなメッセージを表示するでしょうか?
<回答4>
User was successfully destroyed.
を表示する。
MVC(Model-View-Controller)の動き
「/users にあるindexページをブラウザで開く」という操作を行った時、どのように内部では処理が動くのか。
これは、Railsチュートリアルの方を見てください。図もあり説明もありなので分かりやすいです。
https://railstutorial.jp/chapters/toy_app?version=5.0#sec-mvc_in_action
2.2.2 演習
<問題1> 図 2.11を参考にしながら、/users/1/edit というURLにアクセスしたときの振る舞いについて図を書いてみてください。
<回答1> routes → コントローラーのeditアクション → モデル問い合わせなし → edit.html生成 → コントローラーがhtml受け取り → ブラウザへ返す
<問題2> 図示した振る舞いを見ながら、Scaffoldで生成されたコードの中でデータベースからユーザー情報を取得しているコードを探してみてください。
<回答2>
@users = User.all
<問題3> ユーザーの情報を編集するページのファイル名は何でしょうか?
<回答3>edit.html.erb
2.3.1 演習
<問題1> CSSを知っている読者へ: 新しいマイクロポストを作成し、ブラウザのHTMLインスペクター機能を使って「Micropost was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
<回答1>
2.2.1 演習
とほとんど同じ。
<問題2> マイクロポストの作成画面で、ContentもUserも空にして作成しようとするどうなるでしょうか?
<回答2> 問題なく登録できる。
<問題3> 141文字以上の文字列をContentに入力した状態で、マイクロポストを作成しようとするとどうなるでしょうか? (ヒント: WikipediaのRubyの記事にある1段落目がちょうど150文字程度ですが、どうなりますか?)
<回答3> 問題なく登録できる
<問題4> 上記の演習で作成したマイクロポストを削除してみましょう。
<回答4> 問題なく削除できる。
入力チェック(バリデーション)
モデルのファイルmicropost.rb
で最大140文字までの入力チェックを付ける
class Micropost < ApplicationRecord validates :content, length: { maximum: 140 } end
必須入力を付ける場合はpresence: true
を付ける
class Micropost < ApplicationRecord validates :content, length: { maximum: 140 }, presence: true end
2.3.2演習
<問題1> 先ほど2.3.1.1の演習でやったように、もう一度Contentに141文字以上を入力してみましょう。どのように振る舞いが変わったでしょうか?
<回答1>
Content is too long (maximum is 140 characters)
というエラーメッセージが表示される
<問題2> CSSを知っている読者へ: ブラウザのHTMLインスペクター機能を使って、表示されたエラーメッセージを調べてみてください。
<回答2>
画面上部にエラーメッセージが表示される
<div id="error_explanation"> <h2>1 error prohibited this micropost from being saved:</h2> <ul> <li>Content is too long (maximum is 140 characters)</li> </ul> </div>
Content
の入力欄が赤枠になる
<div class="field"> <div class="field_with_errors"><label for="micropost_content">Content</label></div> <div class="field_with_errors"><textarea name="micropost[content]" id="micropost_content">12345678901234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 </textarea></div> </div>
1対nの関連付け
これはRailsチュートリアルを参照してください。
https://railstutorial.jp/chapters/toy_app?version=5.0#sec-demo_user_has_many_microposts
簡単に書くと
- 1の方に
has_many
を書いて、nを複数形で指定する - nの方に
belongs_to
を書いて、1を単数系で指定する
Railsコンソール
データベースへアクセスし色々できる
$ rails c exit # 終了
2.3.3 演習
<問題1> ユーザーのshowページを編集し、ユーザーの最初のマイクロポストを表示してみましょう。同ファイル内の他のコードから文法を推測してみてください (コラム 1.1で紹介した技術の出番です)。うまく表示できたかどうか、/users/1 にアクセスして確認してみましょう。
<回答1>
users/show.html.erb
に追加
<p> <strong>Content:</strong> <%= @user.microposts.first.content %> </p>
<問題2>リスト 2.16は、マイクロポストのContentが存在しているかどうかを検証するバリデーションです。マイクロポストが空でないことを検証できているかどうか、実際に試してみましょう (図 2.16のようになっていると成功です)。
<回答2>
app/models/micropost.rb
を修正
class Micropost < ApplicationRecord belongs_to :user validates :content, length: { maximum: 140 }, presence: true end
<問題3> リスト 2.17のFILL_INとなっている箇所を書き換えて、Userモデルのnameとemailが存在していることを検証してみてください (図 2.17)。
<回答3>
app/models/user.rb
を修正
class User < ApplicationRecord has_many :microposts validates :name, presence: true validates :email, presence: true end
継承について
Railsチュートリアルを参照
https://railstutorial.jp/chapters/toy_app?version=5.0#sec-inheritance_hierarchies
- モデルクラスの場合は、
ApplicationRecord
→ActiveRecord::Base
を継承している - データベースに接続する処理やデータベースを扱える処理は
ActiveRecord::Base
側で定義されている - これによって、新しく作ったクラスでデータベースに接続する処理やデータベースを扱える処理を書かなくて色々できる
- モデルだけじゃなくコントローラーも同じようになっている
- コントローラーの場合は
ApplicationController
を継承している
2.3.4 演習
<問題1>Applicationコントローラのファイルを開き、ApplicationControllerがActionController::Baseを継承している部分のコードを探してみてください。
<回答1>
class ApplicationController < ActionController::Base
<問題2>ApplicationRecordがActiveRecord::Baseを継承しているコードはどこにあるでしょうか? 先ほどの演習を参考に、探してみてください。ヒント: コントローラと本質的には同じ仕組みなので、app/modelsディレクトリ内にあるファイルを調べてみると…?)
<回答2>
app/models/application_record.rb
のclass ApplicationRecord < ActiveRecord::Base
herokuへのデプロイ
手順は1章と同じですが、今回はDBを新たに作ったのでマイグレーションをする必要があります
$ git add . $ git commit -m "終わりー" $ git push origin master $ git push heroku $ heroku run rails db:migrate $ heroku open
その他メモ
- 間違えて
scaffold
しちゃっ時は削除できる
$ rails destroy scaffold Micropost Running via Spring preloader in process 4158 Expected string default value for '--jbuilder'; got true (boolean) invoke active_record remove db/migrate/20170614102406_create_microposts.rb remove app/models/micropost.rb invoke test_unit remove test/models/micropost_test.rb remove test/fixtures/microposts.yml invoke resource_route route resources :microposts invoke scaffold_controller remove app/controllers/microposts_controller.rb invoke erb remove app/views/microposts remove app/views/microposts/index.html.erb remove app/views/microposts/edit.html.erb remove app/views/microposts/show.html.erb remove app/views/microposts/new.html.erb remove app/views/microposts/_form.html.erb invoke test_unit remove test/controllers/microposts_controller_test.rb invoke helper remove app/helpers/microposts_helper.rb invoke test_unit invoke jbuilder remove app/views/microposts remove app/views/microposts/index.json.jbuilder remove app/views/microposts/show.json.jbuilder invoke assets invoke coffee remove app/assets/javascripts/microposts.coffee invoke scss remove app/assets/stylesheets/microposts.scss invoke scss
- 新しく綺麗な状態のDBを作りたいとき DB削除 → DB作成 → migrationを一発でやってくれる
$ rails db:migrate:reset Dropped database 'db/development.sqlite3' Database 'db/test.sqlite3' does not exist Created database 'db/development.sqlite3' Created database 'db/test.sqlite3' == 20170614093157 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0021s == 20170614093157 CreateUsers: migrated (0.0023s) ============================= == 20170614102713 CreateMicroposts: migrating ================================= -- create_table(:microposts) -> 0.0008s == 20170614102713 CreateMicroposts: migrated (0.0009s) ========================