🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby

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

Ruby on Rails

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

Q&A

解決済

1回答

4826閲覧

seeds.rbの画像データの定義の方法がよく分からない

divclass123

総合スコア35

Ruby

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

Ruby on Rails

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

0グッド

0クリップ

投稿2021/01/31 13:16

このアプリのgithub

前提・実現したいこと

サンプルデータを差し込みたくて,投稿の画像を定義したいのですが
いくつかエラーが出てしまいます

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

Drink.create!( name: "ハウスブレンド", price: 1050, explain: " #RICH #LIVELY ナッツやココアの味わいが特徴で、スターバックス ローストによって引き出された香りやコクなど、すべてのバランスがよいコーヒー。", user_id: 6, region_id: 3, body_id: 3, acidity_id: 3, processing_id: 2, image: File.open("app/assets/images/houseblend.jpeg") )

と書いたら

ArgumentError: Could not find or build blob: expected attachable, got #File:app/assets/images/houseblend.jpeg

が出てしまいます。

houseblend = Drink.create!( name: "ハウスブレンド", price: 1050, explain: " #RICH #LIVELY ナッツやココアの味わいが特徴で、スターバックス ローストによって引き出された香りやコクなど、すべてのバランスがよいコーヒー。", user_id: 6, region_id: 3, body_id: 3, acidity_id: 3, processing_id: 2 ) houseblend.image.attach(io: File.open(Rails.root.join("app/assets/images/houseblend.jpeg")),filename: "houseblend.jpeg")

と書いたら

ActiveRecord::RecordInvalid: Validation failed: Image can't be blank

とエラーが出てしまいます。

houseblend = Drink.create!( name: "ハウスブレンド", price: 1050, explain: " #RICH #LIVELY ナッツやココアの味わいが特徴で、スターバックス ローストによって引き出された香りやコクなど、すべてのバランスがよいコーヒー。", user_id: 6, region_id: 3, body_id: 3, acidity_id: 3, processing_id: 2, image: Drink.image.attach(io: File.open(Rails.root.join("app/assets/images/houseblend.jpeg")),filename: "houseblend.jpeg") )

と定義したら

NoMethodError: undefined method `image' for #Class:0x00007f93413afe90

となりました。

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

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

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

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

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

guest

回答1

0

ベストアンサー

一番最初のパターンで、

File.open(Rails.root.join("app/assets/images/houseblend.jpeg"))

~~とやってもうまくいかないでしょうか。
~~

完全に読み違えていました。
Drinkのimageが必須になっているのでこれが任意にできるのなら簡単なんですけれど。

手元で試せないのでうまくいくか未確認ですが、ActiveStorage::Blob.create_and_upload! を使ってみるとどうなるでしょうか。
最初のパターンでこう書きます。

iamge: ActiveStorage::Blob.create_and_upload!(io: File.open(Rails.root.join("app/assets/images/houseblend.jpeg"))

Rails.root.joinはなくても動く気はしますけれど。

--

こうすればうまくいくかな?と考えるに至ったプロセスを説明します。

よくある使い方はRails Guideなんかに書いてあるやつですね。
https://railsguides.jp/active_storage_overview.html#file-io-objects%E3%82%92%E3%82%A2%E3%82%BF%E3%83%83%E3%83%81%E3%81%99%E3%82%8B

例えばすでに保存済みの User モデルのインスタンス、@user に対して @user.image.attach みたいな使い方を行えます。
実際に試した2個目のやり方はこれをやろうとしたんだと思いますが、Drink モデルの image は必須になっており、後から画像だけ追加というやり方は今回は使えません。

で、 ArgumentError: Could not find or build blob: expected attachable, というエラーメッセージです。
日本語に訳すと「ファイルが見つからないか、またはblobの作成に失敗しました」みたいな感じです。
なので、blobデータを必要としているように見えます。

ActiveStorageは active_storage_blobsactive_storage_attachments の二つのテーブルで機能を実現しています。
これはつまり ActiveStorage::Blob と ActiveStorage::Attachment の二つのモデルで構成されていて、今回はその ActiveStorage::Blob を必要としているエラーだと解釈しました。
上の attach メソッドも、中身を見てみると blob を生成しているんですよね。

def attach(attachable) blob_was = blob if attached? blob = create_blob_from(attachable) unless blob == blob_was transaction do detach write_attachment build_attachment(blob: blob) end blob_was.purge_later if blob_was && dependent == :purge_later end end

create_blob_from がその blob を生成している部分。
この中身をさらに追ってみると

def create_blob_from(attachable) case attachable when ActiveStorage::Blob attachable when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile ActiveStorage::Blob.create_after_upload! \ io: attachable.open, filename: attachable.original_filename, content_type: attachable.content_type when Hash ActiveStorage::Blob.create_after_upload!(attachable) when String ActiveStorage::Blob.find_signed(attachable) else nil end end

こんな感じ。

このメソッドは残念ながら private メソッドなのでそのまま呼べないんですが、多分 public メソッドで blob を作成するものがあるだろうなと探したのが https://edgeapi.rubyonrails.org/classes/ActiveStorage/Blob.html#method-c-create_and_upload-21 って訳です。
上のコード内でも出てきてますね。

Creates a new blob instance and then uploads the contents of the given io to the service. The blob instance is going to be saved before the upload begins to prevent the upload clobbering another due to key collisions. When providing a content type, pass identify: false to bypass automatic content type inference.

って説明を読む限りもそういう目的で使う感じですしね。

実際にこれを使っている実例を探してみたものの見つけられなかったので、残念ながらサンプル的なものは提供できなかったのですが。 attach メソッド内部でも使われていることと同じことをやる以上、非常に公式な使い方なのでまあ大丈夫だろうなと思いました。

私はそれほど ActiveStorage 使っていないので少々調べるのに時間がかかりましたが、慣れている人ならもっとスムーズに調べられるかもしれません。
コツとしては

  • エラーメッセージを読む(言語やフレームワークがうまくいかない理由を教えてくれているので、一番のヒント)
  • 使おうとしている機能やライブラリ(ここでは ActiveStorage)の動作の概要を理解する
  • RailsGuideなどで紹介されている一般的な利用法で呼び出すメソッドが、Railsやgem内部でどう動いているかを調べる(この場合は attach メソッド)

というところかなと思います。
でも、これを解決できるのは個人的な感覚では中級者以上で、ちょっと難しいかな、という気はします。

実際にはそこまで調べずに解決できないかなーと、seed 側でこの手の問題を解決した情報がないかとか探してみたんですが、まあたいていはあとから attach する RailsGuide のパターンでやっている例ばかりでした。
そのものズバリの例をどこかで見つけられると上の三つのステップなんていらないんですが、まあそういつも楽できるとは限りません。

投稿2021/01/31 14:46

編集2021/02/01 17:38
oakbow

総合スコア227

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

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

divclass123

2021/02/01 16:15

ありがとうございます!試してみます
divclass123

2021/02/01 16:38

image: ActiveStorage::Blob.create_and_upload!(io: File.open("app/assets/images/houseblend.jpeg"),filename: "houseblend.jpeg") こんな感じで上手くいきました! ありがとうございます! ActiveStorage::Blob.create_and_upload!みたいな記事は出てこなかったのですが、なぜこのように書くと上手くいくと思われたのですか?
oakbow

2021/02/01 17:09

解決のプロセス、ちょっと長くなるので回答に補足しました。 こんな感じで OSSのソースを読みつつ調査を進められるとかなり自力で解決できるようになると思います。
divclass123

2021/02/01 17:57

ご丁寧にありがとうございます! 今、ローカルでは上手く行ったんですが、本番環境に反映されないのでそこの問題を解決したいと思います!
oakbow

2021/02/01 18:28

ここで指摘するか迷ったんですが、assets ディレクトリの画像を指定しているのはなぜですか? assets ディレクトリの画像は原則的に、サイト内で汎用的に利用される画像を格納するためのものです。 これらはAsset Pipeline で管理されるため、ステージングや本番環境では`app/assets/images/houseblend.jpeg` というファイルパスでアクセスできる状態では保持されません。 個別のモデルで登録する画像であれば、例え全く同じ画像を使用したいのであっても別のディレクトリに保存するべきです。 seed.rbは通常 db ディレクトリの下に置きますが、そこで使用する画像であれば db/fixtures ディレクトリなどを作り、そちらに置くのがセオリーかなと思います。
oakbow

2021/02/01 18:33

また、seed データは 「サービス運用で不可欠となるマスターデータの投入」「開発環境でとりあえず動作確認や検証に必要な最低限のサンプルデータの投入」などに用いられます。 例えばサービスによっては都道府県データをあらかじめテーブルに格納しないと動作させられないので、そのために前者の目的で seed データを作成するというのはよくある事例です。 やろうとしていることはあくまでサンプルデータの投入に思えたのですが、どちらのデータでしょうか? サンプルデータであればそもそも本番環境では実行する必要はないはずで、せいぜい開発環境かテスト環境程度でしょう。 本番やステージングにサンプルデータを投入する必要が出てくることがないとは言いませんが、その辺りの切り分けを考えてseedデータを作ったほうがいいとは思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問