views:

42

answers:

2

I've got a links array that I'm saving to a database. The problem is that the records aren't saved in the order of the array ie links[1] is saved before links[2] and so on...

This is a example from the view file:

<p>
  <label for="links_9_label">Label</label>
  <input id="links_9_name" name="links[9][name]" size="30" type="text" />
  <input id="links_9_url" name="links[9][url]" size="30" type="text" />
</p>

And this is my controller:

def create
    @links = params[:links].values.collect { |link| @user.links.new(link) }

    respond_to do |format|
         if @links.all?(&:valid?)
         @links.each(&:save!)

        flash[:notice] = 'Links were successfully created.'
        format.html { redirect_to(links_url) }
      else
        format.html { render :action => "new" }
      end
    end
  end

Thanks in advance!

Alfred

A: 

Alter your schema so it has a display_order column or something similar, and have this pushed through as a hidden input on the front end.

Relying on the id column for ordering is discouraged, as it is essentially an arbitrary value created by the DB, which happens to follow a logical sequence most of the time. Having a separate column for the order won't break DRYness because it is (technically) for a different purpose.

p.g.l.hall
+1  A: 

The reason your links aren't being stored in order is that you're not presenting them to the db in order.

-- The param object is not an array, even though, to an extent it might look like one. Rather, they are a hash where the keys are integers. You can tell this is true because you need to use

params[:links].values.collect

if it was an array, you'd use

params[:links].collect

Any time you use .values method, the key order is indeterminate.

Solution

I agree with @p.g that you should be very careful about using db id's as an order. Eg suppose you want to change the order later? I often have 'list_order' as a field in my models.

To reorder the params, and save in order of the key, try the following:

links_param = params[:links]
ordered_keys = links_param.keys.sort
@links = ordered_keys.collect {|key| @user.links.new(links_param[key])}

Added: also note that the keys might well come in as strings, not integers. So if you have more than 10 of them, might be an idea to sort them as integers:

ordered_keys = links_param.keys.sort{|a,b| a.to_i <=> b.to_i}

Added in response to comment about saving the "display_order" --

First, add new field to model, :display_order. Be sure to add an index on the field since you'll be sorting on it.

Then, in your controller, you need to expose the key to the model. Eg something like this:

 links_param = params[:links]
 links_params.keys.each{|key|links_params[key][:display_order] = key.to_i}

Now when you build your user.link object, the display_order param will be set too.

Larry K
Don't get that code to work, still a strange order......how can I fetch the number from links[] and put in a seperate column?
Alfred Nerstu
Got more than 10 and with that code snippet it worked flawlessly! Thanks a lot!
Alfred Nerstu