Wednesday, April 20, 2016

Rails 4 - Carrierwave image not uploading

Leave a Comment

UPDATE: Thanks everyone for the help in trying to figure this out. I decided to give up trying to learn and added another image attribute to the profile model. This will have to do for now.

Im trying to make an app in rails 4.

I have a user model, which has an avatar attribute on it.

I also have a profile model.

User :has one profile Profile :belongs to User

The reason I split them is because profile contains all the variables and user contains all the fixed attributes that cannot be changed without admin permission.

The exception is that my user model has an avatar (image) attribute which I want to allow users to change.

I have user carrierwave in other parts of my code and it works. However, something is wrong in this use case.

I have an avatar uploader with:

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    def cache_dir     "#{Rails.root}/tmp/uploads"   end    # Provide a default URL as a default if there hasn't been a file uploaded:   # def default_url   #   # 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    process :resize_to_fit => [800, 800]   # Create different versions of your uploaded files:   version :thumb do     process :resize_to_fill => [200,200]   end    version :profile do     process :resize_to_fill => [345,245]   end    version :wide do     process :resize_to_fill => [951,245]   end    version :preview do     process :resize_to_fill => [90,90]   end    version :small do     process :resize_to_fill => [35,35]   end     # Add a white list of extensions which are allowed to be uploaded.   # For images you might use something like this:   def extension_white_list      %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 

In my user model i have:

class User < ActiveRecord::Base   mount_uploader :avatar, AvatarUploader end 

In my user controller - I whitelist the :avatar in strong params.

In my profile controller strong params I've tried adding:

params.require(:profile).permit(:title, :overview, user_attributes: [:avatar]) 

And I have also tried allowing user attributes in my profile model (as follows):

belongs_to :user accepts_nested_attributes_for :user 

I have a partial form in my views users folder.

<%= simple_fields_for :user,  html: { multipart: true } do |f| %>   <%= f.error_notification %>   <div class="form-inputs">     <%= f.input :avatar, as: :file, :label => "Add a profile image (head shot)" %>   </div> <% end %> 

That partial is included in my profiles form.

I then want to display the user avatar in my profile show view as:

<div class="col-md-5 col-lg-4 vc-photo" style= "background-image: url(<%= image_url @profile.user.avatar.url if @profile.user.avatar? %>);">&nbsp;</div> 

However, it just renders a blank.

I tried:

);"> 

but it still just renders blank.

Can anyone see what I've done wrong?

When i try to test in the rails console by checking the user.avatar, I get this:

u.avatar => #, last_sign_in_ip: #, confirmation_token: "73abb47df224dbc3a612b46ced66e1aba...", confirmed_at: "2016-04-02 07:13:57", confirmation_sent_at: "2016-04-02 22:59:44", unconfirmed_email: "testiest@gmail.com", failed_attempts: 0, unlock_token: nil, locked_at: nil, created_at: "2016-04-02 07:13:57", updated_at: "2016-04-02 22:59:43", avatar: nil, approved: false>, @mounted_as=:avatar>

TAKING THE SUGGESTION BELOW

I change my forms so that profile form now has:

  <%= render 'users/profileimgform', f: f %> 

The users/profileimgform now has:

<%= simple_fields_for :user,  html: { multipart: true } do |ff| %>   <%= f.error_notification %>                <div class="form-inputs">                 <%= ff.input :avatar, as: :file, :label => "Add a profile image (head shot)" %>                </div>           <% end %> 

I'm not sure whether I need to incorporate the 'ff' into the line in my profile form. I tried replacing the last 'f' with 'ff' but got an error asking me whether it should be a single 'f'.

When I try this, the console still shows that the user.avatar is 'nil'.

4 Answers

Answers 1

I guess you have error with this code:

<%= simple_fields_for :user,  html: { multipart: true } do |f| %>   <%= f.error_notification %>   <div class="form-inputs">     <%= f.input :avatar, as: :file, :label => "Add a profile image (head shot)" %>   </div> <% end %> 

Look at html that is generated. I think there should be something like this:

<%= simple_form_for :profile do |f| %>   ...   <%= f.simple_fields_for :user do |ff| %>     <%= ff.input :avatar %>   <% end %> <% end %> 

Answers 2

shouldn't accepts_nested_attributes_for be on the model with has_..association? there's clearly smth that i dont understand there because docs say: Nested attributes allow you to save attributes on associated records through the parent.http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html you are kinda reversing the logic there. also let me understand better: u say: "...user contains all the fixed attributes that cannot be changed without admin permission. The exception is that my user model has an avatar (image) attribute which I want to allow users to change" -- (but above u say cannot be changed without admin permission). maybe there's an easier way to solve all this. do you want the image to be required when users sign up? or only update after they sign up? in either case image can be uploaded during sign up and update...and then you just show it on the profile page. or if you want to sign users up without image and only have them add image through profile then image attr should be on the profile model/table... maybe i really misunderstood everything here, but only trying to help...

Answers 3

Notice that nested form or complex form should connect model and association with form_for and fields_for helper, like this:

<%= form_for @person do |f| %>   Addresses:   <ul>     <%= f.fields_for :addresses do |addresses_form| %>       <li>         <%= addresses_form.label :kind %>         <%= addresses_form.text_field :kind %>       </li>     <% end %>   </ul> <% end %> 

The way to connect between model and associations is

  • form_for @person builds person form instance f
  • use person form instance f to build association form f.fields_for :addresses instance addresses_form
  • use association form input to upload file addresses_form.input ...

Last, I guess simple_fields_for as extra helpers of simple_form, perhaps it only works for form_for helper.


Here is a example for given models and controllers:

app/models/user.rb

class User < ActiveRecord::Base   mount_uploader :avatar, AvatarUploader   has_one :profile end 

app/models/profile.rb

class Profile < ActiveRecord::Base   belongs_to :user   accepts_nested_attributes_for :user end 

Actually, there's two ways to modify model's nested attributes.

  1. Modify the model and the nested attributes at the same form

You should build a complex form with both of your model and associations

More info: http://guides.rubyonrails.org/form_helpers.html#building-complex-forms

app/controllers/profile_controller.rb

class ProfileController < ApplicationController    private    def profile_params     params.require(:profile).permit(:title, user_attributes: [:avatar])   end end 

app/views/profile/edit.html.erb

<%= form_for :profile do |f| %>   Avatar:   <ul>     <%= f.fields_for :user do |uf| %>       <li>         <%= uf.label :avatar %>         <%= uf.file_field :avatar %>       </li>     <% end %>   </ul> <% end %> 
  1. Modify the nested attributes only

You could build a normal form just for that association attribute, but it's tricky and vague when you have to initialize it, so I rather not to provide sample code here than confusing you.

Answers 4

I couldn't figure this out. I gave up trying to learn and instead found a way around the problem. I moved avatar from user to profile and I no longer face this problem. Thanks anyway to those that tried to help. I appreciated the suggestions for things to try.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment