【问题标题】:Sorting/Paginating/Filtering Complex Multi-AR Object Tables in Rails在 Rails 中对复杂的多 AR 对象表进行排序/分页/过滤
【发布时间】:2010-05-01 05:34:33
【问题描述】:

我有一个从多 ActiveRecord 对象数组中提取的复杂表。此列表是所有特定用户“最喜欢的”项目(歌曲、消息、博客帖子等)的组合显示。这些项目中的每一个都是成熟的 AR 对象。

我的目标是为用户提供一个简化的搜索、排序和分页界面。用户不需要知道歌曲有歌手,消息有作者——对于最终用户,表中的两个条目都将显示为“用户”。因此,搜索框将只是一个下拉列表,询问他们要搜索的内容(用户名、创建时间等)。在内部,我需要将其转换为适当的对象搜索、组合结果并显示。

我可以单独进行分页 (mislav will_paginate)、排序和过滤,但是将它们组合在一起时会遇到一些问题。

例如,如果我对项目的组合列表进行分页,分页插件可以很好地处理它。由于分页发生在应用程序与数据库中,因此效率不高,但我们假设预期的用例表明绝大多数用户将拥有少于 30 个收藏项目以及所有其他行为、服务器功能等。表示这不会成为瓶颈。

但是,如果我希望对列表进行排序,则无法通过分页插件对其进行排序,因为它依赖于假设结果集来自单个 SQL 查询,并且字段名称始终保持一致。因此,我必须通过 ruby​​ 对合并后的数组进行排序,例如

@items.sort_by{ |i| i.whatever }

但是,由于项目不共享通用名称,我必须首先询问对象,然后调用正确的排序依据。例如,如果用户希望按用户名排序,如果排序的对象是消息,我按作者排序,但如果对象是歌曲,我按歌手排序。这一切都非常恶心,感觉不像红宝石。

过滤器也会出现同样的问题。如果用户过滤“父项”(消息的线程,歌曲的专辑),我必须将其转换为适当的集合对象方法。也很恶心。

这不是确切的设置,但足够接近。请注意,这是一个遗留应用程序,因此更改它非常困难,尽管并非不可能。另外,是的,可以做一些 DRY,但不要专注于以下代码的风格或优雅。然而,解决方案的风格/优雅很重要! :D

型号:

class User < ActiveRecord::Base
  ...
  has_and_belongs_to_many :favorite_messages, :class_name => "Message"
  has_and_belongs_to_many :favorite_songs,    :class_name => "Song"
  has_many                :authored_messages, :class_name => "Message"
  has_many                :sung_songs,        :class_name => "Song"
end

class Message < ActiveRecord::Base
  has_and_belongs_to_many :favorite_messages  
  belongs_to              :author, :class_name => "User"
  belongs_to              :thread
end

class Song < ActiveRecord::Base
  has_and_belongs_to_many :favorite_songs  
  belongs_to              :singer, :class_name => "User"
  belongs_to              :album
end

控制者:

def show
  u = User.find 123

  @items = Array.new
  @items << u.favorite_messages
  @items << u.favorite_songs
  # etc. etc. 

  @items.flatten!

  @items = @items.sort_by{ |i| i.created_at }

  @items = @items.paginate :page => params[:page], :per_page => 20
end

def search

  # Assume user is searching for username like 'Bob'

  u = User.find 123

  @items = Array.new
  @items << u.favorite_messages.find( :all, :conditions => "LOWER( author ) LIKE LOWER('%bob%')" )
  @items << u.favorite_songs.find( :all, :conditions => "LOWER( singer ) LIKE ... " )
  # etc. etc. 

  @items.flatten!

  @items = @items.sort_by{ |i| determine appropriate sorting based on user selection }

  @items = @items.paginate :page => params[:page], :per_page => 20
end

查看:

   #index.html.erb
   ...
   <table>
     <tr>
       <th>Title (sort ASC/DESC links)</th>
       <th>Created By (sort ASC/DESC links))</th>
       <th>Collection Title (sort ASC/DESC links)</th>
       <th>Created At (sort ASC/DESC links)</th> 
     </tr>
     <% @items.each |item| do %>
       <%= render { :partial => "message", :locals => item } if item.is_a? Message %>
       <%= render { :partial => "song",    :locals => item } if item.is_a? Song %>
     <%end%>
   ...
   </table>

   #message.html.erb
   # shorthand, not real ruby
   print out message title, author name, thread title, message created at

   #song.html.erb
   # shorthand
   print out song title, singer name, album title, song created at

【问题讨论】:

    标签: ruby-on-rails ruby sorting filter pagination


    【解决方案1】:

    你应该试试thinking-sphinx。为了了解它的工作原理,您可以访问我的网站 www.campzero.com 并尝试使用“Dhaka”进行搜索

    Thinking-sphinx 是一个 Rails 插件,可帮助您搜索多个模型/字段,并内置分页/排序功能。更多信息请访问thinking-sphinx 网站。

    【讨论】:

    • 我喜欢它,但为这个小功能安装一个搜索引擎似乎有点过头了(因为让 sphinx 启动并运行等并非易事)
    【解决方案2】:

    作为一个仅供参考,我设法用一个非常胖的模型完成了所有这些,但它似乎工作得很好。

    【讨论】:

      【解决方案3】:

      您还可以在item 的每种类型中添加#collection_title 和#title 方法,并通过ducktyping 进行排序。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-08
        • 1970-01-01
        • 2016-05-08
        相关资源
        最近更新 更多