Kyle C
Kyle C

Reputation: 4097

Rails select form with the two options

I am trying to create an upload form that allows the user to select a blank value which will then result in another upload process. ( I substituted in "class" for my actual class name).

My upload form is

<%= form_for @upload, :url => class_uploads_path(@uploader), :method =>            
 :post, :html =>{ :multipart => true } do |f| %>
<%= render "shared/error_messages", :target => @upload %>

  <p>This media relates to...<br/>
 <%= select "upload", "class_id", Class.all.collect {|class| [class.name,class.id] }, {        
  :include_blank => true } %></p>

  <p>Title:<br/>
  <%= f.text_field :title %></p>

 <p>Description:<br/>
  <%= f.text_area :description, :rows => 3 %></p>

 <p>File to upload:<br/>
  <%= f.file_field :data %></p>

  <p><%= f.submit 'Upload for review' %></p>
   <% end %>

Here is my create method, if the user leaves the select form blank I would like the first upload action to run and if they chose a class I want the second upload to run.

def create          
  upload_params = params[:upload] || params[:image] || params[:video]
   @class = Class.find(upload_params[:class_id]) || nil

  begin
    if upload_params[:data]
      file_name = upload_params[:data].original_filename
      file_contents = upload_params[:data].read
    end
  rescue
    @upload = Upload.new
    flash[:error] = "Could not find upload file data.  Please reselect file."
    render :action => 'new' and return
  end

  begin
    if @class.nil?
            @upload = Upload.factory({ :file => file_contents, 
                                        :name => file_name, 
                                        :network => @uploader,
                                        :title => upload_params[:title],
                                        :description => upload_params[:description] })
    else
         @upload = Upload.factory({ :file => file_contents, 
                                        :name => file_name, 
                                        :class => @class,
                                        :network => @uploader,
                                        :title => upload_params[:title],
                                        :description => upload_params[:description] })
    end
    @upload.save!

I have tried a number of different hacks, I am trying to avoid adding a new column to the database. Thanks in advance.

Upvotes: 0

Views: 318

Answers (2)

Alex D
Alex D

Reputation: 30485

Kyle, if the blank option is chosen, I believe params[:class_id] should be "" (a blank string). You should check this -- look in your development logs and see what params are getting passed back to your controller method, both when you select a class and when you don't.

If that is correct:

class_id = params[:class_id]
@class   = Class.find(class_id.to_i) if class_id and not class_id.empty?

As gg_s pointed out, there is lots of room for refactoring the code you posted. But if you just want to get the existing code working, this might be helpful.

In future, when something is not working, rather than just "hacking on it until it works", look at the logs/console output for more information. Add logging statements to the controller method, printing out the values of variables, etc. Then try it out, and look at the logs again to see what is happening inside the method.

Upvotes: 1

Substantial
Substantial

Reputation: 6682

If the only difference between both uploads is a :class => @class pair, there should be no reason to have two separate actions.

I assume the following:

  • We're working with Upload objects here (upload.rb model, uploads_controller.rb controller)
  • Upload.factory is a processor method which handles and formats upload data, resulting in a complete Upload object with a file and metadata.

If so, all of that controller logic can be moved to the model. Pass data to the model with instance variables (via attr_accessor, not by adding columns). Slap a before_create filter on factory so it processes your upload before saving.

Use validations to handle "no data" errors.

Here's an example. upload.rb:

before_create :factory # run your factory method before creating

attr_accessor :form_data, :form_network, :form_title, \ # settable from controller
              :form_description, :form_class_id

validates :form_data, :presence => true, :on => :create, \  # fail if no data
          :message => "No file data.  Please reselect file."

private

def factory
  file_name = self.form_data.original_filename
  file_contents = self.form_data.read
  class = Class.where(:id => self.form_class_id).first # returns Class or nil
  # if class is nil, handle it however you need to here
  # access self.form_network, self.form_title, self.form_description directly

  # ... factory magic ...
end

Now your controller can be as tidy as this:

def create
  upload_params = params[:upload] || params[:image] || params[:video]

  @upload = Upload.new({ :form_data        => upload_params[:data], 
                         :form_network     => @uploader, # <-- undefined!
                         :form_class_id    => upload_params[:class_id],
                         :form_title       => upload_params[:title],
                         :form_description => upload_params[:description]})
  if @upload.save
    redirect_to @upload
  else
    render :action => "new"
  end
end

Note that @uploader is undefined.

@upload.save will validate the @upload. If file data is absent, save will fail, kicking you back to the new action with a "no data" error message. Otherwise, if validation passes, factory is called, then the record saves.

Upvotes: 0

Related Questions