You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 23:01:08