Rails如何定义关联实现连接表按两端关联过滤且适配GraphQL AssociationLoader
实现方案
结论是可以实现,我们可以通过给关联添加扩展注入上下文,配合Product侧的无参作用域关联完成需求,完全适配无参的AssociationLoader要求。
第一步:修正原模型笔误
原代码中Product类的类名错误使用了复数形式,先修正为单数:
# 修正前错误写法 # class Products < ApplicationRecord # 修正后正确写法 class Product < ApplicationRecord has_many :items end
第二步:修改关联定义
1. Package类关联添加扩展
给products关联添加扩展,为每一个从该关联加载的Product实例注入当前所属的Package上下文:
class Package < ApplicationRecord has_many :items has_many :products, through: :items do def each super do |product| # 给当前关联返回的Product实例绑定所属Package product.instance_variable_set(:@bound_package, proxy_association.owner) yield product end end end end
2. Product类添加无参过滤关联
定义无参的作用域关联,自动读取绑定的Package上下文过滤Item:
class Product < ApplicationRecord has_many :items # 无参关联,完全适配AssociationLoader要求 has_many :package_scoped_items, -> { bound_package = instance_variable_get(:@bound_package) bound_package ? where(package_id: bound_package.id) : none }, class_name: 'Item' # 如果希望和示例场景完全对齐,直接调用items就返回过滤结果,可以覆盖原有关联,不会影响直接查询Product时的使用 # has_many :items, -> { # bound_package = instance_variable_get(:@bound_package) # bound_package ? where(package_id: bound_package.id) : all # } end
效果验证
按照你给出的示例场景调用,即可得到期望的结果:
last_package = Package.eager_load(:products).last last_product = last_package.products.last last_product.package_scoped_items # 此处返回的就是同时属于last_package和last_product的Item记录,不会返回该Product下的所有Item
注意事项
- 只有从Package的
products关联中预加载/加载得到的Product实例,才会携带绑定的Package上下文,直接查询Product.all得到的实例调用该关联会返回空(兜底逻辑可根据业务需求调整为返回全部Item) - 方案不会产生N+1查询问题,完全符合AssociationLoader对无参关联的要求
内容的提问来源于stack exchange,提问作者taras
相关产品推荐
相关产品推荐

