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

###エラー

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`のパスワードのバリデーションにありました。

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


##問題

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

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

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

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

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

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

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

`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にあると気づくのに時間がかかった。