Rails多态关联中如何校验belongs_to关联对象的类型
问题原因
你当前的写法只给membership和ticket关联指定了外键object_id,没有添加关联类型校验逻辑。Rails默认处理belongs_to关联时,只会按照外键值去对应模型的表中查找记录,不会校验当前Product实例的object_type是否匹配,因此只要object_id对应的ticket记录存在,调用product.ticket就会正常返回结果,不会触发报错。
解决方案
可以通过重写关联的getter方法,在调用时校验object_type是否匹配,不匹配直接抛出异常,同时可以给关联添加查询scope,避免关联查询时拉到不匹配的记录。
方案1:手动重写每个关联的访问方法
class Product < ApplicationRecord belongs_to :object, polymorphic: true # 关联添加scope,保证关联查询时只会匹配对应类型的记录 belongs_to :membership, -> { where(products: { object_type: 'Membership' }) }, foreign_key: :object_id belongs_to :ticket, -> { where(products: { object_type: 'Ticket' }) }, foreign_key: :object_id def membership raise ActiveRecord::AssociationTypeMismatch, "当前Product关联类型为#{object_type},无法访问Membership关联" unless object_type == 'Membership' super end def ticket raise ActiveRecord::AssociationTypeMismatch, "当前Product关联类型为#{object_type},无法访问Ticket关联" unless object_type == 'Ticket' super end end
方案2:元编程批量生成校验逻辑(适合多态关联类型较多的场景)
class Product < ApplicationRecord belongs_to :object, polymorphic: true # 列出所有多态关联的目标类型 POLYMORPHIC_ASSOCIATIONS = %w[Membership Ticket] POLYMORPHIC_ASSOCIATIONS.each do |klass_name| assoc_name = klass_name.underscore belongs_to assoc_name.to_sym, -> { where(products: { object_type: klass_name }) }, foreign_key: :object_id define_method(assoc_name) do raise ActiveRecord::AssociationTypeMismatch, "当前Product关联类型为#{object_type},无法访问#{klass_name}关联" unless object_type == klass_name super() end end end
配置完成后,如果调用和当前object_type不匹配的关联,就会直接抛出ActiveRecord::AssociationTypeMismatch异常。
内容的提问来源于stack exchange,提问作者HJW
相关产品推荐
相关产品推荐

