Reputation: 974
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
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