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? %>);"> </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 instancef
- use person form instance
f
to build association formf.fields_for :addresses
instanceaddresses_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.
- 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 %>
- 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.
0 comments:
Post a Comment