バリデーション不足によるエラー解決　~Active Storage を用いた画像投稿機能の実装時発生~

##はじめに
・投稿者は初学者ですので誤った情報を投稿してしまうことがあります。その時はぜひ、遠慮会釈なしにご指摘いただけると幸いです。


##今回のエラーの原因
画像データのバリデーションが欠けていたため、本来であれば画像とセットで投稿しないといけないが、画像がない状態でデータの保存ができてしまっていた。
そのため、画像のないデータを受け取ったビューファイルが混乱し、エラーが起きた。

以下、詳細。

##画像投稿の実装の手順
画像投稿機能を実装しようと`ActiveStorage`を導入した。
そして、以下の手順で準備を進めた。
まずは投稿データと画像データが1対1の関係になるように記した。

```models/prototype.rb
class Prototype < ApplicationRecord
  belongs_to :user
  has_one_attached :image #1対1の関係になる
  has_many :comments, dependent: :destroy

  validates :title, :catch_copy, :concept, presence: true
end
```

次に、コントローラーを以下のように書き換えた。

```controllers/prototypes_controller.rb
  def create
    @prototype = Prototype.create(prototype_params)

    if @prototype.save
      redirect_to root_path
    else
      render :new
    end
  end
 
  private 
  def prototype_params
    params.require(:prototype).permit(:title, :catch_copy, :concept, :image).merge(user_id: current_user.id)
  end
```

`prototype_params`に`:image`を追加することによって投稿データに画像も付属されるように設定。

続いてviweファイルの調整を行う。

```views/prototypes/index.html.erb
<div class="card__wrapper">
  <%= render partial: 'prototype',  collection: @prototype, locals: { prototype: @prototype } %>
</div>
```

```views/prototypes/_prototype.html.erb
<div class="card">
  <%= link_to image_tag(prototype.image, class: :card__img ), prototype_path(prototype.id) %>
  <div class="card__body">
    <%= link_to prototype.title, prototype_path(prototype.id), class: :card__title %>
    <p class="card__summary">
      <%= prototype.catch_copy %>
    </p>
    <%= link_to "by #{prototype.user.name}", user_path(prototype.user.id), class: :card__user %>
  </div>
</div>
```

##エラー発生
投稿の内容は、「投稿物の名前」、「キャッチコピー」、「コンセプト」と先ほど設定した「画像」の全部で4つ。
ローカル環境で動作確認をしていたところ、画像がない状態で投稿すると、投稿が弾かれることなく以下のようなエラー画面が発生した。

![スクリーンショット 2020-12-01 12.11.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/874721/a0ebf5da-60d3-89b2-aa7f-e0d649cc0ffb.png)


よく見ると上から３行目に、
`Can't resolve image into URL: to_model delegated to attachment, but attachment is nil`
と書かれていることが分かる。
Can't resolve image／attachment is nil と書かれているので、「画像がないからエラーが出ているぞと」言っているのだろうと理解できた。

##エラー解決のための仮説
エラー発生直前に行なっていた動作と、エラー画面を合わせて考えると、以下のような仮説にたどり着いた。

１、画像だけがない状態で投稿をしたらエラーが発生した。
２、エラー画面に表示されているコードはビューファイルのもので、尚且つ、画像を表示するコードを赤く示している。

=>あるはずの画像データがないため、ビューファイルのコードが混乱しているのではないか。
=>画像だけがない状態で投稿をしたら、弾かれなかったということは画像データのバリデーションが正常に機能していないのではないか。

この2つの仮説をもとに、モデルのファイルを覗いてみると、案の定、画像データのバリデーションが欠けていた。


<b>誤</b>

```models/prototype.rb
class Prototype < ApplicationRecord
  belongs_to :user
  has_one_attached :image #1対1の関係になる
  has_many :comments, dependent: :destroy

  validates :title, :catch_copy, :concept, presence: true
end
```

<b>正</b>

```models/prototype.rb
class Prototype < ApplicationRecord
  belongs_to :user
  has_one_attached :image #1対1の関係になる
  has_many :comments, dependent: :destroy

  validates :title, :catch_copy, :concept, :image, presence: true
end
```

`validates :image` が抜けていたために起きたエラーであった。
これによりバリデーションがかかり、エラーの原因は解消できた。

##エラー画面が消えない（余談）

バリデーションを設定し直したことで原因は解消されたが、サーバーやPCを再起動してもエラー画面から抜け出すことができなった。最初は、他に抜けているところがあるに違いないと、様々なファイルを覗いたが間違いはないように思えた。
頭を抱えて休日の半日を過ごしたが、原因は簡単なものだった。

<b>バリデーションの設定前に保存した、画像がない投稿データがある</b>からエラー画面から抜け出せないだけであった。

Sequel Proを用いて直接、画像のない投稿データをレコードごと削除すると、無事にエラーの解消ができた。
Herokuでデプロイした後であっても、下記のブログを参考にすれば解決できることも判明したので、忘れないようにこれも記述する。

##参考文献
[herokuでデプロイしたDBにSequelProで接続してみる](https://qiita.com/mami3sansan/items/ecb2c2216be9b1d4f11e)
[【Active Storage】ファイルアップロード時のバリデーション設定](https://qiita.com/sdkk-rails/items/8f5c006a1f052beb91ce)
[【初心者向け】RailsのActive Recordの解説&メソッドまとめ](https://qiita.com/ryokky59/items/a1d0b4e86bacbd7ef6e8)

とても参考になりました。
ありがとうございました。


##感想
「バリデーション抜け」も「画像がない投稿データ」も、些細な原因ではあるが、解決までに十数時間を費やした。

エラー画面を見続けて、苦しさとか悲しさとか色々と感情が込み上げて泣きそうにもなったが、解決できたときは晴れやかな気持ちになって「プログラミング最高！」と1人で盛り上がった。

エラーで挫けそうになることはこれから先、何度もあるはずだが、解決できたら最高な気分になれるという今回の教訓を思い出して乗り越えたい。