Associated Paperclip Validation

I am using Paperclip to show screenshots of apps. It’s on a model called screenshots which belongs_to: :app and has an image attribute for the attachment.

I want the App model to validate that there is at least one screenshot before it gets saved, which seems to be working with: validates :screenshots, presence: true I get the proper Screenshots can’t be blank in @app.errors.full_messages when I try to submit without one.

However, using simpleform (or vanilla rails), I get no error message displaying around the field it self unlike all the other fields. I’ve tried a few things like moving the validation around to the screenshot model and using validates_associated on the app model. Nothing seems to work.

So, my question is A) am I validating correctly and B) how can I get my validation errors to show up on the form?

Hi @edwardloveall, are you displaying the errors in the form with something like:

<% if @post.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
 
    <ul>
    <% @post.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

And creating a styling hook around the form fields:

<div class="field_with_errors">
 <input id="post_title" name="post[title]" size="30" type="text" value="">
</div>

I’m asking because I tend to forget this :slight_smile:

edit: both the code samples are from the Rails Guides

Mostly, yes. I was using a partial that did basically what you showed for errors, but simpleform actually has a helper:

<%= f.error_messages %>

And then each error message is displayed inline in the html:

<div class="field_with_errors">
  <label for="post_title">Title</label>
  <input id="post_title" name="post[title]" type="text">
  <span class="error">can't be blank</span>
</div>

More or less that, but I’ve simplified it.

Well, in that case the only thing I can think of checking is that the class “field_with_errors” does have styles applied in the css. Wish I could help more :frowning:

Yeah, it doesn’t show up, which is mostly the problem. Simpleform knows that it’s a required field, rails triggers the validation, I just get no field wrapping.

Thanks though!

Which method are you using in your controller to save the form, “save” or “save!” ?

Just save

def create
  @app = App.new(params[:app])

  if @app.save
    redirect_to admin_apps_path
  else
    @app.screenshots.build
    render 'new'
  end
end

I think the problem might be with the placement of your “@app.screenshots.build” in the ‘negative’ branch of the method. If the association is properly set and validated you shouldn’t need to explicitly build the association object, Rails should pick up that App “has screenshots” and save both objects correctly.

I’ll try that @pedromoreira! I’m currently working on a different branch and should finish that before I come back to this, but I’ll let you know how it goes.

I tried this, but what happens is when I have a validation error, the app displays the form without the screenshot field, so there’s not even a field to put an error on to.

Can you post the full code for the controller, model and form?

Sure

apps_controller.rb

class Admin::AppsController < AdminController
  def index
    @apps = App.all
  end

  def new
    @app = App.new
    @app.screenshots.build
  end

  def create
    @app = App.new(params[:app])

    if @app.save
      redirect_to admin_apps_path
    else
      @app.screenshots.build
      render 'new'
    end
  end

  def edit
    @app = find_app
  end

  def update
    @app = find_app

    if @app.update_attributes(params[:app])
      redirect_to admin_apps_path
    else
      render 'edit'
    end
  end

  def destroy
    @app = find_app
    @app.destroy

    redirect_to admin_apps_path
  end

  private

  def find_app
    @app = App.find(params[:id])
  end
end

app.rb

class App < ActiveRecord::Base
  attr_accessible :name, :website, :description, :ios_url, :android_url, :icon, :screenshots_attributes

  has_many :screenshots
  has_attached_file :icon

  accepts_nested_attributes_for :screenshots, allow_destroy: true

  validates :name, presence: true
  validates :ios_url, presence: true, unless: :android_url?
  validates :android_url, presence: true, unless: :ios_url?
  validates :description, presence: true
  validates :icon, presence: true
  validates :screenshots, presence: true
end

screenshot.rb

class Screenshot < ActiveRecord::Base
  belongs_to :app
  attr_accessible :image
  has_attached_file :image
end

_form.html.erb (rendered for new and edit in apps)

<%= simple_form_for [:admin, @app], html: { multipart: true } do |form| %>
  <h1><%= form_title(form) %></h1>

  <%= form.input :name %>
  <%= form.input :website %>
  <%= form.input :ios_url, label: "iOS Download Link" %>
  <%= form.input :android_url, label: "Android Download Link" %>
  <%= form.input :description %>
  <%= form.input :icon %>
  <%= form.simple_fields_for :screenshots do |screenshot_fields| %>
    <%= label_tag nil, "Screenshots" %>
    <%= render 'screenshot_fields', form: screenshot_fields %>
  <% end %>

  <div class="input">
    <%= link_to_add_fields("Add Screenshot", form, :screenshots) %>
  </div>

  <%= form.button :submit %>
<% end %>

_screenshot_fields.html.erb

<% if form.object.new_record? %>
  <%= form.input :image, label: false %>
<% else %>
  <%= image_tag form.object.image.url %>
  <%= form.hidden_field :_destroy %>
  <%= link_to "Delete Screenshot", "#", class: "js-remove-fields remove-fields" %>
<% end %>

I wanted to try this for myself to see if I could help any further, but I didn’t find the time.

While searching around trying to understand the problem I’ve found that, by using ‘accepts_nested_attributes_for’, you might get the ‘Screenshots can’t be blank’ error in ‘@app.errors.full_message’, but it might not be able to identify the proper field to highlight in the form. There is a open issue in the rails repo that might offer further information.

Also, this seems to have a lot of ‘moving parts’ and it would probably be best to try and find a simpler solution.

Anyway, hope this helps!

So, I did eventually fix this, with Chad’s help. The validation is working fine, but the problem is the actual attribute (:image) isn’t being validated. It’s :screenshot. So simple_form doesn’t have any idea where to put the error. So, I used an if statement in my view

<% if @app.errors[:screenshots].present? %>

to check for errors, and then display something accordingly. Not the cleanest, but it works.