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

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

ただいまの
回答率

88.91%

carrierwaveの画像投稿で、画像データがDBに登録されない(インスタンス変数に代入されない)

解決済

回答 2

投稿 編集

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

moai891

score 9

困っていること

carrierwave + mini_magick + deviseを用いてユーザー登録時にアバター画像を投稿できるようにしたいです。
画像投稿時、ストロングパラメーターは渡っているように見えるのですが、画像データがDBに登録されません。(インスタンス変数に代入されない)

長文となり恐れ入りますが、アドバイス頂ければ幸いです。よろしくお願いいたします。

課題に対するアクション

  • binding.pryを用いたデバッグ

① ストロングパラメーターのうち、[:avatar]のみインスタンス変数@profileに渡っていないように見える

[1] pry(#<Users::RegistrationsController>)> @profile
=> #<Profile:0x00007fb1b7dd95a8
 id: nil,
 phonenumber: "1118883333",
 old: 21,
 job: 1,
 experience: 1,
 practice_time: 1,
 detail: "よろしく",
 club: "ホゲホゲ",
 avatar: nil, //問題点。なぜかnil
 user_id: nil,
 created_at: nil,
 updated_at: nil>

②ストロングパラメーターは適切なデータを受け取っていると考えられる

=> <ActionController::Parameters {"phonenumber"=>"1118883333", "old"=>"21", "job"=>"1", "experience"=>"1", "practice_time"=>"1", "detail"=>"よろしく", "club"=>"ホゲホゲ", "avatar"=>#<ActionDispatch::Http::UploadedFile:0x00007fb1b7de7888 @tempfile=#<Tempfile:/var/folders/l9/g98ryfjs4730gx0slq67713r0000gn/T/RackMultipart20200718-27218-1f1z01b.png>, @original_filename="7071b4262c632dc0e7c7660f15459fba.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"profile[avatar]\"; filename=\"7071b4262c632dc0e7c7660f15459fba.png\"\r\nContent-Type: image/png\r\n">} permitted: true>

③@profile.avatarでは画像データが代入されている

=> #<AvatarUploader:0x00007fb1b7dcaff8
 @cache_id="1595048033-27925243257579-0005-8319",
 @cache_storage=#<CarrierWave::Storage::File:0x00007fb1b7dc2b28 @cache_called=nil, @uploader=#<AvatarUploader:0x00007fb1b7dcaff8 ...>>,
 @file=
  #<CarrierWave::SanitizedFile:0x00007fb1b7dc9b58
   @content=nil,
   @content_type="image/png",
   @file="/Users/teranishiyusuke/projects/badsuru/public/uploads/tmp/1595048033-27925243257579-0005-8319/7071b4262c632dc0e7c7660f15459fba.png",
   @original_filename="7071b4262c632dc0e7c7660f15459fba.png">,
 @filename="7071b4262c632dc0e7c7660f15459fba.png",
 @identifier=nil,
 @model=
  #<Profile:0x00007fb1b7dd95a8
   id: nil,
   phonenumber: "1118883333",
   old: 21,
   job: 1,
   experience: 1,
   practice_time: 1,
   detail: "よろしく",
   club: "ホゲホゲ",
   avatar: nil,
   user_id: nil,
   created_at: nil,
   updated_at: nil>,
 @mounted_as=:avatar,
 @original_filename="7071b4262c632dc0e7c7660f15459fba.png",
 @staged=true,
 @versions={}>

④avatarがnilだからか、@user.save時に、avatarに対するSQL文が発行されていないように見える

[4] pry(#<Users::RegistrationsController>)> exit
   (11.0ms)  BEGIN
  ↳ app/controllers/users/registrations_controller.rb:34:in `create_profile'
  User Exists? (13.0ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'raaa@r.com' LIMIT 1
  ↳ app/controllers/users/registrations_controller.rb:34:in `create_profile'
  User Create (17.4ms)  INSERT INTO `users` (`nickname`, `email`, `encrypted_password`, `created_at`, `updated_at`) VALUES ('aabbcc', 'raaa@r.com', '$2a$11$HMGyw2YYP9qDzQKbh369PeCcnxGAB5dxLIUO/vwnvqzuiRqK9xwcS', '2020-07-18 04:59:38.775316', '2020-07-18 04:59:38.775316')
  ↳ app/controllers/users/registrations_controller.rb:34:in `create_profile'
  Profile Create (16.6ms)  INSERT INTO `profiles` (`phonenumber`, `old`, `job`, `experience`, `practice_time`, `detail`, `club`, `user_id`, `created_at`, `updated_at`) VALUES ('1118883333', 21, 1, 1, 1, 'よろしく', 'ホゲホゲ', 4, '2020-07-18 04:59:38.809212', '2020-07-18 04:59:38.809212')
  ↳ app/controllers/users/registrations_controller.rb:34:in `create_profile'
   (18.7ms)  COMMIT
  ↳ app/controllers/users/registrations_controller.rb:34:in `create_profile'
No template found for Users::RegistrationsController#create_profile, rendering head :no_content
Completed 204 No Content in 344980ms (ActiveRecord: 76.7ms | Allocations: 56585)

詳細なコード

  • registration_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
  # before_action :configure_sign_up_params, only: [:create]
  # before_action :configure_account_update_params, only: [:update]

  # GET /resource/sign_up
  def new
    @user = User.new
  end

  # POST /resource
  def create
    @user = User.new(sign_up_params)
    unless @user.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new and return
    end
    session["devise.regist_data"] = {user: @user.attributes}
    session["devise.regist_data"][:user]["password"] = params[:user][:password]
    @profile = @user.build_profile
    render :new_profile
  end

  def create_profile
    @user = User.new(session["devise.regist_data"]["user"])
    @profile = Profile.new(profile_params)
    unless @profile.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new_profile and return
    end
    @user.build_profile(@profile.attributes)
    binding.pry
    @user.save
    session["devise.regist_data"]["user"].clear
    sign_in(:user, @user)
  end
  # GET /resource/edit
  # def edit
  #   super
  # end

  # PUT /resource
  # def update
  #   super
  # end

  # DELETE /resource
  # def destroy
  #   super
  # end

  # GET /resource/cancel
  # Forces the session data which is usually expired after sign
  # in to be expired now. This is useful if the user wants to
  # cancel oauth signing in/up in the middle of the process,
  # removing all OAuth session data.
  # def cancel
  #   super
  # end

  protected

  def profile_params
    params.require(:profile).permit(:phonenumber, :old, :job, :experience, :practice_time, :detail, :club, :avatar)
  end

  # If you have extra params to permit, append them to the sanitizer.
  # def configure_sign_up_params
  #   devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
  # end

  # If you have extra params to permit, append them to the sanitizer.
  # def configure_account_update_params
  #   devise_parameter_sanitizer.permit(:account_update, keys: [:attribute])
  # end

  # The path used after sign up.
  # def after_sign_up_path_for(resource)
  #   super(resource)
  # end

  # The path used after sign up for inactive accounts.
  # def after_inactive_sign_up_path_for(resource)
  #   super(resource)
  # end
end
  • profile.rb
class Profile < ApplicationRecord
  belongs_to  :user, optional: true
  validates   :old, :job, :experience, :phonenumber, :practice_time, presence: true
  mount_uploader :avatar, AvatarUploader
end
  • avatar_uploader.rb
class AvatarUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick

  # Choose what kind of storage to use for this uploader:
  storage :file
  # storage :fog

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # Provide a default URL as a default if there hasn't been a file uploaded:
  # def default_url(*args)
  #   # For Rails 3.1+ asset pipeline compatibility:
  #   # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
  #
  #   "/images/fallback/" + [version_name, "default.png"].compact.join('_')
  # end

  # Process files as they are uploaded:
  # process scale: [200, 300]
  #
  # def scale(width, height)
  #   # do something
  # end

  # Create different versions of your uploaded files:
  # version :thumb do
    process resize_to_fit: [50, 50]
  # end

  # Add a white list of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  # def extension_whitelist
  #   %w(jpg jpeg gif png)
  # end

  # Override the filename of the uploaded files:
  # Avoid using model.id or version_name here, see uploader/store.rb for details.
  # def filename
  #   "something.jpg" if original_filename
  # end
end
  • 登録フォーム
<h2>住所情報登録</h2>

<%= form_for @profile do |f| %>
  <%= render "devise/shared/error_messages", resource: @profile %>

  <div class="field">
    <%= f.label :phonenumber %><br />
    <%= f.text_field :phonenumber %>
  </div>

  <div class="field">
    <%= f.label :old %><br />
    <%= f.text_field :old %>
  </div>

  <div class="field">
    <%= f.label :job %><br />
    <%= f.text_field :job %>
  </div>

  <div class="field">
    <%= f.label :experience %><br />
    <%= f.text_field :experience %>
  </div>

  <div class="field">
    <%= f.label :practice_time %><br />
    <%= f.text_field :practice_time %>
  </div>

  <div class="field">
    <%= f.label :detail %><br />
    <%= f.text_field :detail %>
  </div>

  <div class="field">
    <%= f.label :club %><br />
    <%= f.text_field :club %>
  </div>

  <div class="field"> //画像投稿欄
    <%= f.label :avatar %><br />
    <%= f.file_field :avatar , class: "avatar"%>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

+1

Deviseを使ってユーザ登録する場合、password、email以外の項目を追加する場合はDeviseにその旨伝える必要があります。
してますか?
たとえば usernameの追加には以下のようにします。このようなことをどこかでやっていないなら、username を avaterに読み替えて行ってみてください

application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    added_attrs = [ :email, :username, :password, :password_confirmation ]
    devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
    devise_parameter_sanitizer.permit :account_update, keys: added_attrs
    devise_parameter_sanitizer.permit :sign_in, keys: added_attrs
  end
end

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/07/19 14:17 編集

    ご返信ありがとうございます。

    関係ないかもしれないのですが、コンソールをいじっていたらavatarのみ以下のエラーが出ました。
    avatarカラムはstring型で、他のデータと同様なのですが、なぜか保存できないみたいです・・・
    carrier waveの仕様なのでしょうか?

    9] pry(main)> Profile.new(avatar:2)
    TypeError: no implicit conversion of nil into String
    from /Users/teranishiyusuke/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/carrierwave-2.1.0/lib/carrierwave/uploader/cache.rb:190:in `join'

    ●carrierwave/uploader/cache.rb:190:in `join'のコード

    def workfile_path(for_file=original_filename)
    File.join(CarrierWave.tmp_path, @cache_id, version_name.to_s, for_file) ⇦cache.rb:190:in
    end

    キャンセル

  • 2020/07/19 14:36 編集

    avatarカラムはstring型なんですが、carrier waveによりhuckが定義されてるみたいで、それをアクセスするとcarrier waveの何やらが動いてfileの格納場所だとかURLだとかを持っているobjectが得られます。その辺りの仕掛けを整えるに足る情報が渡らないとエラーになるのだろう、と解釈しています

    キャンセル

  • 2020/07/19 14:49

    恐れ入りますが、huckについて教えて詳しく教えて頂くことはできますでしょうか?

    コンソールで2だけ代入しようとしてもエラーになるのですね・・・

    キャンセル

check解決した方法

0

無理矢理な感じが否めないですが、以下のコードを追加してavatarを追加できるようにしました。

  • registration_controller.rb
  def create_profile
    @user = User.new(session["devise.regist_data"]["user"])
    @profile = Profile.new(profile_params)
    unless @profile.valid?
      flash.now[:alert] = @user.errors.full_messages
      render :new_profile and return
    end
    @user.build_profile(@profile.attributes)
    @user.save
    @profile_avatar = Profile.find_by( id: @user.id ) //追加
    @profile_avatar.update!(avatar: profile_params[:avatar]) ////追加
    session["devise.regist_data"]["user"].clear
    sign_in(:user, @user)
  end

エラーの原因としては、@user.build_profile(@profile.attributes)でオブジェクトを作成後、@user.profile.avatarの値がnilになってしまっているからだと考えられます。

@profile = Profile.new(profile_params)にてProfileモデルオブジェクトを作成した時は、@profile.avatarにて登録データが格納されていたのを確認しました。

しかし、上述の通り@user.build_profile(@profile.attributes)にてオブジェクトを作成した時は、@profile.attributesにて画像格納データ(AvatarUploaderモデルオブジェクト)が作成されなかったようです。

ご協力頂きありがとうごいました。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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

  • トップ
  • Rubyに関する質問
  • carrierwaveの画像投稿で、画像データがDBに登録されない(インスタンス変数に代入されない)