Rails多态关联在同一个模型上有多个关联

我的问题基本上和这个一样: 在同一个模型上有多个关联的多态关联

然而,提议/接受的解决scheme不起作用,如后面的评论者所示。

我有一个在我的应用程序中使用的照片类。 一篇文章可以有一张照片。 但是,我想重新使用多态关系添加辅助照片。

之前:

class Photo belongs_to :attachable, :polymorphic => true end class Post has_one :photo, :as => :attachable, :dependent => :destroy end 

期望:

 class Photo belongs_to :attachable, :polymorphic => true end class Post has_one :photo, :as => :attachable, :dependent => :destroy has_one :secondary_photo, :as => :attachable, :dependent => :destroy end 

然而,这个失败,因为它找不到“SecondaryPhoto”类。 根据我从其他线索可以看出,我想要做的:

  has_one :secondary_photo, :as => :attachable, :class_name => "Photo", :dependent => :destroy 

除了调用Post#secondary_photo只是返回通过照片关联相同的照片,例如Post#photo === Post#secondary_photo。 看着SQL,它确实在WHERE type =“Photo”而不是像“SecondaryPhoto”那样,我想…

思考? 谢谢!

我已经在我的项目中做了这个。

诀窍是照片需要在has_one条件中使用的列来区分主照片和次照片。 注意在这里发生的:conditions

 has_one :photo, :as => 'attachable', :conditions => {:photo_type => 'primary_photo'}, :dependent => :destroy has_one :secondary_photo, :class_name => 'Photo', :as => 'attachable', :conditions => {:photo_type => 'secondary_photo'}, :dependent => :destroy 

这种方法的@post.build_photo是,当您使用@post.build_photo创build照片时,photo_type将自动预先填充相应的types,如“primary_photo”。 ActiveRecord足够聪明,可以做到这一点。

Rails 4.2+

 class Photo belongs_to :attachable, :polymorphic => true end class Post has_one :photo, :as => :attachable, :dependent => :destroy has_one :secondary_photo, -> { where attachable_type: "SecondaryPhoto"}, class_name: Photo, foreign_key: :attachable_id, foreign_type: :attachable_type, dependent: :destroy end 

你需要提供foreign_key根据…. ableness或Rails会要求post_id列的照片表。 Attachable_type列将作为SecondaryPhoto填充Rails魔术

未来的参考检查这个职位的人

这可以使用下面的代码来实现…

Rails 3:

 has_one :banner_image, conditions: { attachable_type: 'ThemeBannerAttachment' }, class_name: 'Attachment', foreign_key: 'attachable_id', dependent: :destroy 

Rails 4:

 has_one :banner_image, -> { where attachable_type: 'ThemeBannerAttachment'}, class_name: 'Attachment', dependent: :destroy 

不知道为什么,但在Rails 3中,您需要提供一个foreign_key值以及条件和class_name。 不要使用'as::attachable',因为这将在设置多态types时自动使用调用类名称。

以上也适用于has_many。

像下面的东西工作查询,但分配从用户到地址不起作用

用户类

 has_many :addresses, as: :address_holder has_many :delivery_addresses, -> { where :address_holder_type => "UserDelivery" }, class_name: "Address", foreign_key: "address_holder_id" 

地址类

 belongs_to :address_holder, polymorphic: true 

我没有使用它,但我search了一下,看着Rails的来源,我认为你要找的是:foreign_type 。 试试看,并告诉它是否工作:)

 has_one :secondary_photo, :as => :attachable, :class_name => "Photo", :dependent => :destroy, :foreign_type => 'SecondaryPost' 

我认为你的问题的types应该是Post而不是Photo ,分别使用SecondaryPost来分配Post模型会更好。

编辑:

以上回答完全错误。 :foreign_type belongs_to关联中的多态模型中可以使用:foreign_type来指定包含关联模型types的列的名称。

当我看着Rails的源代码,这一行设置这种types的关联:

 dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as] 

正如你所看到的,它使用base_class.name来获取types名称。 据我所知,你无能为力。

所以我的诠释是添加一列到照片模型,例如: photo_type 。 如果是第一张照片,则将其设置为0;如果是第二张照片,则将其设置为1。 在你的关联中分别添加:conditions => {:photo_type => 0}:conditions => {:photo_type => 1} 。 我知道这不是你正在寻找的解决scheme,但我找不到更好的东西。 顺便说一下,也许只是使用has_many关联会更好?

你将不得不将猴子的shelltypes的概念修补为has_one关系。 这是我为has_many做的。 在初始化程序文件夹中的一个新的.rb文件中,我调用了我的add_foreign_type_support.rb它可以让你指定你的attachable_type是什么。 例如:has_many照片,:class_name =>“图片”,:as =>可连接,:foreign_type =>'Pic'

 module ActiveRecord module Associations class HasManyAssociation < AssociationCollection #:nodoc: protected def construct_sql case when @reflection.options[:finder_sql] @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) when @reflection.options[:as] resource_type = @reflection.options[:foreign_type].to_s.camelize || @owner.class.base_class.name.to_s @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " @finder_sql += "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(resource_type)}" else @finder_sql += ")" end @finder_sql << " AND (#{conditions})" if conditions else @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" @finder_sql << " AND (#{conditions})" if conditions end if @reflection.options[:counter_sql] @counter_sql = interpolate_sql(@reflection.options[:counter_sql]) elsif @reflection.options[:finder_sql] # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */ @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" } @counter_sql = interpolate_sql(@reflection.options[:counter_sql]) else @counter_sql = @finder_sql end end end end end # Add foreign_type to options list module ActiveRecord module Associations # :nodoc: module ClassMethods private mattr_accessor :valid_keys_for_has_many_association @@valid_keys_for_has_many_association = [ :class_name, :table_name, :foreign_key, :primary_key, :dependent, :select, :conditions, :include, :order, :group, :having, :limit, :offset, :as, :foreign_type, :through, :source, :source_type, :uniq, :finder_sql, :counter_sql, :before_add, :after_add, :before_remove, :after_remove, :extend, :readonly, :validate, :inverse_of ] end end 

以前的答案都没有帮助我解决这个问题,所以我会把这个放在这里,以防其他人遇到这个问题。 使用Rails 4.2 +。

创build迁移(假设您已经有了一个Addresses表):

 class AddPolymorphicColumnsToAddress < ActiveRecord::Migration def change add_column :addresses, :addressable_type, :string, index: true add_column :addresses, :addressable_id, :integer, index: true add_column :addresses, :addressable_scope, :string, index: true end end 

设置你的多态关联:

 class Address < ActiveRecord::Base belongs_to :addressable, polymorphic: true end 

设置将从以下位置调用关联的类:

 class Order < ActiveRecord::Base has_one :bill_address, -> { where(addressable_scope: :bill_address) }, as: :addressable, class_name: "Address", dependent: :destroy accepts_nested_attributes_for :bill_address, allow_destroy: true has_one :ship_address, -> { where(addressable_scope: :ship_address) }, as: :addressable, class_name: "Address", dependent: :destroy accepts_nested_attributes_for :ship_address, allow_destroy: true end 

诀窍是您必须调用Order实例上的构build方法,否则scope列将不会被填充。

所以这不起作用:

 address = {attr1: "value"... etc...} order = Order.new(bill_address: address) order.save! 

但是,这是可行的。

 address = {attr1: "value"... etc...} order = Order.new order.build_bill_address(address) order.save! 

希望能帮助别人。

你可以添加一个SecondaryPhoto模型:

 class SecondaryPhoto < Photo end 

然后从has_one:secondary_photo?中跳过:class_name。

对于mongoid使用这个解决scheme

发现这个问题后,经历了艰难的时刻,但得到了有效的解决scheme

添加到你的Gemfile

gem“mongoid-multiple-polymorphic”

这就像一个魅力:

  class Resource has_one :icon, as: :assetable, class_name: 'Asset', dependent: :destroy, autosave: true has_one :preview, as: :assetable, class_name: 'Asset', dependent: :destroy, autosave: true end 

这些解决scheme似乎都不能在Rails 5上工作。出于某种原因,看起来围绕关联条件的行为已经发生了变化。 在分配相关对象时,条件似乎不被用于插入; 只有在阅读关联时。

我的解决scheme是重写关联的setter方法:

 has_one :photo, -> { photo_type: 'primary_photo'}, as: 'attachable', dependent: :destroy def photo=(photo) photo.photo_type = 'primary_photo' super end