noggy’s blog

自分用の備忘録です。。。

質問投稿サイトの作成(1/3)

ここでは,投稿フォームから新規の質問ができるようにする質問する人の名前(name),質問のタイトル(title),質問(content)を入力できるようにする

108.新規railsプロジェクトの作成

例えば,rails_projectsというフォルダを作成し,RubyRailsのバージョンを確認.

$ cd rails_projects
$ ruby -v
$ rails  -v
Rails 5.2.x
$ rails _5.2.x_ new qanda
cd qanda

追加設定

Gemfileに以下を追加

gem 'sqlite3', '~> 1.3.6'  #追加

bundle install

データベースの作成

$ rails db:create

'db/development.sqlite3'
'db/test.sqlite3'
が生成される

109.Questionsコントローラーの作成

質問を管理するコントローラーを作成

$ rails g controller questions index show new edit

すると,コントローラにindex,show,new,editアクションがつくられ,同時にviewもつくられる.

Running via Spring preloader in process 4071
      create  app/controllers/questions_controller.rb
       route  get 'questions/index'
              get 'questions/show'
              get 'questions/new'
              get 'questions/edit'
      invoke  erb
      create    app/views/questions
      create    app/views/questions/index.html.erb
      create    app/views/questions/show.html.erb
      create    app/views/questions/new.html.erb
      create    app/views/questions/edit.html.erb
      invoke   test_unit
      create    test/controllers/questions_controller_test.rb
      invoke   helper
      create    app/helpers/questions_helper.rb
      invoke    test_unit
      invoke   assets
      invoke   coffee
      create    app/assets/javascripts/questions.coffee
      invoke    scss
      create    app/assets/stylesheets/questions.scss

110.Questionモデルの作成

質問を保存するquestionモデルを作成

rails g model question name:string title:string content:text

questionモデルファイルと同時に、このモデルが担当するテーブルを作成するためのマイグレーションファイルが自動で作成される.

Running via Spring preloader in process 4209
      invoke  active_record
      create   db/migrate/XX_create_questions.rb
      create   app/models/question.rb
      invoke   test_unit
      create   test/models/question_test.rb
      create   test/fixtures/questions.yml

作成されたマイグレーションファイルは,db/migrateフォルダに XX_create_questions.rb というファイル名でできている.

class CreateQuestions < ActiveRecord::Migration[5.2]
  def change
    create_table :questions do |t|
      t.string :name
      t.string :title
      t.text :content

      t.timestamps
    end
  end
end

スぺルミスなどがあれば修正する.問題がなければ,次のマイグレーションでDBに反映

rails db:migrate

== XX CreateQuestions: migrating ==================================
-- create_table(:questions)
   -> 0.0063s
== XX CreateQuestions: migrated (0.0065s) =========================

すると,questionsテーブルが作成されたことが分かる.

DB構造の確認のため

rails dbconsole   
sqlite> .schema テーブル名(今はquestions)

sqlite> .q

111.ルーティングの設定

config/routes.rb に自動でルーティングが作成されているので,削除かコメントアウト
次の一般的なアプリケーションに必要なルーティングで自動で用意してくれるresourcesメソッドを使う

Rails.application.routes.draw do
  get 'questions/index' #削除
  get 'questions/show' #削除
  get 'questions/new' #削除
  get 'questions/edit' #削除
  resources :questions #追加
end

複数形に注意!

112.rootメソッドの設定

config/routes.rbに以下を追加

root 'questions#index' #追加
resources :questions

rootメソッドを使う場合はスラッシュではなくハッシュに注意!

113.質問一覧ページの作成

ここでは質問の「ID」と「タイトル」を一覧表示する.

コントローラ

 def index
  @questions = Question.all
 end

view

以下を追加

<h2>Questions</h2>
  <div class="row">
    <div class="col-md-12">
      <table class="table table-striped">
        <thead class="thead-light">
	  <tr>
	    <th>ID</th>
	    <th>Title</th>
	    <th>Menu</th>
	  </tr>
	</thead>
      <tbody>
      <% @questions.each do |question| %>
        <tr>
	  <td><%= question.id %></td>
	  <td><%= question.title %></td>
	  <td>[Edit][Delete]</td>
	</tr>
       <% end %>
      </tbody>
    </table>
  </div>
</div>

f:id:nogicchi:20210918141120p:plain
投稿がないため、ヘッドの行だけの表示だけになり,かなり味気ない

114.シードファイルを使った初期データの挿入

questionsテーブルにはデータが何も入っていないので,初期データをいれる。db/seeds.rbに以下を追加

  Question.create(id: 1, name: "Test name 1", title: "Test question 1", content: "Test content 1" )
  Question.create(id: 2, name: "Test name 2", title: "Test question 2", content: "Test content 2" )
  Question.create(id: 3, name: "Test name 3", title: "Test question 3", content: "Test content 3" )

DBに反映

rails db:seed

DBに反映できているかチェック

rails c

> Question.all

f:id:nogicchi:20210918141743p:plain

115.Bootstrapの導入

フロントエンド開発を効率的に行われるCSSフレームワーク.無償bootstrapはgemを使って導入できる.
bootstrap4を使う.詳細はGitHub参照

Gemfile

  #一番下などに追加
  gem 'bootstrap', '~> 4.1.1'   
  gem 'jquery-rails', '~> 4.3.1'

bundle install

application.css

application.cssの拡張子をcssからscssに変換(questions.cssも自動的にscssになる)

mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss

scssはsass を記述するためのファイルの拡張子.
sassはcssを便利に実装するためのスクリプト言語
applications.scssに以下を追加

@import "bootstrap";

application.js

GitHubの「bootstrap-rubygems」の下,「Add Bootstrap dependencies and Bootstrap to your applications.js:」 の3行をコピーし,assets/javascripts/application.jsファイルの//= require_treeの前にはりつけ

//= require rails-ujs
//= require activestorage
//= require turbolinks
<!-- 追加ココから -->
//= require jquery3
//= require popper
//= require bootstrap-sprockets
<!-- ココまで -->
//= require_tree .

application.htm.erb

一覧表示を中央ぞろえにするため以下を追加

  <body>
    <div class="container">  <!-- 追加 -->
      <%= yield %>
    </div>  <!-- 追加 -->
  </body>

f:id:nogicchi:20210918141851p:plain
かなり見栄えがよくなった

116.新規質問の投稿

新規質問の投稿フォームを作る。questions/new.html.erbファイルに以下を追加

<div>
  <div class="col-md-4 offset-md-4">
    <h2 class="text-center">New question</h2>
      <%= form_with model: @question, local:true do |f| %>
        <div class="form-group">
	  <label>Name</label>
	  <%= f.text_field :name, class: "form-control" %>
	</div>
	<div class="form-group">
	  <label>Title</label>
	  <%= f.text_field :title, class: "form-control" %>
        </div>
	<div class="form-group">
	  <label>Content</label>
	  <%= f.text_field :content, class: "form-control" %>
        </div>
	<div class="text-center">
        <%= f.submit "Save", class: "btn btn-primary" %>
      </div>
    <% end %>
  </div>
</div>

最初と最後の< div >は無くてもよい?

form_with

form_with, text_field, submitという各メソッドは,ホームヘルパーで,開発者が煩雑になりがちなhtmlをかかなくても,フォームに関するhtmlを自動で生成してくれる.form_withはrails5.1から導入された新しいメソッド.form_forとform_tagの利用は非推奨form_withで生成されたフォームはデフォルトで非同期通信で行われるのだが,local:trueで非同期通信を無効にしている。

form_with model: @モデル名 do |f|

formは渡されたものによって,行うHTTPメソッドとアクションを判断してくれる.モデルに入っているものが「①新規に作られたものの場合」と「②既存のもののを呼び出した場合」で処理が変わる.①新規に作られたものが渡された場合(今の場合) newアクションから渡されたものがこれにあたる.Railsは渡されたモデル(@question)の中身が空であるであることから,createメソッドを呼び出してくれる.また,form_withで生成されたフォームはデフォルトで非同期通信で行われるが,local:trueで非同期通信を無効にしている.

Bootstraps
  • Bootstrapでは横幅を12分割したグリッドシステムを採用
  • class = “container”(固定枠)または”container-fluid”(流動枠)の中に書く
  • col-md-12: 「.col-画面幅-グリッド数」 md:「画面幅がMidium」
  • 例えば、col-md-4と記述すると,画面幅がMiduim以上のときはグリッドを4個持つカラムを生成

グリッドに関しては次のサイトが分かりやすい
websae.net

f:id:nogicchi:20210918142043p:plain
コントローラ側を実装していないので,「Save」を押しても何も反応はない

新規質問のコントローラ

 def new
  @question = Question.new
 end

webページの入力欄にフォーカスをあてて,右クリック→「検証」でhtmlが見られるホームヘルパーを使うと楽ができるが,慣れるまではホームヘルパーによって,どんなhtmlタグが生成されているか確認

117.投稿データの保存

  def new
    @question = Question.new
  end
  #追加ココから
  def create
    @question = Question.new(question_params)
    if @question.save
      redirect_to root_path, notice: "Success!"
    else
      flash[:alert] ="Save error!"
      render :new
    end
  end
  #ココまで

  #一番下に追加
  private 
    def question_params
      params.require(:question).permit(:name, :title, :content)
    end
  #ココまで
end

saveが成功すれば,redirect_to で root_path(一覧表示)にとばす.
失敗すれば,renderでnew(新規質問入力)にとばす.
ちなみに、Prefixとコントローラのアクションは次の関係

        Prefix Verb   URI Pattern                  Controller#Action
           root GET    /                            questions#index
      questions GET    /questions(.:format)         questions#index
                POST   /questions(.:format)         questions#create
   new_question GET    /questions/new(.:format)     questions#new

render action: :newとかいてもよい.renderに関しては次が分かりやすい
pikawaka.com

f:id:nogicchi:20210918142438p:plain
実際に投稿してみる

118.ストロングパラメーター

フォームから送られてきたデータは信頼できないので,question_paramasメソッドを使って指定したパラメータだけを登録対象とする.

フォームから送られてきた:questionのデータのうち:name, :title, :contentを採用.

paramsの中身に関して,ここではモデルを渡しているので,params[:question][:name]と階層構造になっていることに注意!それによりストロングパラメータの設定で,.require(:question)のように一度仲介をはさむ必要がある。

119.paramsに入るデータをデバッグ

gem 'byebug'

を追加し、

bundle install

プログラムの止めたいところにbyebugを入力.
コマンドプロンプトで,フォームから送られた変数に入っている値を確認できる(byebug)params.
nextを入力してEnterを押すと次の行にいく.
quitで終了

120.バリデートの追加

class Question < ApplicationRecord
  validates :name, presence: true
  validates :title, presence: true
  validates :content, presence: true
end

121.エラーメッセージの表示

  <body>
    <div class="container">
   <!-- 追加ココから -->
      <% if flash[:notice] %>
        <p class = "text-success"><%= flash[:notice] %></p>
      <% end %>
      <% if flash[:alert] %>
        <p class="text-danger"><%= flash[:alert] %></p>
      <% end %>
  <!-- ココまで -->
      <%= yield %>
    </div>
  </body>

入力画面へのリンクをつくっていないので,rails sしてからにURLに/questions/newを追加入力できることが分かる.