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,这样分页后的新实例也能正常获取值:
- 修改
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
- 在视图部分,先调用加载方法再使用属性:
修改_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
相关产品推荐
相关产品推荐

