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

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

ただいまの
回答率

87.92%

投稿内容を編集するとエラーが起きる

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 278

score 3

投稿内容を編集しようとするとパラメーターが無いとエラーが生じる

現在、投稿内容を編集する機能を作成しているのですが、投稿内容を編集しようとすると下記のようなエラーが起きる。

発生している問題・エラーメッセージ

ActionController::ParameterMissing in UploadsController#update
param is missing or the value is empty: upload_form
Did you mean?
upload
id
controller
_method
Extracted source (around line #58):
56
57
58
59
60


  def upload_params
    params.require(:upload_form).permit(:title, :text, :url, :working_day, :day_off, :cafe_wifi_id, :cafe_charging_id, :cafe_smoking_id, :image, :name).merge(user_id: current_user.id)⇦この部分
  end
end

Extracted source (around line #34):
32
33
34
35
36
37

  def update
    @upload = Upload.find(params[:id])
    @upload_form = UploadForm.new(upload_params, upload: @upload)⇦この部分
    tag_list = params[:upload][:name].split(",")
    if @upload_form.valid?
      @upload_form.save(tag_list)

formオブジェクトを用いて編集機能を作成しているため、モデルの処理などはupload_form.rbというファイルに作成しております。

該当するエラー部分に関するコードなどを確認してみましたが、どの部分を修正する必要があるのか原因が掴めない状況です。

エラーに関する原因やヒントなどをご教授願えますでしょうか。

該当のソースコード

app/views/uploads/edit.html.erb

<%= render "shared/header" %>

<div class="upload-form-contents">
  <div class="upload-form">
    <h2 class="upload-form__title">カフェの情報を編集</h2>
    <%= form_with(model: @upload_form, url: upload_path, local: true)  do |f| %>

      <%# カフェ画像 %>
      <div class="img-upload">
        <div class="upload-text">
          カフェの画像
          <span class="important">必須</span>
        </div>
        <div class="click-upload">
          <p>
            クリックしてファイルをアップロード
          </p>
          <%= f.file_field :image, id: "item-image" %>
        </div>
      </div>
      <%# カフェ名とカフェの説明%>
      <div class="cafe-item">
        <div class="upload-text">
          カフェ名
          <span class="important">必須</span>
        </div>
        <%= f.text_area :title, class: "cafe-text", id: "uploads-name", placeholder: "カフェ名 (必須 40字まで)", maxlength: "40" %>
        <div class="cafe-explain">
          <div class="upload-text">
            カフェの説明
            <span class="important">必須</span>
          </div>
          <%= f.text_area :text, class: "cafe-text", id: "cafe-info", placeholder: "カフェの説明 (必須 1000字まで) こちらのカフェでは充電はもちろんのことwifiまで完備されているので,外で作業をする人におすすめです。", rows: "7", maxlength: "1000" %>
        </div>
        <div class="upload-tag">
          <div class="upload-text">
            タグ
            <span class="important">必須</span>
          </div>
          <%= f.text_field :name, class: "uploads-field", id: "cafe-tag", placeholder: "タグを入力。複数の場合は,で区切る", maxlength: "20" %>
        </div>
        <div class="upload-text">
          URL
        </div>
        <%= f.text_field :url, class: "uploads-field", id: "cafe-url", placeholder: "カフェのURL", maxlength: "60" %>
        <div class="upload-text">
          営業時間
        </div>
        <%= f.text_field :working_day, class: "uploads-field", id: "cafe-working_day", placeholder: "平日10:00~20:00 土日祝9:00~20:00", maxlength: "60" %>
        <div class="upload-text">
          定休日
        </div>
        <%= f.text_field :day_off, class: "uploads-field", id: "cafe-day-off", placeholder: "火曜日", maxlength: "60" %>
      </div>
      <%# カフェの詳細%>
      <div class="upload-detail">
        <div class="upload-text">カフェの詳細</div>
        <div class="upload-form-detail">
          <div class="upload-text">
            店内のwifi
            <span class="important">必須</span>
          </div>
          <%= f.collection_select(:cafe_wifi_id, CafeWifi.all, :id, :name, {}, { class: "select-box", id: "uploads-cafe-wifi" }) %>
          <div class="upload-text">
            充電コンセント
            <span class="important">必須</span>
          </div>
          <%= f.collection_select(:cafe_charging_id, CafeCharging.all, :id, :name, {}, { class: "select-box", id: "uploads-cafe-charging" }) %>
          <div class="upload-text">
            喫煙について
            <span class="important">必須</span>
          </div>
          <%= f.collection_select(:cafe_smoking_id, CafeSmoking.all, :id, :name, {}, { class: "select-box", id: "uploads-cafe-smoking" }) %>
        </div>
      </div>
      <%# 投稿ボタン %>
      <div class="upload-btn-contents">
        <%= f.submit "更新する", class: "uploads-form-btn" %>
        <%= link_to 'トップページへ', root_path, class: "back-btn" %>
      </div>
    <% end %>
  </div>
</div>


app/models/upload_form.rb

class UploadForm
  include ActiveModel::Model
  attr_accessor :title, :text, :url, :working_day, :day_off, :cafe_wifi_id, :cafe_charging_id, :cafe_smoking_id,
                :user_id, :image, :name

  with_options presence: true do
    validates :title
    validates :text
    validates :cafe_wifi_id
    validates :cafe_charging_id
    validates :cafe_smoking_id
    validates :user_id
    validates :image
    validates :name
    validates :upload_id, presence: true
    validates :tag_id, presence: true
  end

  with_options numericality: { other_than: 0 } do
    validates :cafe_wifi_id
    validates :cafe_charging_id
    validates :cafe_smoking_id
  end

  delegate :persisted?, to: :upload

  def initialize(attributes = nil, upload: Upload.new)
    @upload = upload
    attributes ||= default_attributes
    super(attributes)
  end

  def save(tag_list)

    ActiveRecord::Base.transaction do
      @upload.update(title: title, text: text, url: url, working_day: working_day, day_off: day_off, cafe_wifi_id: cafe_wifi_id, cafe_charging_id: cafe_charging_id, cafe_smoking_id: cafe_smoking_id, user_id: user_id, image: image)

      @upload.upload_tags_relation.each do |tag|
        tag.delete
      end

      tag_list.each do |tag_name|
        tag = Tag.where(name: tag_name).first_or_initialize
        tag.save

        upload_tag_relation = UploadTagRelation.where(upload_id: upload.id, tag_id: tag.id).first_or_initialize
        upload_tag_relation.update(upload_id: upload.id, tag_id: tag.id)

      end
    end
    # upload = Upload.create(title: title, text: text, url: url, working_day: working_day, day_off: day_off, cafe_wifi_id: cafe_wifi_id, cafe_charging_id: cafe_charging_id, cafe_smoking_id: cafe_smoking_id, user_id: user_id, image: image)
    # map = Map.create(address: address, latitude: latitude, longitude: longitude, upload_id: upload_id)
  end

  def to_model
    upload
  end

  private

  attr_reader :upload

  def default_attributes
    {
      title: upload.title,
      text: upload.text,
      url: upload.url,
      working_day: upload.working_day,
      day_off: upload.day_off,
      cafe_wifi_id: upload.cafe_wifi_id,
      cafe_charging_id: upload.cafe_charging_id,
      cafe_smoking_id: upload.cafe_smoking_id,
      image: upload.image,
      name: upload.tags.pluck(:name).join(",")
    }
  end
end

app/controllers/uploads_controller

class UploadsController < ApplicationController
  def index
    @uploads = Upload.all.order(created_at: :desc)
    @tag_list = Tag.all
  end

  def new
    @upload_form = UploadForm.new
  end

  def create
    @upload_form = UploadForm.new(upload_params)
    tag_list = params[:upload_form][:name].split(",")
    if @upload_form.valid?
      @upload_form.save(tag_list)
      redirect_to root_path
    else
      render new
    end
  end

  def show
    @upload = Upload.find(params[:id])
    @tag = @upload.tags
  end

  def edit
    @upload = Upload.find(params[:id])
    @upload_form = UploadForm.new(upload: @upload)
  end

  def update
    @upload = Upload.find(params[:id])
    @upload_form = UploadForm.new(upload_params, upload: @upload)
    tag_list = params[:upload][:name].split(",")
    if @upload_form.valid?
      @upload_form.save(tag_list)
      redirect_to upload_path(@upload.id)
    else
      render :edit
    end
  end

  def destroy
    @upload = Upload.find(params[:id])
    @upload.image.purge if @upload.image.attached?
    if @upload.destroy
      redirect_to root_path
    else
      render :show
    end

  end

  private

  def upload_params
    params.require(:upload_form).permit(:title, :text, :url, :working_day, :day_off, :cafe_wifi_id, :cafe_charging_id, :cafe_smoking_id, :image, :name).merge(user_id: current_user.id)
  end
end

試したこと

・paramsが取得できていないことが原因なので、edit,updateアクションのコードに不足がないか確認してみたが、この部分にエラーが発生するコードがあるとは考えずらかった。
・formオブジェクト内の記述に原因がないか確認してみたところ、saveアクションの中に必要な処理は記述されていた。
・edit.html.erbのform_with modelの記述に問題がないか確認してみたが原因が掴めなかった。

補足情報(FW/ツールのバージョンなど)

開発環境
・rubymine
・ruby(3.0.1)
・Ruby on rails (6.1.3.1)
Githubのリポジトリ
https://github.com/yuuffff1212/locat

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

check解決した方法

0

カスタムメソッドの定義方法が難しかったため、別の方法で試してみたことろ編集ができるようになりました。

1.upload_formに記述している中間テーブルのバリデーションを削除し、中間テーブルに記述した

class UploadTagRelation < ApplicationRecord
  belongs_to :tag
  belongs_to :upload

  validates :upload_id
  validates :tag_id
end

上記のように記述してみたがエラーが発生。

ArgumentError in UploadsController#show
You need to supply at least one validation

エラー内容を調べてみたところ、バリデーションを下記のように修正する必要があると分かった。

class UploadTagRelation < ApplicationRecord
  belongs_to :tag
  belongs_to :upload

  validate :upload_id
  validate :tag_id
end

2.upload_formのsaveメソッドを修正

def save(tag_list)

    ActiveRecord::Base.transaction do
      @upload.update(title: title, text: text, url: url, working_day: working_day, day_off: day_off, cafe_wifi_id: cafe_wifi_id, cafe_charging_id: cafe_charging_id, cafe_smoking_id: cafe_smoking_id, user_id: user_id, image: image)

      @upload.upload_tag_relations.each do |tag|
        tag.delete
      end

→@upload.upload_tag_relationをupload_tag_relationsと複数系に修正。

      tag_list.each do |tag_name|
        tag = Tag.where(name: tag_name).first_or_initialize
        tag.save

        upload_tag_relation = UploadTagRelation.where(upload_id: upload.id, tag_id: tag.id).first_or_initialize
        upload_tag_relation.update(upload_id: upload.id, tag_id: tag.id)

      end
    end

今回の場合は、formオブジェクトを使用して中間テーブルのバリデーションに誤りがあったため編集、更新、投稿ができない問題が起きていることが分かりました。

ただ、この記述方法でバリデーションが正常に動いているのか少々不安ですが編集ができるようになったのでひとまず安心です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

UploadForm#to_modelにてUploadのインスタンスを返しているため

params.require(:upload) #以後省略

になります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/05/16 21:33

    両者のvalidatesが処理できていないために、取得結果がnillとなりデーターの保存ができないんですね。

    カスタムメソッドが初めて聞くものですので、いまいちイメージが掴めないんですが、定義後に子要素に丸投げするとはどのような意味なんでしょうか。

    キャンセル

  • キャンセル

  • 2021/05/17 21:53

    カスタムメソッドの実装方法がなかなか難しかったので、formオブジェクトに記述していたバリデーションをupload_tag_relation(中間テーブル)に記述してみたところ編集ができるようになりました。

    キャンセル

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

  • ただいまの回答率 87.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る