Rails入門5: deviseのユーザー情報を利用しよう
#01:掲示板でユーザー情報を使おう
このレッスンでは、Scaffoldで作成した1行掲示板に、deviseを使ってユーザー認証機能を追加します。まずは、どのような機能を作るのか整理しましょう。
このレッスンで作る掲示板
- deviseで作成したユーザー認証に、1行掲示板を組み合わせる
- ログインしている時だけ投稿できる
- 投稿者名をdeviseのユーザー名にする
- 自分の投稿だけ、編集・削除できる
作成手順
- ログイン時だけ投稿できる掲示板を作る
- 1行掲示板の記事に、Emailを表示する
- Userモデルに、nameカラムを追加する
- Userモデルに、ユーザー名を保存する
- 投稿時にログインユーザー名を保存
- 自分の記事だけを編集・削除
参考になるWebページ
-
[ASCII.jp:ユーザー認証でなにができるのですか?|セキュリティの素朴な疑問を解く]
http://ascii.jp/elem/000/000/436/436614/ -
[よくわかる認証と認可 | Developers.IO]
https://dev.classmethod.jp/security/authentication-and-authorization/ -
[【プログラマ英語】それ認証って意味じゃないですよ(厳密には) - Qiita]
https://qiita.com/tienlen/items/9e1b58dd173472f071c0 -
[plataformatec/devise]
https://github.com/plataformatec/devise -
[Railsのログイン認証gemのDeviseのインストール方法 - Rails Webook]
http://ruby-rails.hatenadiary.com/entry/20140801/1406907000
#02:ログイン時だけ投稿できる掲示板を作ろう
ここでは、前回のレッスンで作成したユーザー認証機能を利用して、1行掲示板へのアクセスを制御します。誰でも記事を表示できて、登録したユーザーだけが新しい記事を投稿・編集できるようにしましょう。
1行掲示板を作成する
$ cd bbs_users
$ rails g scaffold article user_id:integer content:string
$ rails db:migrate
掲示板の初期データを投入する
db/seeds.rb
Article.create(user_id: 1, content: 'hello world')
Article.create(user_id: 1, content: 'hello paiza')
Article.create(user_id: 2, content: '世界の皆さん、こんにちは')
データベースに反映するには、次のコマンドを実行する
$ rails db:seed
ログイン時に、特定のアクションだけ実行できるようにする
articles_controller.rb
before_action :authenticate_user!, only: [:new, :create, :edit, :update, :destroy]
before_action :set_article, only: [:show, :edit, :update, :destroy]
演習課題「ユーザーログインに初期ユーザーを登録する・作成する」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。
ここに、以下の初期投稿を登録してください。この時、seed.rbファイルで一括登録します。
- user_id: 1, content: '吾輩は猫である'
- user_id: 1, content: '名前はまだない'
- user_id: 2, content: 'どこで生まれたか'
- user_id: 3, content: 'とんと見当がつかぬ'
- user_id: 1, content: 'なんでも'
採点して、すべてのジャッジに正解すれば、演習課題クリアです!
模範解答1
/home/ubuntu/myblog/db/seed.rbファイルに登録するデータを記述する。
Talk.create(user_id: 1, content: '吾輩は猫である')
Talk.create(user_id: 1, content: '名前はまだない')
Talk.create(user_id: 2, content: 'どこで生まれたか')
Talk.create(user_id: 3, content: 'とんと見当がつかぬ')
Talk.create(user_id: 1, content: 'なんでも')
演習課題「ログインしているときだけ投稿・編集・削除」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成してあります。
この環境で、ログインしているユーザーだけが編集・削除できるように、talk_controllers.rbファイルを設定してください。編集・削除に対応するのは、以下のアクションです。
- edit
- update
- destroy
採点して、すべてのジャッジに正解すれば、演習課題クリアです!
模範解答1
/home/ubuntu/myblog/app/controllers/talks_controller.rbに次の内容を記述する
class TalksController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update, :destroy]
before_action :set_talk, only: [:show, :edit, :update, :destroy]
# GET /talks
# GET /talks.json
def index
@talks = Talk.all
end
# GET /talks/1
# GET /talks/1.json
def show
end
# GET /talks/new
def new
@talk = Talk.new
end
# GET /talks/1/edit
def edit
end
# POST /talks
# POST /talks.json
def create
@talk = Talk.new(talk_params)
respond_to do |format|
if @talk.save
format.html { redirect_to @talk, notice: 'Talk was successfully created.' }
format.json { render :show, status: :created, location: @talk }
else
format.html { render :new }
format.json { render json: @talk.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /talks/1
# PATCH/PUT /talks/1.json
def update
respond_to do |format|
if @talk.update(talk_params)
format.html { redirect_to @talk, notice: 'Talk was successfully updated.' }
format.json { render :show, status: :ok, location: @talk }
else
format.html { render :edit }
format.json { render json: @talk.errors, status: :unprocessable_entity }
end
end
end
# DELETE /talks/1
# DELETE /talks/1.json
def destroy
@talk.destroy
respond_to do |format|
format.html { redirect_to talks_url, notice: 'Talk was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_talk
@talk = Talk.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def talk_params
params.require(:talk).permit(:user_id, :content)
end
end
#03:1行掲示板にEmailアドレスを表示しよう
ここでは、ユーザーの情報を掲示板に利用します。ここでは、投稿一覧にユーザーのメールアドレスを表示しましょう。
ArticlesモデルとUserモデルを関連付ける
model/article.rb
class Article < ApplicationRecord
belongs_to :user
end
投稿者のメールアドレスを表示する
iews/articles/index.html.erb
User | Content | |||
---|---|---|---|---|
<%= article.user.email %> | <%= article.content %> | <%= link_to 'Show', article %> | <%= link_to 'Edit', edit_article_path(article) %> | <%= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' } %> |
Welcomeページから、掲示板にリンクする
index.html.erb
Welcome#index
Find me in app/views/welcome/index.html.erb
<%= link_to "articles", articles_path %>ナビゲーションを共通で表示する
app/views/layouts/application.html.erb
<%= notice %>
<%= alert %>
<%= yield %>演習課題「掲示板とユーザーを関連付ける」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成してあります。
掲示板の投稿一覧では、Userモデルのメールアドレスを表示するようになっていますが、エラーになってしまいます。データベースの関連付けを設定して、メールアドレスが表示されるようにしてください。
模範解答1
/home/ubuntu/myblog/app/models/talk.rbに次の内容を記述する
class Talk < ApplicationRecord
belongs_to :user
end
演習課題「投稿一覧にメールアドレスを表示する」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成し、talkと関連付けてあります。
この掲示板の投稿一覧に、Userモデルのメールアドレスを表示するように設定してください。
模範解答1
/home/ubuntu/myblog/app/views/talks/index.html.erbに次の内容を記述する
<%= notice %>
Talks
User | Content | |||
---|---|---|---|---|
<%= talk.user.email %> | <%= talk.content %> | <%= link_to 'Show', talk %> | <%= link_to 'Edit', edit_talk_path(talk) %> | <%= link_to 'Destroy', talk, method: :delete, data: { confirm: 'Are you sure?' } %> |
<%= link_to 'New Talk', new_talk_path %>
#04:Userモデルにnameカラムを追加しよう
ここでは、記事を投稿したユーザーの名前を表示するため、deviseのUserモデルに名前のカラムを追加します。それに合わせて、ユーザーの登録フォームを変更しましょう。
Userモデルにカラムを追加
$ rails g migration AddNameToUser name:string
$ rails db:migrate
コンソールで確認
rails console
User.all
サインアップ画面に「name」カラムを追加
app/views/devise/registrations/new.html.erb
<%= f.text_field :name %>
ユーザー情報の変更画面に「name」カラムを追加
app/views/devise/registrations/edit.html.erb
<%= f.text_field :name %>
参考になるWebページ
-
[devise にusername カラムを追加し、usernameを登録できるようにする。 - Qiita]
https://qiita.com/yasuno0327/items/ff17ddb6a4167fc6b471 -
[初めてのdevise ② -- カラムを追加してみる -- ~ やってみようカスマイズ! ~ - Qiita]
https://qiita.com/uloruson/items/40154b4be19d1ac900f3 -
[Railsのログイン認証gemのDeviseのカスタマイズ方法 - Rails Webook]
http://ruby-rails.hatenadiary.com/entry/20140804/1407168000
演習課題「Userモデルにnameカラムを追加する」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成して、talkと関連付けてあります。
このUserモデルに、nameカラムを追加してください。
模範解答1
次のコマンドを順にターミナルで実行する
cd myblog
rails g migration AddNameToUser name:string
rails db:migrate
演習課題「サインアップ画面と変更画面に、nameカラムを追加する」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成し、talkと関連付けてあります。
このUserモデルにnameカラムを追加したので、次のフォームに、ラベルとテキストフィールドを追加してください。
- edit.html.erb
- new.html.erb
採点して、すべてのジャッジに正解すれば、演習課題クリアです!
模範解答1
/home/ubuntu/myblog/app/views/devise/registrations/edit.html.erbに次の内容を記述する
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div class="field">
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "off" %>
<% if @minimum_password_length %>
<br />
<em><%= @minimum_password_length %> characters minimum</em>
<% end %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "off" %>
</div>
<div class="actions">
<%= f.submit "Update" %>
</div>
<% end %>
<h3>Cancel my account</h3>
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
<%= link_to "Back", :back %>
#05:Userモデルのユーザー名を保存しよう
ここでは、Userモデルにある名前のカラムをデータベースに保存できるようにします。
コントローラで、nameカラムを保存する
app/controllers/application_controller.rb
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
devise_parameter_sanitizer.permit(:account_update, keys: [:name])
end
演習課題「nameカラムを保存できるようにする」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成し、talkと関連付けてあります。
このUserモデルに、nameカラムを追加したので、保存できるようにコードを修正してください。
採点して、すべてのジャッジに正解すれば、演習課題クリアです!
模範解答1
/home/ubuntu/myblog/app/controllers/application_controller.rbに次の内容を記述する
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
devise_parameter_sanitizer.permit(:account_update, keys: [:name])
end
end
#06:掲示板にユーザー名を表示しよう
ここでは、1行掲示板のナビゲーションと投稿一覧に、Userモデルのnameカラムを表示しましょう。
ナビゲーションのログイン情報に、ユーザー名を表示
app/views/layouts/application.html.erb
<% if user_signed_in? %>
Logged in as <%= current_user.name %>.
<%= link_to "Settings", edit_user_registration_path, :class => "navbar-link" %> |
<%= link_to "Logout", destroy_user_session_path, method: :delete, :class => "navbar-link" %>
<% else %>
<%= link_to "Sign up", new_user_registration_path, :class => 'navbar-link' %> |
<%= link_to "Login", new_user_session_path, :class => 'navbar-link' %>
<% end %>
投稿一覧に、nameカラムを表示する
views/articles/index.erb.html
Name | Content | |||
---|---|---|---|---|
<%= article.user.name %> | <%= article.content %> | <%= link_to 'Show', article %> | <%= link_to 'Edit', edit_article_path(article) %> | <%= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' } %> |
投稿の詳細画面に、nameカラムを表示する
views/articles/show.erb.html
User: <%= @article.user.name %>
演習課題「投稿一覧と詳細画面に、Userモデルのnameカラムを表示する」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成し、talkと関連付けてあります。
このUserモデルに、nameカラムを追加したので、投稿一覧と詳細画面で、user_idの代わりにUserモデルのnameカラムを表示してください。
採点して、すべてのジャッジに正解すれば、演習課題クリアです!
模範解答1
/home/ubuntu/myblog/app/views/talks/index.html.erbに次の内容を記述する
<%= notice %>
Talks
User | Content | |||
---|---|---|---|---|
<%= talk.user.name %> | <%= talk.content %> | <%= link_to 'Show', talk %> | <%= link_to 'Edit', edit_talk_path(talk) %> | <%= link_to 'Destroy', talk, method: :delete, data: { confirm: 'Are you sure?' } %> |
<%= link_to 'New Talk', new_talk_path %>
模範解答2
/home/ubuntu/myblog/app/views/talks/show.html.erbに次の内容を記述する
<%= notice %>
User: <%= @talk.user.name %>
Content: <%= @talk.content %>
<%= link_to 'Edit', edit_talk_path(@talk) %> |
<%= link_to 'Back', talks_path %>
#07:ログインユーザー名で投稿を保存しよう
ここでは、1行掲示板の投稿を、ログインしたユーザーの名前で保存できるようにします。すでに、ログインした時だけ投稿できるようになっているので、現在のログインユーザーで投稿できるようにしましょう。
新規投稿フォームを修正して、user_idを削除する
app/views/articles/_form.html.erb
<%= form_with(model: article, local: true) do |form| %>
<% if article.errors.any? %>
<%= pluralize(article.errors.count, "error") %> prohibited this article from being saved:
<ul>
<% article.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
createメソッドを修正する
app/controllers/articles_controller.rb
POST /articles
POST /articles.json
def create
@article = Article.new(article_params)
@article.user_id = current_user.id
演習課題「投稿一覧と詳細画面に、Userモデルのnameカラムを表示する」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成し、talkと関連付けてあります。
talks_controllerを修正し、ユーザーがログインしている時だけ、
新規投稿のユーザー名をログインユーザー名にしてください。なお、ログインしているかどうかは「user_signed_in?」で判別できます。
採点して、すべてのジャッジに正解すれば、演習課題クリアです!
模範解答1
/home/ubuntu/myblog/app/controllers/talks_controller.rbに次の内容を記述する
class TalksController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update, :destroy]
before_action :set_talk, only: [:show, :edit, :update, :destroy]
# GET /talks
# GET /talks.json
def index
@talks = Talk.all
end
# GET /talks/1
# GET /talks/1.json
def show
end
# GET /talks/new
def new
@talk = Talk.new
end
# GET /talks/1/edit
def edit
end
# POST /talks
# POST /talks.json
def create
@talk = Talk.new(talk_params)
if user_signed_in?
@talk.user_id = current_user.id
end
respond_to do |format|
if @talk.save
format.html { redirect_to @talk, notice: 'Talk was successfully created.' }
format.json { render :show, status: :created, location: @talk }
else
format.html { render :new }
format.json { render json: @talk.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /talks/1
# PATCH/PUT /talks/1.json
def update
respond_to do |format|
if @talk.update(talk_params)
format.html { redirect_to @talk, notice: 'Talk was successfully updated.' }
format.json { render :show, status: :ok, location: @talk }
else
format.html { render :edit }
format.json { render json: @talk.errors, status: :unprocessable_entity }
end
end
end
# DELETE /talks/1
# DELETE /talks/1.json
def destroy
@talk.destroy
respond_to do |format|
format.html { redirect_to talks_url, notice: 'Talk was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_talk
@talk = Talk.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def talk_params
params.require(:talk).permit(:user_id, :content)
end
end
#08:自分の記事だけ編集・削除 その1
ここでは、投稿したユーザーだけが自分の記事を編集・削除できるようにしましょう。そのために、contollerで、user_idが一致した時だけ、アクションを実行させます。
updateアクションを修正する
def update
if @article.user_id == current_user.id
respond_to do |format|
if @article.update(article_params)
format.html { redirect_to @article, notice: 'Article was successfully updated.' }
format.json { render :show, status: :ok, location: @article }
else
format.html { render :edit }
format.json { render json: @article.errors, status: :unprocessable_entity }
end
end
else
redirect_to @article, notice: "You don't have permission."
end
end
destroyアクションを修正する
def destroy
if @article.user_id == current_user.id
@article.destroy
msg = "Article was successfully destroyed."
else
msg = "You don't have permission."
end
respond_to do |format|
format.html { redirect_to articles_url, notice: msg }
format.json { head :no_content }
end
end
#09:自分の記事だけ編集・削除 その2
ここでは先ほどの続きとして、使わないアクションを呼び出すリンクを非表示にします。
updateアクションを修正する
def update
if @article.user_id == current_user.id
respond_to do |format|
if @article.update(article_params)
format.html { redirect_to @article, notice: 'Article was successfully updated.' }
format.json { render :show, status: :ok, location: @article }
else
format.html { render :edit }
format.json { render json: @article.errors, status: :unprocessable_entity }
end
end
else
redirect_to @article, notice: "You don't have permission."
end
end
destroyアクションを修正する
def destroy
if @article.user_id == current_user.id
@article.destroy
msg = "Article was successfully destroyed."
else
msg = "You don't have permission."
end
respond_to do |format|
format.html { redirect_to articles_url, notice: msg }
format.json { head :no_content }
end
end
投稿一覧を修正する
<% if user_signed_in? && article.user_id == current_user.id %>
<td><%= link_to 'Edit', edit_article_path(article) %></td>
<td><%= link_to 'Destroy', article, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
詳細画面を修正する
<% if user_signed_in? && @article.user_id == current_user.id %>
<%= link_to 'Edit', edit_article_path(@article) %> |
<% end %>
<%= link_to 'Back', articles_path %>
演習課題「ユーザーがログインしている時だけ、編集・削除を可能にする」
右の環境には、「myblog」プロジェクトに「talk」という1行掲示板が作成されています(user_idとcontentというカラムを持っています)。また、ユーザー認証構築用にdeviseを導入して、「User」というモデルを作成し、talkと関連付けてあります。
この掲示板で、ユーザーがログインしている時だけ、投稿一覧と詳細画面に、編集と削除のリンクを表示してください。なお、ログインしているかどうかは「user_signed_in?」で判別できます。
採点して、すべてのジャッジに正解すれば、演習課題クリアです!
模範解答1
/home/ubuntu/myblog/app/views/talks/index.html.erbに次の内容を記述する
<p id="notice"><%= notice %></p>
<h1>Talks</h1>
<table>
<thead>
<tr>
<th>User</th>
<th>Content</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @talks.each do |talk| %>
<tr>
<td><%= talk.user.name %></td>
<td><%= talk.content %></td>
<td><%= link_to 'Show', talk %></td>
<% if user_signed_in? && talk.user_id == current_user.id %>
<td><%= link_to 'Edit', edit_talk_path(talk) %></td>
<td><%= link_to 'Destroy', talk, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Talk', new_talk_path %>
模範解答2
/home/ubuntu/myblog/app/views/talks/show.html.erbに次の内容を記述する
<p id="notice"><%= notice %></p>
<p>
<strong>User:</strong>
<%= @talk.user.name %>
</p>
<p>
<strong>Content:</strong>
<%= @talk.content %>
</p>
<% if user_signed_in? && @talk.user_id == current_user.id %>
<%= link_to 'Edit', edit_talk_path(@talk) %>
<% end %>
<%= link_to 'Back', talks_path %>
お疲れ様でした!!!!