2021年10月29日金曜日

「学生 - 科目」選択アプリのモデル

学生が科目選択してホゲホゲするアプリの雛形である.手順を以下に示す.なお「Ruby on Railsにおける多対多モデルの実装」も参照のこと.

準備

まず,Railsでdeviseを用いてユーザ管理するところ,ログイン後にマイページを表示させるようにして,自分以外のマイページにはアクセスできないようにする,そのような機能を備えたところまで作り込んだテンプレートを GitHub にアップロードしておいたので,そこからダウンロードすると,簡単だろう.

$ git clone https://github.com/iiojun/ulexample.git

Railsの環境を整える.なお,Rubyのバージョンが違う!とエラーになるときは,Gemfile を弄ってバージョンを揃えるか,rbenv を使っている人は適切なバージョンをインストールしておくこと.

$ bundle install

...

$ bin/rails webpacker:install

...

$ bin/rails db:create

...

$ bin/rails db:migrate

...

以上の手続きをして,環境を整える.コンソールを1つサーバ起動用に用意して,サーバはずっと起動させておくとよい.

$ bin/rails s

ブラウザから localhost:3000/mypage/ にアクセスすると,Mypage#home が表示されるはず.sign up のためのリンクがあるのでそこをクリックしてユーザアカウントをsign up,ログインできるかどうか確認せよ.ログインできると Mypage#show が表示されるはずである.

Lectureの導入

Lecture モデルを作成し,Lecture コントローラの雛形を作る.Lecture は,タイトルと教員と教室のデータを持つものとする.実際には曜日だの何限だのと,もっと多くの情報を入れるべき(説明のために単純化している).

$ bin/rails g model lecture title:string teacher:string room:string
...
$ bin/rails db:migrate
...
$ bin/rails g controller lectures index show
...

Lectureの情報にアクセスするためのルーティングを設定する.

config/routes.rb を編集し,次のようにする(get 'lectures/index' 等は削除してよい).

Rails.application.routes.draw do

  namespace :mypage do

    root to: 'home#index'

    resources :users, only: [ :show, :update, :edit ]

  end


  root to: 'lectures#index'

  resources :lectures, only: [ :index, :show ]

  #resources :user_lectures, only: [ :create, :destroy ]


  devise_for :users, controllers: {

    sessions:      'users/sessions',

    passwords:     'users/passwords',

    registrations: 'users/registrations'

  }

end

http://localhost:3000 で lectures の一覧が表示されるようになる.ただし,まだこの時点 では何も表示されない.

app/controllers/lectures_controller.rb を以下のとおりに修正する.

class LecturesController < ApplicationController

  def index

    @lectures = Lecture.all 

  end


  def show

    @lecture = Lecture.find(params[:id])

  end

end

index では全てのエントリを取得し,show では指定するエントリを取得するという基本的なパターンである.

ビューを設定する.app/views/lectures/index.html.erb を以下のとおりに修正する.

<h1>Lectures List</h1>

<ul>

  <% @lectures&.each {|lecture| %>

    <li><%= link_to lecture.title, lecture_path(lecture) %>

        <%= ": #{lecture.teacher}, @ #{lecture.room}" %></li> 

  <% } %>

</ul>

テスト用に,コンソールからデータを入れてみよう.ダミーデータなので,好きなデータを入れてよい.

$ bin/rails c

Running via Spring preloader in process 91299

Loading development environment (Rails 6.1.4.1)

irb(main):001:0> Lecture.create(title: '国際情報学演習 I', teacher: '飯尾淳', room: '501')

...

irb(main):002:1> Lecture.create(title: '国際情報学演習 III', teacher: '飯尾淳', room: '501')

...

irb(main):003:0> Lecture.create(title: 'プログラミング基礎', teacher: '飯尾淳', room: '501')

...

irb(main):004:0> exit

ブラウザで localhost:3000 にアクセスし,一覧が表示されることを確認せよ.

科目をクリックしてもそっけないテンプレートが表示されるだけなので,なんとかしよう.

app/views/lectures/show.html.erb を以下のとおりに修正する.

<h1>Lecture Info</h1>

<ul>

  <li>科目: <%= @lecture.title %></li>

  <li>教員: <%= @lecture.teacher %></li>

  <li>教室: <%= @lecture.room %></li>

</ul>

一覧に表示された科目のリンクをクリックすると,科目情報の詳細が表示されるようになった.


UserLectureの導入

User と Lecture には多対多の関係がある.そこで,User と Lecture を繋ぐ UserLecture モデルを導入する.

$ bin/rails g model user_lecture user:references lecture:references evaluation:integer

なお,UserLectureの属性として evaluation という項目を一つ用意しているが,ここは,いろいろと追加すること(今回はevaluationだけ使う).

モデル間の関連付けをする.

app/models/user.rb を次のように修正する(赤字部分を追記).

class User < ApplicationRecord

  has_many :user_lectures

  has_many :lectures, through: :user_lectures


  # Include default devise modules. Others available are:

  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable

  devise :database_authenticatable, :registerable,

         :rememberable, :validatable


  def email_required?

    false

  end

end

また app/models/lecture.rb を次のように修正する.

class Lecture < ApplicationRecord

  has_many :user_lectures

  has_many :users, through: :user_lectures

end

app/models/user_lecture.rb は,モデル作成時に references を指定して作ると自動で belongs_to が追記されるので,なにもし なくてよいが,いちおう,こうなっている.

class UserLecture < ApplicationRecord

  belongs_to :user

  belongs_to :lecture

end

モデルを作ったらデータベースに反映させる.

$ bin/rails db:migrate


UserLectureの操作(生成と削除)

コントローラを作成する.UserLectures はこのサンプルでは作って消すだけなのでとりあえず create と destroy だけだが,評価を記入する画面を作るときには show と update も必要になるかな?まあ,後からメソッドを追加すればよかろう.

$ bin/rails g controller user_lectures create destroy

ルーティングを設定する.config/routes.rb を編集し,次のようにする.

Rails.application.routes.draw do

  namespace :mypage do

    root to'home#index'

    resources :usersonly: [ :show:update:edit ]

  end


  root to'lectures#index'

  resources :lecturesonly: [ :index:show ]

  resources :user_lecturesonly: [ :create:destroy ]


  devise_for :userscontrollers: {

    sessions:      'users/sessions',

    passwords    'users/passwords',

    registrations'users/registrations'

  }

end

app/views/lectures/show.html.erb を以下のとおりに修正する(赤字部分を追記).

<h1>Lecture Info</h1>

<ul>

  <li>科目: <%= @lecture.title %></li>

  <li>教員: <%= @lecture.teacher %></li>

  <li>教室: <%= @lecture.room %></li>

</ul>


<% if current_user %> 

  <% if @registered %>

    <%= link_to '登録をやめる', user_lecture_path(@registered),                          method:deleteclass'btn btn-danger' %>

  <% else %>

    <%= link_to '登録する', user_lectures_path(lecture: @lecture),

                method: :post, class: 'btn btn-primary' %>

  <% end %>

<% end %>

<br /><br />

<%= link_to '科目一覧へ戻る', lectures_path %>

app/controllers/lectures_controller.rb に一行(赤字部分)追記する.画面の都合上,下記では折り返しているが,実際には一行でよい.

class LecturesController < ApplicationController

  def index

    @lectures = Lecture.all

  end


  def show

    @lecture = Lecture.find(params[:id])

    @registered = UserLecture

       .find_by(user: current_user, lecture: @lecture)

  end

end

app/controllers/user_lectures_controller.rb を次のように作る.

class UserLecturesController < ApplicationController

  def create

    lecture = Lecture.find(params[:lecture])

    UserLecture.create(user: current_user, lecture: lecture) 

    redirect_to lecture_path lecture

  end


  def destroy

    ul = UserLecture.find(params[:id])

    UserLecture.destroy(ul.id)

    redirect_to lecture_path ul.lecture

  end

end

ここまでの設定で,UserLecture のデータを生成,削除できるようになった.ログインした状態で科目の詳細ページにアクセスすると「登録する」ボタンができているはず.

「登録する」ボタンを押してみよう.UserLecture#create が発動され,UserLecture データが登録される.処理終了後,redirect_to が設定されているので,再度,Lecture#show に戻るが,そのときは既に登録済み(@registered が nil ではない)なので,「登録をやめる」ボタンが表示されるようになる.

「登録をやめる」を押すと UserLecture#destroy の処理を経た結果,同様の動作で元にもどる.[この動作の仕組みを考えて,きちんと理解すること]

登録済み科目のマイページへの表示

登録した Lecture をマイページに一覧表示させる.

 app/views/mypage/users/show.html.erb を以下のとおりに修正するだけでよい.

<h1><%= current_user.fullname %>'s MyPage</h1>

<h2>登録済み科目一覧</h2>

<ul>

  <% current_user.lectures&.each {|lecture| %>

    <li><%= link_to lecture.title, lecture_path(lecture) %>

        <%= ": #{lecture.teacher}, @ #{lecture.room}" %></li>

  <% } %>

</ul>

マイページに登録済みの科目一覧が表示されるようになった.


科目の評価ボタンの追加

では,科目を評価するインタフェースを作ろう.ログインしていたら科目の詳細ページから科目を評価できるようにする.科目評価のために,UserLectureに show と update を追加する.

ルーティングを設定する.config/routes.rb を編集し,次のようにする(赤字部分が追記した箇所).

Rails.application.routes.draw do

  namespace :mypage do

    root to'home#index'

    resources :usersonly: [ :show:update:edit ]

  end


  root to'lectures#index'

  resources :lecturesonly: [ :index:show ]

  resources :user_lectures, only[ :create, :edit, :update, :destroy ]


  devise_for :userscontrollers: {

    sessions:      'users/sessions',

    passwords    'users/passwords',

    registrations'users/registrations'

  }

end

app/views/lectures/show.html.erb に次の行(赤字部分)を追加する.

<% if current_user %>

  <% if @registered %>

    <%= link_to '登録をやめる', user_lecture_path(@registered),                          method: :delete, class: 'btn btn-danger' %>

    <%= link_to '評価する', edit_user_lecture_path(@registered), 

                class: 'btn btn-success' %>

  <% else %>

    <%= link_to '登録する', user_lectures_path(lecture: @lecture),

                method: :post, class: 'btn btn-primary' %>

  <% end %>

<% end %>

<br /><br />

<%= link_to '科目一覧へ戻る', lectures_path %>

評価ページへのボタンが追記された.ただし,コントローラを書いていないので,押すとエラーになる.


科目の評価(編集画面と更新処理)

ボタンを押すと,UserLecture#edit が呼ばれるようになる.そこで,UserLecture のコントローラを修正する.

app/controllers/user_lectures_controller.rb に次(赤字部分)を追記する.edit, updateメソッドの追記と,ストロングパラメータの処理の追加を行なった.


class
 UserLecturesController < ApplicationController

  def create

    lecture = Lecture.find(params[:lecture])

    UserLecture.create(user: current_user, lecture: lecture) 

    redirect_to lecture_path lecture

  end


  def edit

    @ul = UserLecture.find(params[:id])

  end


  def update

    ul = UserLecture.find(params[:id])

    p = user_lecture_params

    ul.evaluation = p[:evaluation]

    ul.save

    redirect_to lecture_path ul.lecture

  end


  def destroy

    ul = UserLecture.find(params[:id])

    UserLecture.destroy(ul.id)

    redirect_to lecture_path ul.lecture

  end


  private

  def user_lecture_params

    params.require(:user_lecture).permit(:evaluation)

  end

end

評価を編集するビューを書く.

app/views/user_lectures/edit.html.erb を次のように用意する.

<h1>Evaluation of Lecture</h1>

<p>

科目: <%= @ul.lecture.title %><br />

教員: <%= @ul.lecture.teacher %><br />

教室: <%= @ul.lecture.room %>

</p>

あなたの評価:

<%= form_with model: @ul do |f| %>

  <%= f.select :evaluation, [['', 1], ['やや鬼', 2], ['普通', 3],

                             ['やや神', 4], ['', 5]],

               include_blank: true %>

  <%= f.submit '送信' %>

<% end %>

これでOK.「評価する」ボタンを押すと,次のような画面になる.

プルダウンメニューから選択肢を選んで,送信すれば,登録される.「評価する」を再度押すと,先ほど評価した値が設定されていることを確認せよ.ただし,科目の登録をやめると,UserLecture そのものが削除されてしまうため,評価も再度やり直しになる点には注意のこと.

科目の評価処理の確認

また,上記の評価処理を行なった結果,データベースに評価が登録されていることを,次の手順でも確認できる.

$ bin/rails c

Running via Spring preloader in process 94649

Loading development environment (Rails 6.1.4.1)

irb(main):001:0> UserLecture.all

  UserLecture Load (0.6ms)  SELECT "user_lectures".* FROM "user_lectures"

=> 

[#<UserLecture:0x000000014223bcc8

  id: 8,

  user_id: 1,

  lecture_id: 1,

  evaluation: 5,

  created_at: Thu, 28 Oct 2021 22:16:47.783474000 UTC +00:00,

  updated_at: Thu, 28 Oct 2021 22:19:02.728075000 UTC +00:00>]

irb(main):002:0> 

コンソールで確認すると,evaluation に値がセットされていることがわかる.

とりあえずここまで.

0 件のコメント:

コメントを投稿