1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

form_tagで渡したユーザー情報がredirect先のブラウザに表示されないエラーの原因と解決策

1
Last updated at Posted at 2019-03-21

エラー

form_tagで渡したユーザー情報がredirect先のブラウザに表示されないエラー。

View

form_tagで入力した情報をuserコントローラーのupdateアクションへ渡す。

users/edit.html.erb
<%= form_tag("/users/#{@user.id}/update", {multipart: true}) do %>
  <p>Name</p>
  <input name="name" value="<%= @user.name %>">
  <p>Image</p>
  <input name="image" type="file">
  <p>Email</p>
  <input name="email" value="<%= @user.email %>">
  <input type="submit" value="Update">
<% end %>

Route

form_tagで渡された情報が post 'users/:id/update', to:'users#update'を通る。

routes.rb
Rails.application.routes.draw do
post 'users/:id/update', to:'users#update'
get 'users/:id/edit', to: 'users#edit'
end

Controller

updateアクションで、URLから検索したidを@userに代入する。

@userのnameとemailに、form_tagで渡ってきたnameと、emailを代入する。

@user.saveされたら、/users/#{@user.id}のURLを通って、user/showページに画面推移。されなかったら、render("users/edit")。

users_controller.rb

def edit
    @user = User.find_by(id: params[:id])
end

def update
    @user = User.find_by(id: params[:id])
    @user.name = params[:name]
    @user.email = params[:email]

    if params[:image]
      @user.image_name = "#{@user.id}.jpg"
      image = params[:image]
      File.binwrite("public/user_images/#{@user.image_name}", image.read)
    end

    if @user.save
      flash[:notice] = "Updated your information."
      redirect_to("/users/#{@user.id}")
    else
      render("users/edit")
    end
  end

form_tag("/users/#{@user.id}/update", {multipart: true}) doでsubmit後のログ

Started POST "/users/1/update" for 127.0.0.1 at 2019-03-20 23:39:07 +0900
Processing by UsersController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"teAfeEXI8IMrtjWSRdq9DD9a3l+chPishnWRIT18+r5/gGYyRU3Y1vomGpK4tA2aI06/yV12ZkBHPSXlOiBlCg==", "name"=>"あああ", "ema=>"uuu@u.com", "id"=>"1"}
  User Load (0.6ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
   (0.2ms)  BEGIN
  User Exists (1.1ms)  SELECT  1 AS one FROM `users` WHERE `users`.`email` = BINARY 'uuu@u.com' AND (`users`.`id` != 1) LIMIT 1
   (0.3ms)  ROLLBACK
  Rendering users/edit.html.erb within layouts/application
  Rendered users/edit.html.erb within layouts/application (2.0ms)
Completed 200 OK in 68ms (Views: 54.9ms | ActiveRecord: 2.7ms)

推定原因

form_tagで渡したユーザー情報がDBで保存されていない。

保存されていないため、Update後のユーザー情報がリダイレクト先のブラウザに表示されない。

Update後のユーザー情報がリダイレクト先のブラウザに表示するには、updateアクションの結果をDBに保存する必要がある。

なぜ、updateアクションの結果がDBで保存されていないかわからない。なので、再度、updateアクションを確認。

users_controller.rb
def update
    @user = User.find_by(id: params[:id])
    @user.name = params[:name]
    @user.email = params[:email]

    if params[:image]
      @user.image_name = "#{@user.id}.jpg"
      image = params[:image]
      File.binwrite("public/user_images/#{@user.image_name}", image.read)
    end

    if @user.save
      flash[:notice] = "Updated your information."
      redirect_to("/users/#{@user.id}")
    else
      render("users/edit")
    end
  end

ユーザーモデルも確認

user.rb
class User < ApplicationRecord
  has_secure_password
  validates :email, {presence: true, uniqueness: true}
  validates :password, {presence: true}
  validates :password, confirmation: true
  validates :name,  presence: true, length: { maximum: 50 }
end

ユーザー編集時に、ユーザー情報がDBで保存されない原因

validates :password, confirmation: trueのパスワードのバリデーションにありました。

この1行を消したら、form_tagで送信した情報がデータベースに保存されて、リダイレクト先のブラウザで変更内容を出力できました。

問題

ユーザー編集時に、データベースに保存される場合は、

validates :password, {presence: true} validates :password, confirmation: true

このバリデーションのコードを消せば、ユーザーupdateが実施され、データベースにデータが保存されました。パスワードが空の場合は、データベースに保存しないバリデーションを外したからです。

しかし、ユーザー編集時とは違い、ユーザー登録時には、パスワードが空の場合、データベースに保存されないようにするために、バリデーションが必要になります。

なので、以下の2つの条件を満たす、バリデーションが必要になることがわかりました。

①ユーザー登録の際に、パスワードが空の場合は、新規ユーザーが作成されないけど、②ユーザー情報の更新時に、パスワードが空の場合でも、データベースにユーザー情報が保存されるバリデーション

パスワードのバリデーションに対して、空だったときの例外処理(条件①、②を両立させるバリデーション)

allow_nil: true

user.rb
class User < ApplicationRecord
  has_secure_password
  validates :password, presence: true, allow_nil: true
end

allow_nil: trueによって、ユーザー登録の際に、パスワードが空の場合でも、新規ユーザーが作成されるようになってしまうのでは?

大丈夫だった。has_secure_passwordによって、オブジェクトの生成時(User.newの時)に、オブジェクトの存在性(presence)を検証するようになっている。そのため、ユーザー登録の際に、空のパスワードだと、新規ユーザーが作成されないようになってる。だから、安心。

「リスト 10.13によって、新規ユーザー登録時に空のパスワードが有効になってしまうのかと心配になるかもしれませんが、安心してください。6.3.3で説明したように、has_secure_passwordでは (追加したバリデーションとは別に) オブジェクト生成時に存在性を検証するようになっているため、空のパスワード (nil) が新規ユーザー登録時に有効になることはありません。」

has_secure_passwordの性質

has_secure_passwordには、validationを追加しなくても、User.newで生成される、例えば、name,email,password,image_nameのそれぞれが、presenceかnilかを検証してくれる性質があると理解した。

users_controller.rb
def create
    @user = User.new(
        name: params[:name],
        email: params[:email],
        image_name: "default_user.jpg",
        password: params[:password]
    )
    if @user.save
      session[:user_id] = @user.id
      flash[:notice] = "ユーザー登録が完了しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/new")
    end
  end

引用元:https://railstutorial.jp/chapters/updating_and_deleting_users?version=5.1#sec-updating_users

補足:存在性の検証とは

参考資料:https://railsguides.jp/active_record_validations.html

person.rb
class Person < ApplicationRecord 
  validates :name, presence: true 
 end 
   
Person.create(name: "John Doe").valid? # => true 
Person.create(name: nil).valid? # => false 

メモ

エラーの原因を考えるときは、view,route,controllerだけではなく、model,databaseまで視野に入れる

MVCでエラーの原因を考えるとき、view,route,controllerまで確認できていたけど、modelまでは確認できていなかった。なので、エラーの原因がmodelにあると気づくのに時間がかかった。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?