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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

1回答

5998閲覧

Active Storageのhas_many_attachedによって添付した画像に対してカスタムバリデーション を設定したいのですが、上手くきません。

sassan738

総合スコア32

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2020/06/25 19:41

編集2020/06/25 20:54

Railsの学習のためにゲームソフトのユーザーレビューサイトを制作しています。

環境は
Rails 5.2.4
Ruby 2.6.5
PostgreSQL
Docker Compose
です。

ユーザーがレビューを投稿する際に画像を投稿できる機能をActiveStorageで実装しようとしていて、投稿された画像に対してvalidateメソッドでバリデーションをかけたいのですがそれがうまくいきません。ActiveStorageの設定はhas_many_attached :imagesで行っています。
バリデーションの条件は
・画像が3枚以上添付されているとエラーになる。
・添付されたファイルのcontent_typeがimage/jpg image/jpeg image/gif image/pngのいずれかでなければエラーになる。
・画像1枚あたりの容量が2目がバイトより大きいとエラーになる。
です。
現在の状態は、file_fieldにcontent_typeがimage/jpgの画像を添付して送信するとレビューが正常に保存されて欲しいのですが、以下の画像のように「イメージはイメージファイルを指定して下さい」というエラーメッセージが表示されて保存されません。
イメージ説明
関連するコードは以下の通りです。
review.rb

class Review < ApplicationRecord belongs_to :user belongs_to :game validates :user_id, presence: true validates :game_id, presence: true validates :user_id, uniqueness: { scope: :game_id, message: "は既にレビューを投稿しています" } validates :title, length: { maximum: 50 } validates :review, length: { maximum: 500 } validate :validate_score validate :validate_images has_many_attached :images #titleとreviewに値が入力されているreviewインスタンスを返す scope :review_with_value, -> { where.not(review: "").order(updated_at: "desc") } # scoreのバリデーション(1~10の数字以外はエラーになる) def validate_score regex = /[1-9]/ if score == nil || score == "" || !score.integer? errors.add(:score, "が正しくありません") elsif score.to_s.length == 1 if !score.to_s.match(regex) errors.add(:score, "が正しくありません") end elsif score.to_s.length >= 2 if score != 10 errors.add(:score, "が正しくありません") end end end # images画像のバリデーション def validate_images if images.count >= 3 images.purge errors.add(:images, "は2つまで指定できます") elsif !image_file? images.purge errors.add(:images, "はイメージファイルを指定して下さい") else case images.count when 1 then if images[0].byte_size > 2.megabytes images.purge errors.add(:images, "は2メガバイト以下の画像を指定して下さい") end when 2 then if images[0].byte_size > 2.megabytes || images[1].byte_size > 2.megabytes images.purge errors.add(:images, "は2メガバイト以下の画像を指定して下さい") end end end end # imagesで指定されたファイルがイメージファイルかどうかを調べる def image_file? case images.count when 1 then %w[image/jpg image/jpeg image/gif image/png].include?(images[0].content_type) when 2 then %w[image/jpg image/jpeg image/gif image/png].include?(images[0].content_type) && %w[image/jpg image/jpeg image/gif image/png].include?(images[1].content_type) end end end

_reviews_form.html.erb

<%= form_for @review, url: { action: yield(:action) } do |f| %> <%= render 'shared/error_messages', object: @review %> <div class="form-group"> <div><%= f.label :score %><span>(スコアだけ投稿できます)</span></div> <%= f.select :score, (1..10).to_a, {prompt: "スコアを選択してください"} , {class: "form-control", id: "score"} %> </div> <div class="form-group"> <%= f.label :title %> <%= f.text_area :title, class: "form-control", id: "title" %> </div> <div class="form-group"> <div><%= f.label :review %><span>(レビューを空欄にするとゲーム詳細ページにレビューが表示されません)</span></div> <%= f.text_area :review, class: "form-control", id: "review" %> </div> <div class="form-group"> <div><%= f.label :images %><span class="file_number">1</span></div> <%= f.file_field :images, multiple: true, class: "form-control", id: "file_field" %> </div> <div class="form-group"> <div><%= f.label :images %><span class="file_number">2</span></div> <%= f.file_field :images, multiple: true, class: "form-control", id: "file_field" %> </div> <%= hidden_field_tag(:game_id, @review.game_id) %> <%= f.submit yield(:button_text), class: "btn btn-success" %> <% end %>

reviews_controller.rb

class ReviewsController < ApplicationController before_action :find_resource, except: [:new, :create] before_action :logged_in_user, only: [:new, :create, :edit, :update, :destroy] def new @review = Review.new @review.game_id = params[:game_id] end def create @review = current_user.reviews.new(review_params) @review.game_id = params[:game_id] if @review.save flash[:success] = "レビューが投稿されました" redirect_to Game.find(params[:game_id]) else render "new" end end def show end def edit end def update if current_user == User.find(@review.user_id) if @review.update_attributes(review_params) flash[:success] = "レビューが更新されました" redirect_to game_path(@review.game_id) else render "edit" end else flash[:danger] = "このレビューは編集できません" redirect_to game_path(@review.game_id) end end def destroy if current_user == User.find(@review.user_id) if @review.destroy flash[:success] = "レビューが削除されました" redirect_to game_path(@review.game_id) else flash[:danger] = "レビューは存在しません" redirect_to game_path(@review.game_id) end else flash[:danger] = "このレビューは削除できません" redirect_to game_path(@review.game_id) end end private def review_params params.require(:review).permit(:score, :title, :review, images: []) end def find_resource @review = Review.find(params[:id]) end end

フォームを送信した時のログは以下の通りです。

Started POST "/reviews" for 172.18.0.1 at 2020-06-25 20:52:50 +0000 Cannot render console from 172.18.0.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by ReviewsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"93R7dbCejITKT8m6U9vsXGRwyoZFYkCdnuPorhrZQceJShF7CqiHjWeDZwqX2o61OP5ll6P600aQjNbYAROnWw==", "review"=>{"score"=>"5", "title"=>"", "review"=>"hoge", "images"=>[#<ActionDispatch::Http::UploadedFile:0x00007fac64326cd0 @tempfile=#<Tempfile:/tmp/RackMultipart20200625-1-fr3buu.jpg>, @original_filename="バイオ3.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"review[images][]\"; filename=\"\xE3\x83\x8F\xE3\x82\x99\xE3\x82\xA4\xE3\x82\xAA\xEF\xBC\x93.jpg\"\r\nContent-Type: image/jpeg\r\n">]}, "game_id"=>"4", "commit"=>"投稿"} User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] ↳ app/helpers/sessions_helper.rb:23  Disk Storage (6.3ms) Uploaded file to key: MKZwRH7SmEZbxnv6S2R9VzBX (checksum: NC1O8MLXS28QNfdAsUVu8g==)  (0.2ms) BEGIN ↳ app/controllers/reviews_controller.rb:11 ActiveStorage::Blob Create (0.3ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["key", "MKZwRH7SmEZbxnv6S2R9VzBX"], ["filename", "バイオ3.jpg"], ["content_type", "image/jpeg"], ["metadata", "{\"identified\":true}"], ["byte_size", 19053], ["checksum", "NC1O8MLXS28QNfdAsUVu8g=="], ["created_at", "2020-06-25 20:52:50.916214"]] ↳ app/controllers/reviews_controller.rb:11  (0.5ms) COMMIT ↳ app/controllers/reviews_controller.rb:11  (0.2ms) BEGIN ↳ app/controllers/reviews_controller.rb:13 Game Load (0.2ms) SELECT "games".* FROM "games" WHERE "games"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]] ↳ app/controllers/reviews_controller.rb:13 Review Exists (0.4ms) SELECT 1 AS one FROM "reviews" WHERE "reviews"."user_id" = $1 AND "reviews"."game_id" = $2 LIMIT $3 [["user_id", 1], ["game_id", 4], ["LIMIT", 1]] ↳ app/controllers/reviews_controller.rb:13 ActiveStorage::Attachment Exists (0.3ms) SELECT 1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."blob_id" = $1 LIMIT $2 [["blob_id", 61], ["LIMIT", 1]] ↳ app/models/review.rb:40 ActiveStorage::Blob Destroy (0.4ms) DELETE FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 61]] ↳ app/models/review.rb:40  Disk Storage (0.5ms) Deleted file from key: MKZwRH7SmEZbxnv6S2R9VzBX  Disk Storage (0.9ms) Deleted files by key prefix: variants/MKZwRH7SmEZbxnv6S2R9VzBX/  (0.2ms) ROLLBACK ↳ app/controllers/reviews_controller.rb:13 Rendering reviews/new.html.erb within layouts/application Rendered shared/_error_messages.html.erb (0.9ms) Rendered reviews/_reviews_form.html.erb (14.8ms) Rendered reviews/new.html.erb within layouts/application (24.1ms) Rendered layouts/_header.html.erb (1.7ms) Completed 200 OK in 179ms (Views: 148.2ms | ActiveRecord: 2.9ms)

どこを修正すればいいかわからないのでアドバイスを頂きたいです。よろしくお願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

image_file? が常に false を返しているということでしょうか。
としますと可能性が3つ

  1. images[0].content_type が期待した値を返していない
  2. 一つしか添付していないのに 空の データが来て、images.count が1以外に成っていて、when 2 に常に行っている
  3. そのタイミングではimages.countが期待通りに動かず、when 1,2 いずれにもならずに、caseを抜けるので false

そのタイミングでの images の内容を確認してみるとはっきりすると思います

投稿2020/06/26 23:14

winterboum

総合スコア23567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sassan738

2020/06/27 09:27

回答頂きありがとうございます。 原因は3のimages.countが期待通りに動いていないことでした。 rails consoleでimagesに画像を添付したreviewインスタンスにreview.images.countとした時は添付している画像の枚数を返してくれていたのですが、validate_imagesメソッドの途中にdebuggerを挿入して、images.countとしてみると画像を添付していても0が返ってきていました。 validate_imagesを編集して、期待通りにバリデーションできるようになりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問