views:

45

answers:

1

I have a very common situation and a solution, but I would like to ask the Rails experts out there if it can be improved.

I have a very typical RESTful controller where the user supplies some of the object attributes upon creation. There is a thing model, a ThingsController and various views, including new, create, and a _form partial.

A thing has two attributes,

  1. a color, which is set when they hit a link to create it (i.e. a “Create Red Thing” link that encodes the ID of red as a URL parameter)
  2. a description, which is entered by the user in the form view

I’m happy with the approach for dealing with an attribute like the description that that a user specifies in the form, but less confident about the way that I handle an attribute that is passed through via the URL parameters associated with the first click.

This is what I am doing right now (note that I have omitted error checking to simplify matters). First, my new and create methods in the controller are as follows:

def new
  @thing = Thing.new
  @thing.color = Color. find(params[:color])
end

def create
  @thing = Thing.new(params[:thing])
  @thing.color = Color. find(params[:color])

  if  @thing.save   
    flash[:notice] = "Successfully created thing."
    redirect_to somewhere_url
  else
    render :action => 'new'
  end
end

The new view just invokes the _form partial, which looks as follows:

<% form_for @thing do |f| %>
  <%= f.error_messages %>

  <%= hidden_field_tag "color", @thing.color.id %>

  <%= f.label :description %>
  <%= f.text_area :description %>
  <%= f.submit "Submit" %>
<% end %>

It seems a little messy to be passing the color ID through to the create method as a URL parameter by putting a hidden field in the form. Does this look reasonable, or is there another approach that would be better?

+2  A: 

Normally in this situation, I would put a hidden field in the form, which will hold the color_id. The nice thing about this way is that you can get by with just setting the color of the object before you render the form. So your controller changes to:

def new
  @thing = Thing.new
  @thing.color = Color. find(params[:color])  
end

def create
  @thing = Thing.new(params[:thing])
  if  @thing.save   
    flash[:notice] = "Successfully created thing."
    redirect_to somewhere_url
  else
    render :action => 'new'
  end
end

and your form will change to

<% form_for @thing do |f| %>
  <%= f.error_messages %>

  <%= f.hidden_field :color_id %>

  <%= f.label :description %>
  <%= f.text_area :description %>
  <%= f.submit "Submit" %>
<% end %>

The color is then passed through both forms, and you only need to retrieve the color in the first form. (Don't forget to add validations your Thing model to make sure it has a valid color though).

jamuraa
Thanks very much jamuraa. This is much cleaner.
Greg