プログラミング備忘録

プログラミングの学習状況をメモしています

88日目

今日の学習

Ruby on Rails

form_withでデータが送れない

以下のようなcreateアクションを作成し、投稿ができるようにしたのだが投稿ができない。

Postでは、タイトル、ゲーム名、本文を入力してもらうフォームを用意する。

  def create
    post = Post.create!(
      title: params[:title],
      game: params[:game],
      body: params[:body],
      user_id: current_user.id
    )
    redirect_to post
  end

  def show
    @post = Post.find(params[:id])
  end

  private

  def post_params
    params.require(:post).permit(:title, :game, :body)
  end

user_idを投稿に紐づけたくてこのような形にしている。

入力した値が送信できなかった原因は、post_paramsというメソッドを作成したにも関わらず、createメソッド内でparamsと記述していたのが原因だった。

見本を写して作ったメソッドのため、そのままにしていたのが良くなかった。ビュー側のform_withの書き方を見直しても問題なかったため、少々行き詰まってしまった。

正しくは、以下のような形にする。

    post = Post.create!(
      title: post_params[:title],
      game: post_params[:game],
      body: post_params[:body],
      user_id: current_user.id
    )

エラー画面には文字を入力してくださいというバリデーションのメッセージが表示されていた。

表示されているエラー画面では以下のように表示されており、ちゃんと値が送れているのではないかと早とちりしてしまっていた。

{"authenticity_token"=>"[FILTERED]", "post"=>{"title"=>"タイトルです", "game"=>"ゲーム名", "body"=>"面白いです"}, "commit"=>"送信"}

しかし、ターミナル側には表示されていなかった。

post_paramsで受け取ることにより、ちゃんと値が入力されている状態にすることができた。

これで良かったと思いきや、ゲーム名の部分でエラーが発生する。

AssociationTypeMismatch

本来、「ゲーム名」としているところには、楽天APIで検索したゲームの情報を選択して投稿...という形にしようと思っているのだが、暫定的にゲーム名を入力するだけにしてある。

ゲームの情報を保存するGameモデルを別で作成してあり、Postモデルと紐づけてある状態だ。

暫定で用意したゲーム名の入力部分に適当な文字を入力して投稿しようとすると、以下のようなエラーが発生した。

ActiveRecord::AssociationTypeMismatch in PostsController#create
Game(#69160) expected, got "ゲーム名" which is an instance of String(#5620)

Ruby - RailsでAssociationTypeMismatchエラー|teratail

こちらを参考にすると、「入力された値("ゲーム名")はStringなので、Gameの外部キーではないためエラーが発生しているということになる。

とりあえず投稿だけできるかを確認したかったため、ゲーム情報を楽天APIから検索してDBに登録する機能は後回しにしようと考えていたが、同時に実装することにした。

一つのフォームから複数のモデルに保存

PostGameモデル両方に登録を行いたい。

Ruby on Rails における build メソッドと new メソッドの違い - Qiita

【Rails】モデルの関連付けで用いられるbuildメソッドまとめ|TechTechMedia

今回はモデルの関連付けを行うため、newではなくbuildメソッドを利用。

buildを使うとfield_forが使えるため、複数モデルにデータを保存できるようになる。

また、belongs_tohas_oneでは親モデル.build_子モデルという形にする必要がある。

今回であればpost.build_gameのようにする。

fields_forの上手な使い方 - Qiita

アバター機能

ポートフォリオに、ユーザーがアバターを設定できる機能をつけたい。

しかし、デプロイする際にデータをどこに保存する設定であれば大丈夫なのかどうかが良く分からない。

【Rails忘備録】carrierwave使ってS3に画像をアップロードするの巻 - 君が代

こちらの方は、冒頭でHerokuへデプロイする場合はS3が必要だと仰っている。

【Rails5】AWS S3+CarrierWave+Fog::AWSを利用して画像アップロード機能を作成する | WEB-LOG

そこで、以下の記事をもとにgem CarrierWaveを画像アップロード機能として使用しながら、本番環境ではS3に画像がアップロードできるように設定した。

【Rails】 CarrierWaveチュートリアル | Pikawaka - ピカ1わかりやすいプログラミング用語サイト

アバターを設定していない場合表示されるデフォルト画像を用意したが、サムネイル用のデフォルト画像は別途で用意しなければならないようだった。

外部キーの追加と削除

追加

開発中に、自分のデータベースの組み立て方がおかしいことに気づいた。具体的にはbelongs_toの矢印方向を間違っていた。

そのため、誤った外部キーを削除し、新たに外部キーを追加なければならない。

追加する際は、マイグレーションファイルを作成し、add_referenceを利用する。

【Rails】後からカラムを追加して外部キーを張る際に、add_referenceを使う場合の注意点。 - Qiita

  def change
    add_reference :子モデル複数形, :親モデル, foreign_key: true
  end

このとき、foreign_keyがデフォルトではfalseになってしまうため、オプションでtrueとする必要がある。

削除

外部キーを削除する際は、マイグレーションファイルを作成し、remove_foreign_keyを利用する。

Rails の migration で外部キー制約の設定をする (add_foreign_key, remove_foreign_key) - Qiita

  def change
    remove_foreign_key :親モデル名複数形, :子モデル名複数形
  end

これでマイグレーションした後にテーブルを確認してみると、まだgame_idというカラムが残ったままだった。

    remove_index :posts, :game_id
    remove_column :posts, :game_id, :bigint

以下を新たに実行すると、削除することができた。