Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CarrierWave and nested forms saving empty image object if photo :title is included in form

I'm after some advice in regards to handling nested form data and I would be ever so grateful for any insights.

The trouble is I'm not 100% sure why I require the following code in my model

  accepts_nested_attributes_for :holiday_image, allow_destroy: true, :reject_if => lambda { |a| a[:title].blank? }

I don't understand why I require to tact on on my accepts_nested_attributes_for association:

:reject_if => lambda { |a| a[:title].blank? }

If I remove this :reject_if lambda, it will save a blank holiday photo object in the database. I presume because it takes the :title field from the form as an empty string?

I guess my question is, am I doing this right or is there a better way of this this within nested forms if I want to extend my HolidayImage model to include more strings like description, notes?

Sorry If I couldn't be more succinct.

My simple holiday app.

# holiday.rb 
class Holiday < ActiveRecord::Base
  has_many :holiday_image
  accepts_nested_attributes_for :holiday_image, allow_destroy: true, :reject_if => lambda { |a| a[:title].blank? }

  attr_accessible :name, :content, :holiday_image_attributes

end

I'm using CarrierWave for image uploads.

# holiday_image.rb 
class HolidayImage < ActiveRecord::Base
  belongs_to :holiday
  attr_accessible :holiday_id, :image, :title

  mount_uploader :image, ImageUploader
end

Inside my _form partial there is a field_for block

<h3>Photo gallery</h3>
<%= f.fields_for :holiday_image do |holiday_image| %>
<% if holiday_image.object.new_record? %>

  <%= holiday_image.label :title, "Image Title" %>
  <%= holiday_image.text_field :title %>
  <%= holiday_image.file_field :image %>

<% else %>

  Title: <%= holiday_image.object.title %>
  <%= image_tag(holiday_image.object.image.url(:thumb)) %>
  Tick to delete: <%= holiday_image.check_box :_destroy %>

<% end %>

Thanks again for your patience.

like image 650
Wasabi Developer Avatar asked Nov 14 '22 03:11

Wasabi Developer


1 Answers

accepts_nested_attributes_for is normally used to build children during mass-assignment (when creating a new record, building any associated models). For instance, if you have a model like User which has_many UserPhotos, you could take multiple UserPhotos during the creation of a User, and have them all built on User creation.

I don't believe you need to deal with nested attributes since you're just mounting a single image (ImageUploader) on a single model (HolidayImage). This gives the model HolidayImage a single field, :image, which CarrierWave will use to mount an instance of the ImageUploader.

There are a few approaches to handle this, but here is some basic information you should know:

  1. You can use validates_presence_of :image to ensure the :image is present. This works because the mounted uploader implements the present? method used by the validation API (see Carrier Wave Wiki on Validating uploads with ActiveRecord). This way you don't have to test if the title is set, but instead can test if the image was uploaded before allowing the model to be created. Of course you should handle failed validations as necessary.

  2. You can add a before_save that does anything you'd like, which can ask CarrierWave if the image is present or just uploaded. In your case you can call image.present? and image_changed? respectively to test for this in your HolidayImage model. See this CarrierWave How To for an example.

  3. If you'd like to accept more than one image in a nested form, see this CarrierWave How To on using accept_nested_attributes_for.

like image 123
Daniel Jabbour Avatar answered Dec 19 '22 05:12

Daniel Jabbour