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

Rails中使用pagy gem时attr_accessor值丢失问题求助

问题原因

你遇到的问题核心是:attr_accessor定义的属性只存在于内存中的对象实例,不会持久化到数据库。

你在控制器里先加载所有Post对象到内存,给它们的icon和subtitle赋值,但执行@pagy, @posts = pagy(@posts)时,pagy会对传入的ActiveRecord::Relation重新执行分页查询(添加LIMIT/OFFSET语句),返回的是从数据库重新加载的全新Post实例——这些新实例没有你之前在内存中赋值的icon和subtitle,所以视图里调用post.icon会返回nil。

解决方案

方案1:转成数组后用pagy分页(快速临时解决)

把Post.all转成数组,避免pagy重新查询数据库,直接用你已经处理好的内存对象:

修改控制器代码:

def index
  @posts = Post.all.to_a # 转成数组,固定内存中的对象集合
  @posts.each do |post|
    post_info = get_post_info(post.url)
    post.icon = post_info["icon"] # 直接给attr_accessor属性赋值,无需update_attribute(因为attr_accessor没有对应数据库字段)
    post.subtitle = post_info["subtitle"]
  end
  print @posts[0].icon
  @pagy, @posts = pagy_array(@posts) # 使用pagy_array处理数组分页
end

注意:如果你的Post数量很大,转成数组会占用较多内存,不适合数据量多的场景。

方案2:模型内延迟加载属性(推荐长期方案)

把属性赋值逻辑移到模型里,让每个Post实例在需要的时候自动加载icon和subtitle,这样分页后的新实例也能正常获取值:

  1. 修改Post模型:
class Post < ApplicationRecord
  attr_accessor :icon, :subtitle

  # 延迟加载post信息,避免重复请求
  def load_post_info
    return if @post_info_loaded

    post_info = get_post_info(self.url) # 把get_post_info方法移到模型里,或者封装成Service类
    self.icon = post_info["icon"]
    self.subtitle = post_info["subtitle"]
    @post_info_loaded = true
  end

  # 如果get_post_info原本是控制器方法,需要迁移到模型或单独的工具类中
  private
  def get_post_info(url)
    # 这里写原本控制器里的get_post_info逻辑
  end
end
  1. 在视图部分,先调用加载方法再使用属性:
    修改_index.html.erb:
<div class="flex mb-4">
  <div class="rounded float-left mr-3 my-3">
    <% post.load_post_info %> <!-- 先加载属性 -->
    <% if !post.icon.nil? %>
      <%= image_tag post.icon, class: "app-icon float-right w-full h-full rounded-3xl", style: "width: 150px;" %>
    <% else %>
      <%= image_tag 'post.svg', class: "app-icon w-full h-full", style: "width: 150px;" %>
    <% end %>
  </div>
  <div style="width: 85%" class="mt-5 pt-3 lg:ml-4">
    <h3 class="lg:text-xl text-sm font-semibold text-gray-50"><%= post.title %></h3>
    <div class="lg:text-base text-xs text-gray-50"> <%= post.subtitle %> </div>
  </div>
</div>

这种方案不会一次性加载所有数据,性能更优,也符合Rails的MVC设计原则。

额外说明

如果你原本的icon和subtitle是数据库字段(不是attr_accessor),那问题可能是update_attribute的修改还没提交到数据库就被pagy查询了。这种情况下可以在批量更新后调用@posts.reload,但更推荐用批量更新语句直接操作数据库,避免内存加载所有对象:

def index
  Post.find_each do |post|
    post_info = get_post_info(post.url)
    post.update(icon: post_info["icon"], subtitle: post_info["subtitle"])
  end
  @pagy, @posts = pagy(Post.all)
end

不过这种方式会产生大量数据库更新请求,数据量大时建议用批量更新优化。

内容的提问来源于stack exchange,提问作者Beu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 16:55:23