在Rails中结合group_by使用Pagy Gem实现分组数据分页
解决分组后分页的两种方案
你现在的问题是分组后直接分页会破坏联赛的分组结构,结合你用的Pagy,给你两种可行方案:
方案一:分页联赛(优先推荐)
这种方式是先分页联赛,每个页面展示若干个联赛的所有赛事,逻辑简单且能保持联赛的完整性,不会出现同一个联赛拆分到不同页面的情况。
修改控制器代码(fixtures_controller.rb)
def index @q = Fixture.ransack(params[:q]) fixtures_scope = @q.result(distinct: true).filter_by_date_range # 获取过滤后赛事对应的所有联赛(去重),按名称排序 @leagues = fixtures_scope.joins(:league).select('leagues.*').distinct.order('leagues.name') # 用Pagy分页联赛,这里设置每页5个联赛,可根据需求调整items数量 @pagy, @paginated_leagues = pagy(@leagues, items: 5) # 批量加载当前页联赛对应的所有赛事,避免N+1查询问题 @fixture_leagues = fixtures_scope.where(league_id: @paginated_leagues.pluck(:id)) .group_by { |fixture| fixture.league.name } end
修改视图代码(fixtures/index.erb)
<%= turbo_frame_tag "table" do %> <p class="loading">Loading...</p> <div class="-mx-4 overflow-hidden sm:-mx-6 md:mx-0"> <ul class="container mx-auto gap-4 list-none"> <%# 遍历分页后的联赛,确保每个联赛完整展示 %> <% @paginated_leagues.each do |league| %> <h2 class="text-base"><%= league.name %></h2> <%# 展示当前联赛下的所有赛事 %> <% @fixture_leagues[league.name]&.each do |fixture| %> <%= render partial: "fixture", locals: { fixture: fixture } %> <% end %> <% end %> </ul> <!-- 添加Pagy的分页导航组件 --> <%= pagy_nav(@pagy) %> <% end %>
优缺点:实现简单,避免N+1查询,用户能完整查看单个联赛的所有赛事;但如果某个联赛赛事极多,单个页面可能还是会有大量内容。
方案二:按赛事数量分页(保持联赛完整性)
如果需要严格控制每页的赛事总数,同时确保同一个联赛的赛事不会拆分到不同页面,需要自定义分页逻辑:
控制器代码示例(fixtures_controller.rb)
def index @q = Fixture.ransack(params[:q]) fixtures_scope = @q.result(distinct: true).filter_by_date_range # 统计每个联赛的赛事数量,并按联赛名称排序 league_counts = fixtures_scope.joins(:league) .group('leagues.id', 'leagues.name') .count(:id) .sort_by { |(id, name), count| name } items_per_page = 20 # 每页最多展示20场赛事,可调整 page = params[:page].to_i || 1 current_leagues = [] accumulated_count = 0 # 遍历联赛,确定当前页要包含的联赛(确保同一联赛不拆分) league_counts.each do |(league_id, league_name), count| # 跳过前面页面的联赛 if accumulated_count + count <= items_per_page * (page - 1) accumulated_count += count next end # 达到当前页的最大数量就停止 break if accumulated_count >= items_per_page * page current_leagues << league_id accumulated_count += count end # 加载当前页联赛的所有赛事并分组 @fixture_leagues = fixtures_scope.where(league_id: current_leagues) .group_by { |fixture| fixture.league.name } # 手动初始化Pagy对象,用于生成分页导航 total_items = fixtures_scope.count @pagy = Pagy.new(count: total_items, items: items_per_page, page: page) end
视图代码和方案一类似,只需要遍历@fixture_leagues并添加pagy_nav即可。
优缺点:严格控制每页赛事数量,同时保证联赛完整性;但实现逻辑更复杂,需要手动处理分页边界。
内容的提问来源于stack exchange,提问作者tommers
相关产品推荐
相关产品推荐

