kalahari
kalahari

Reputation: 974

rails 3.2 a little sorting issue

I am doing a searchable & sortable table. Actually I made it already apart from a couple of little details. I am sorting according to column names, but rails rejects recognize 1 of them. Now, thats my index.html.erb:

<table class="pretty" border="1" cellpadding="10">  
  <tr>
    <th><%= sortable("machine_name",       "M.Name")       %></th>
    <th><%= sortable("machine_brand",      "M.Brand")      %></th>
    <th><%= sortable("machine_model",      "M.Model")      %></th>
    <th><%= sortable("control_unit_brand", "C.Unit Brand") %></th>
    <th><%= sortable("control_unit_model", "C.Unit Model") %></th>
    <th><%= sortable("tool_axis_x",        "Axis X")       %></th>
    <th><%= sortable("tool_axis_y",        "Axis Y")       %></th>
    <th><%= sortable("tool_axis_z",        "Axis Z")       %></th>
    <th><%= sortable("rotary_axis_number", "R.Axis")       %></th>
    <th><%= sortable("linear_axis_number", "L.Axis")       %></th>
    <th><%= sortable "description"                         %></th>
    <th><%= sortable("name",               "Developer")    %></th>
    <th><%= sortable "updated_at"                          %></th>
  </tr>  

  <% for conf in @confs %>  
  <tr class="<%= cycle('oddrow', 'evenrow') -%>">
    <td><%= link_to conf.machine_name, conf %></td>
    <td><%= conf.machine_brand %></td>
    <td><%= conf.machine_model %></td>
    <td><%= conf.control_unit_brand %></td>
    <td><%= conf.control_unit_model %></td>
    <td><%= conf.tool_axis_x %></td>
    <td><%= conf.tool_axis_y %></td>
    <td><%= conf.tool_axis_z %></td>
    <td><%= conf.rotary_axis_number %></td>
    <td><%= conf.linear_axis_number %></td>
    <td><%= conf.description %></td>
    <td><%= conf.developer.name unless conf.developer_id.blank? %></td>
    <td><%= conf.updated_at %></td>
  </tr>
  <% end %>
</table>

You can see how I am reaching model Conf's developer via developer_id and display its name. This part is working but, when it comes to sort it breaks! sortable action uses sort_column and this is it:

def sort_column
    ( (User.column_names.include?(params[:sort])) || (Conf.column_names.include?(params[:sort])) ) ? params[:sort] : "machine_name"
end

User is a model. Conf is another model. User has_many confs. Conf

belongs_to :developer, :class_name => 'User', :foreign_key => 'developer_id'

I think rails try to sort by name, but conf hasn't a name, gives error like

no such column: name: SELECT  "confs".* FROM "confs"

Sort_column defined with User's columns but I think rails behaves as User's columns are different than developers columns. How can I make rails to recognize developers columns? NOTE: I tried Conf.developer.column_names.include?... but get an error "undefined method developer"

Upvotes: 0

Views: 160

Answers (1)

PinnyM
PinnyM

Reputation: 35533

The simplest solution is to add a 'developer_name' column when pulling the data:

@conf = your_current_logic.
          includes(:developer).                               # eager load your developer relation
          where('users.id IS NULL OR users.id IS NOT NULL').  # a meaningless filter to force a LEFT JOIN
          select("confs.*, users.name as developer_name")

Then you can reference 'developer_name' in your sortable column.

Alternatively, you can simply substitute sortable('name', ...) with sortable('users.name', ...). You may need to modify your sort_column logic to permit this convention.

One way to do that may be like this:

def sort_column
  if params[:sort] =~ /([^\.]+)\.([^\.]+)/ &&             # check if 'table.column' format is used
       %w(users confs).include? $1 &&                     # check if table name matches expectations
       $1.classify.constantize.column_names.include?($2)  # check if column is present in table's class
    params[:sort]    # 'table.column' format
  elsif Conf.column_names.include?(params[:sort]))        # if 'column' format is used, just check Conf class
    params[:sort]    # 'column' format
  else
    "machine_name"   # default sort column
  end
end

UPDATE

So using the second method, your data retrieval might look like this:

@confs = Conf.search(params[:search]).
              includes(:developer).
              where('users.id IS NULL OR users.id IS NOT NULL').
              order(sort_column + ' ' + sort_direction).
              paginate(:per_page => 10, :page => params[:page])

And any ambiguous columns (e.g. updated_at) should be qualified by table name:

<th><%= sortable "confs.updated_at"                      %></th>

And since you're going to be requiring qualified names, it sounds like the second method is the way to go. Note that using classify.constantize to dynamically check the model class may have an element of risk - you can use a whitelist of available classes to mitigate this. Updated to demonstrate this.

Upvotes: 1

Related Questions