Rails has_many through同模型多别名关联查询结果重复问题
问题根因
现有关联定义仅通过source_type = 'Contact'过滤了多态关联的对象类型,未对中间表ArchiveJoin存储角色标识的字段做条件过滤,因此三个关联查询时都会拉取当前Archive下所有关联类型为Contact的记录,自然返回结果完全一致。
该问题不需要编写自定义查询方法,通过Rails原生的关联作用域配置即可解决。
前置要求
你的ArchiveJoin中间表必须包含用于区分角色的字段,推荐字段结构如下:
archive_id:bigint:关联Archive模型的外键archiveable_type:string:多态关联的对象类型,存关联模型名(如'Contact')archiveable_id:bigint:多态关联的对象IDrole:string:存储关联角色值,如'author'/'co_author'/'publisher'
正确关联配置
首先确保三个模型的基础关联定义正确:
# app/models/archive_join.rb 中间表模型 class ArchiveJoin < ApplicationRecord belongs_to :archive belongs_to :archiveable, polymorphic: true end
# app/models/contact.rb 联系人模型 class Contact < ApplicationRecord has_many :archive_joins, as: :archiveable, dependent: :destroy has_many :archives, through: :archive_joins end
修改Archive模型的关联定义,给每个角色关联增加对应role字段的过滤作用域:
# app/models/archive.rb 档案模型 class Archive < ApplicationRecord has_many :archive_joins, dependent: :destroy # 作者关联 has_many :authors, -> { where(archive_joins: { role: 'author' }) }, through: :archive_joins, source: :archiveable, source_type: 'Contact' # 共同作者关联 has_many :co_authors, -> { where(archive_joins: { role: 'co_author' }) }, through: :archive_joins, source: :archiveable, source_type: 'Contact' # 出版方关联 has_many :publishers, -> { where(archive_joins: { role: 'publisher' }) }, through: :archive_joins, source: :archiveable, source_type: 'Contact' end
使用说明
- 如果单个Archive在每个角色下仅对应1个Contact,将上述配置中的
has_many替换为has_one即可,过滤逻辑完全通用。 - 写入关联时如果需要自动填充role字段,可以给关联增加扩展回调,避免手动操作中间表:
# 以authors关联为例,其余两个关联配置逻辑一致 has_many :authors, -> { where(archive_joins: { role: 'author' }) }, through: :archive_joins, source: :archiveable, source_type: 'Contact' do def <<(contact) proxy_association.owner.archive_joins.find_or_create_by(archiveable: contact, role: 'author') end end
配置后直接调用archive.authors << Contact.first即可自动创建带正确role值的中间表记录,无需手动指定角色。
配置完成后再调用Archive.first.authors、Archive.first.co_authors、Archive.first.publishers就会返回对应角色下的联系人记录,不会再出现结果一致的问题。
内容的提问来源于stack exchange,提问作者ls168
相关产品推荐
相关产品推荐

