Ruby on Rails:排序对象集合

问题描述:

我有一个方法将提供一个模型对象的数组。其中一些模型的属性很容易通过使用SQL的帮助来排序。我的意思是,使用。查找(:condition => {})Ruby on Rails:排序对象集合

问题是其他的一些属性不是。在展示前必须进行计算,修改或做其他事情。然后,我找到了使用集合分类对其进行分类的方法。这是,collection.sort {| a,b | a.something_to_sort < => b.something_to_sort}

OK!这是有效的。但问题是,是否有可能使某些东西变成动态变量的东西?例如,我想从UI添加一个参数,并将其分配给something_to_sort像下面,

HTML

<select name="sort"> 
    <option value="name">Name</option> 
    <option value="age">Age</option> 
    <option value="activity.category">Activity</option> 
    <option value="tax.calculate_personal_income_tax">Income Tax</option> 
    <option value="tax.calculate_withholding_tax">Withholding Tax</option> 
    <option value="doctor.name">Doctor's Name</option> 
</select> 

Rails的

params[:sort] ||= "Age" 
@people = Person.find(:all) 
@people.sort{|a,b| a.params[:sort] <=> b.params[:sort]} #Note that this is an incorrect syntax 

有没有办法做到这一点通过不必为每个排序选项编写一个排序块?

附加#1

我只是尝试这样做,这是工作了一段排序选项

@people.sort{|a,b| a.send(params[:sort]) <=> b.send(params[:sort])} 

这适用于名字和年龄,因为他们是人的特性(和它的作品了也是peopls的方法)。但对于协会来说,例如tax.calculate_personal_income_tax,事实并非如此。

所以,我的同事告诉我,以创造新的人的方法,calculate_personal_income_tax,然后从tax.calculate_personal_income_tax更改选项值calculate_personal_income_tax使发送方法将能够找到这种方法...

def calculate_personal_income_tax 
    tax.calculate_personal_income_tax 
end 

但是,这不是我真正想要的。因为我相信必须有一种方法来访问这种关联方法,而无需定义新的模型方法。如果我选择这样做,有一天人们的模型变得更大,更多的属性,更多的信息进行排序和显示。然后,会有很多像这样的方法。

另一个原因是,如果我必须定义一个新的方法,该方法应该做一些逻辑,计算或其他,而不仅仅是从其他模型中检索数据。

附加#2

最后,我只是发现通过添加参数传递给了找到方法,访问其他模型属性的方式:加入:选择

@people = Person.find(:all, :select => "persons.*, tax.tax_rate AS tax_rate", :joins => :tax, :condition => {some conditions}) 

的,其结果将是加入一个表。然后,我用send(params [:sort])这个东西来帮我排序我想要的属性。

+0

所以你*不能*使用SQL来排序结果? –

+0

是的,一些显示列不能通过SQL语句进行排序(或者,我不知道如何去做)。其中一些太复杂,因为它涉及到很多相关的模型 –

@people.sort {|a, b| a.send(params[:sort]) <=> b.send(params[:sort])} 
+0

感谢队友,我也发现了这一点。问题是,它适用于人们的属性或方法。但不是它的关联(如tax.calculate_personal_income_tax)......无论如何,再次感谢队友。 –

params[:sort] ||= "age" 
method_chain = params[:sort].split(".") 
@people = Person.find(:all) 
@sorted_people = 
    @people.sort_by do |person| 
    method_chain.inject(person) do |memo,method| 
     memo.send(method) 
    end 
    end 

这假定在PARAMS传递的值[:排序]总是能一起在指定的顺序被链接有效方法名。你要记住,任何可以通过params散列,所以这是非常不安全的,如果暴露给不可信用户(例如params[:sort] = "destroy"。哎呀。)

此外,如果你可以在SQL中做到这一点你会获得更好的表现。

事实上,我越是看这个,越是看起来不好主意。

我有一个战利品系统,这是基于一个计算值,这是基于一个可以改变的参考表。由于价值计算,它可能来自任何地方。

我的玩家/ index.html.rb看起来像。

<h1>Players</h1> 
<table> 
    <tr> 
    <th>Name</th> 
    <%= generate_headings(params) %> 
    </tr> 

    <% players.each do |player| %> 
    <tr> 
     <td><%= player.name %></td> 
     <% LootType.all.each do |loot_type| %> 
     <td><%= player.loot_rate(loot_type.name) %></td> 
     <% end %> 
    <tr> 
    <% end %> 
</table> 

的loot_rate功能被称为 “类型+ 1的玩家袭击/赃物” 运算在两个相关的表(player.raids和player.items)。

的generate_headings功能是players_helper.rb,它看起来像:

def generate_headings(params = {}) 
    sort = params[:sort] 
    headings = "" 
    LootType.all.each do |loot_type| 
    heading_text = "#{loot_type.name} Rate" 
    if (sort == loot_type.name) 
     column_heading = "<th>#{heading_text}</th>" 
    else 
     heading_url = "/players?sort=#{loot_type.name}" 
     column_heading = "<th>#{link_to heading_text, heading_url}</th>" 
    end 
    headings += column_heading 
    end 
    headings.html_safe 
end 

在players_controller.rb的我的索引行动,我有:

def index 
    sort = params[:sort] 
    if !sort 
    @players.sort! { |a,b| a.name <=> b.name } 
    else 
    @players.sort! { |a,b| b.loot_rate(sort) <=> a.loot_rate(sort) } # Reverse sort (for my purposes) 
    end 

    respond_to do |format| 
    format.html 
    format.json {render :json => @players } 
    end 
end 

这意味着,当我添加或删除我的系统中的“战利品类型”,并且玩家列表自动允许用户按照这些类型进行排序。

啰嗦的答案,但希望它有帮助。