質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

2回答

1151閲覧

ユーザー情報のパスワード以外を編集するページを作りたい

haru24s

総合スコア4

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

1グッド

0クリップ

投稿2021/02/19 08:21

編集2021/02/23 14:50

ユーザー登録機能のあるサイトを作っていて、ユーザー情報としてデータベースのテーブルにはユーザー名、メールアドレス、パスワードの情報を持たせています。バリデーションはどのカラムもpresence: true としていて、ユーザー情報(ユーザー名とメールアドレスの変更)の編集ページを作り、ユーザー名、メールアドレスのフォームとアクションを書いたのですが、submitボタンを押すとパスワードが空であるバリデーションエラーが出てしまいます。

パスワードは元のデータベースにあるものを使い、ユーザー名、メールアドレスだけを更新するにはどうすれば良いでしょうか?

編集ページのビュー <div class="main users-edit"> <h1>ユーザー情報の編集</h1> <div class="form"> <div class="form-body"> <% @user.errors.full_messages.each do |message| %> <div class="form-error"> <%= message %> </div> <%end%> <%= form_with model: @user, local: true do |form| %> <div class="field"> <%= form.label :ユーザー名 %> <%= form.text_field :name, placeholder: "ユーザー名" %> </div> <div class="field"> <%= form.label :メールアドレス %> <%= form.text_field :email, placeholder: "メールアドレス" %> </div> <div class="actions"> <%= form.submit "編集する" %> </div> <% end %> </div> </div> </div>
アクション def edit @user = User.find_by(id: params[:id]) end def update @user = User.find_by(id: params[:id]) if @user.update(user_params) flash[:notice] = "ユーザー情報を編集しました" redirect_to("/users/#{@user.id}") else render("users/edit") end end private def user_params params.require(:user).permit(:name, :email, :password) end
/models/user.rb class User < ApplicationRecord has_secure_password validates :name, {presence: true} validates :email, {presence: true, uniqueness: true} validates :password, {presence: true} end
/user_controller.rb class UsersController < ApplicationController before_action :authenticate_user, {only: [:index, :show, :edit, :update]} before_action :forbid_login_user, {only: [:new, :create, :login_form, :login]} before_action :ensure_correct_user, {only: [:edit, :update]} def index @users = User.all end def show @user = User.find_by(id:params[:id]) end def new @user = User.new end def create @user = User.new(user_params) if @user.save session[:user_id] = @user.id flash[:notice]="ユーザー登録が完了しました" redirect_to("/users/#{@user.id}") else render("users/new") end end def edit @user = User.find_by(id: params[:id]) end def update @user = User.find_by(id: params[:id]) if @user.update(user_params) flash[:notice] = "ユーザー情報を編集しました" redirect_to("/users/#{@user.id}") else render("users/edit") end end def login_form end def login @user = User.find_by(email: params[:email]) if @user && @user.authenticate(params[:password]) session[:user_id] = @user.id flash[:notice] = "ログインしました" redirect_to("/posts") else @error_message = "メールアドレスまたはパスワードが間違っています" @email = params[:email] @password = params[:password] render("users/login_form") end end def logout session[:user_id] = nil flash[:notice] ="ログアウトしました" redirect_to("/login") end def ensure_correct_user if @current_user.id != params[:id].to_i flash[:notice] = "権限がありません" redirect_to("/posts") end end def update_name_and_email @user = User.find_by(id: params[:id]) if @user.update_attributes(name: params[:name], email: params[:email]) flash[:notice] = "ユーザー情報を編集しました" redirect_to("/users/#{@user.id}") else render("users/edit") end end private def user_params params.require(:user).permit(:name, :email, :password) end end
schema.rb ActiveRecord::Schema.define(version: 2021_02_14_124745) do create_table "posts", force: :cascade do |t| t.string "title" t.text "content" t.integer "user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "img" end create_table "users", force: :cascade do |t| t.string "name" t.string "email" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "password_digest" end end
shinoharat2👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shinoharat2

2021/02/20 17:56 編集

以下のコードを質問文に追記してほしいです。 app/models/user.rb app/controllers/user_controller.rb db/schema.rb 何故かというと、ちゃんとした回答をするために、 ・Userクラスに「has_secure_password」を定義しているかどうか ・必須以外の検証の内容(パスワードは8文字以上とか、確認のためにパスワードは2回入力するとか) などを把握しておきたいからです。
shinoharat2

2021/02/20 18:15

ちなみに、「今作っているページではパスワードを変更しないけど、あとからパスワード変更用ページを作る予定」という認識であってますか? つまり、「更新時は絶対にパスワードのバリデーションを行わない」のではなく、「画面によって、更新時のパスワードのバリデーションが必要だったり不要だったりする」と考えて良いですか?
haru24s

2021/02/23 14:59

コメントありがとうございます。遅くなってしまい申し訳ありません。 コードの追記をしましたのでご確認いただければと思います。 はい。プロフィールの編集のような感覚でひとまず名前とメールアドレスの編集機能を実装しようとしていて、まだ深く理解できていないbcryptを使っていたりするのでパスワードは慎重に扱うべきかと思い、このページでは編集せず、新規登録時に保存されているものをそのまま使いたいと考えていました。エラー内容を見る限り、フォームに入力された新しいユーザー名、メールアドレスとともに、パスワードの情報もnilとして送信されてしまっている結果バリデーションにかかってしまっているように思うのですがどうでしょうか? 実現したい機能としては、登録されたユーザー名、アドレス、パスワードのなかの、ユーザー名とアドレスの二つだけを更新するページを作りたいです。 shinoharat2さんのおっしゃる「画面によって、更新時のパスワードのバリデーションが必要だったり不要だったりする」という認識で考えています。
guest

回答2

0

ベストアンサー

すみません、とりあえず直し方だけ書きます。

Userクラスの validates :password, {presence: true} を行ごと削除してください。
has_secure_password メソッドには、「新規作成時のみパスワードが必須(更新時は必須じゃない)」というバリデーションを自動で行う効果がありますので、自前で定義する必要はありません。

これで新規作成画面とプロフィール編集画面は問題なく動きます。

修正後のUserモデルは以下のようになります。

rb

1class User < ApplicationRecord 2 has_secure_password 3 4 validates :name, {presence: true} 5 validates :email, {presence: true, uniqueness: true} 6end

補足1

エラー内容を見る限り、フォームに入力された新しいユーザー名、メールアドレスとともに、パスワードの情報もnilとして送信されてしまっている結果バリデーションにかかってしまっているように思うのですがどうでしょうか?

いえ、そういうわけではないです。

例えば、以下のようなデータを新規作成したとします。

rb

1User.create!(name: "太郎", email: "aaa@bbb.com", password: "xyz123")

そして、そのデータを find メソッドで取得します。
すると、name や email は保存したときの値が取得できますが、 password は nil になっています。

rb

1# さっき作成したデータを取得 2user = User.find(さっきのid) 3 4# 普通は保存したときの値が取得できる。 5user.name #=> "太郎" 6user.email #=> "aaa@bbb.com" 7 8# ただし、password は nil になる 9user.password #=> nil

なぜなら、DBに保存されているのは、「password」ではなく「password_digest」だからです。
確認してみると、password_digestカラムにハッシュ化されたパスワードが格納されているのがわかります。

rb

1user.password_digest 2=> "$2a$12$Bc3udhjk8zNWMORNBIK6feRYvF4Ded6WD3.OQVYJ0LJxZ5sJB.b2K"

「password」は「password_digest」を作るための一時的な入れ物で、DBとは直接つながっていません。
なので、DBからロードしたデータの中には「password」が含まれない(nilになる)という形です。

「password が nil だからエラーになっている」という認識は正しいですが、
「フォームから nil が送られてきたので password が nil で上書きされた」というわけではありません。

補足2

今後パスワード更新画面を作る場合、その画面ではパスワードの必須バリデーションを行う必要が出てきます。
このように、ある特定のタイミングでのみバリデーションを実行させたい場合、 :on オプションが利用できます。

例えば、以下のように、バリデーションに on: :change_password を付与したとします。

rb

1class User < ApplicationRecord 2 has_secure_password 3 4 validates :password, presence: true, on: :change_password 5end

このバリデーションは、以下のように、saveメソッドのコンテキストにchange_passwordを指定した場合のみ有効になります。

rb

1@user.save(context: :change_password)

これを使えば、「特定の画面でのみ有効なバリデーション」を作成できます。

rb

1# 実装例 2class UserController < ApplicationController 3 4 def パスワード更新画面 5 @user = User.find(params[:id]) 6 7 @user.attributes = change_password_attributes 8 9 if @user.save(context: :change_password) 10 ・・・ 11 end 12 end 13 14 private 15 16 def change_password_attributes 17 params.require(:user).permit(:password) 18 end 19 20end

投稿2021/02/23 18:14

編集2021/02/24 12:31
shinoharat2

総合スコア73

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

haru24s

2021/02/24 06:29

ご回答ありがとうございます。has_secure_password の、バリデーション機能は知りませんでした。勉強になります。教えていただいた通り試したところ問題なく動きました!後日の解説までご親切にありがとうございます。ご都合よろしければ、ぜひお願いします!
shinoharat2

2021/02/24 12:42

補足を記述しました。 「補足1」は password が nil になった原理の解説です。 理解が深まるかなと思って書いたのですが、難しかったら読み飛ばしても全然OKです。 (初心者のうちにすべてを理解する必要はないですしね) 「補足2」は今後必要になると思われるオプションについての解説です。 気が向いたら目を通してみてください。
haru24s

2021/02/24 15:15

ご丁寧にありがとうございます。とてもわかりやすい説明で、理解が深まりスッキリしました。DBから帰ってくるのはpassword_digestでpasswordの値は無いのに、passwordにバリデーションをかけてしまっていたことで引っかかってしまったんですね。この2つを分けて考えることができていませんでした。 補足2についても、例までつけてくださり助かります。おかげでとても分かりやすいです。この先使ってみたいと思います。 改めて、貴重なお時間を割いてご親切に教えていただいて、本当にありがとうございました。
guest

0

deviseに頼らず自作する。

投稿2021/02/19 09:21

m.ts10806

総合スコア80850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

haru24s

2021/02/19 10:00

いつも厳しいご意見有難うございます。
m.ts10806

2021/02/19 10:09 編集

「いつも」というほどharu24sさんと絡んだ記憶がないのですけど、本質だと思いますよ。 ライブラリ・パッケージから離れたことをやるのでしたら自作するしかありません。 というか、自前で画面作成とCRUD組めるくらいでないと、思うようには使えないのでは。
haru24s

2021/02/19 10:22

たくさんの方にコメント、回答されてますので認識されていなくて当然かと思います。僕の質問にはほぼ毎回コメントいただいています。 プログラミング自体初心者で、就活に向けて初めてwebアプリケーションを作ってみている段階なので、手探りで進めている状態で、プロの方にとってはそんなことここで聞くな、というような内容かもしれません。基礎を固めている最中のような段階ですので、できるだけ早くおっしゃるように思い通りにこの言語を使いこなせるよう勉強していきたいと思います。
m.ts10806

2021/02/19 10:31

>プロの方にとってはそんなことここで聞くな と思ったことはほとんどないですね。(本気でプログラミング関係ないのとか先に聞くべき人が明確な場合は別) 要件が不明瞭であったり、丸投げはそもそも非推奨なので「ルール守ってないね」という意味で指摘はします。 ルールを守ることに初心者かどうかは関係ないですから。 要件が明確であったり、アドバイスの方向性が明確なものはむしろ大歓迎です。 本件についてはもしかしたら「ライブラリにそういう機能があるのではないか」という期待をした質問かもしれませんが、「データの更新」という観点にすると、Deviseから離れてもRailsが持っているデフォルトの機能で対応可能なわけです。 それも「自作」に入ると思います。 もちろんDeviseでもできるかもしれませんが、「自分なら普通に更新ページ作るかな」と思ったための回答です。
haru24s

2021/02/19 13:27

言い訳をするつもりではありませんが、知識が足りないが故にどう質問すれば良いかも少し難しく、質問が情報不足だったり、曖昧な内容になってしまっていたかもしれません。申し訳ありません。 m.ts10806さんのおっしゃるDeviseについても勉強不足でピンと来なかったため調べたのですが、ユーザー機能を作るときに使うgemの名前なんですね。存在を知らなかったので現在このgemは使っておらず、教材で学習した流れで、自分でルーティング、アクション、ビューを書いています。 その中で、更新ページを記載したようなコードで書いたところうまくいかず、「password can't be blank」とエラーが出てしまう解決法がわからず質問させていただきました。railsデフォルトの機能で対応する場合どのようにしたら良いのでしょうか? 自分で調べて、update_attributes を使えばできそうだというところまでは行ったのですが、それをどのように使うかがうまく見つけられませんでした...
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問