61

私の質問は本質的にこれと同じです: 同じモデルに複数の関連付けを持つポリモーフィックな関連付け

ただし、後でコメンターが示すように、提案/承認されたソリューションは機能しません。

アプリ全体で使用される Photo クラスがあります。投稿には 1 枚の写真を含めることができます。ただし、ポリモーフィックな関係を再利用して、2 番目の写真を追加したいと考えています。

前:

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 を呼び出すことを除いて、単純に Photo 関連付けを介して添付された同じ写真を返します (例: Post#photo === Post#secondary_photo)。SQLを見ると、私が望むように、「SecondaryPhoto」の代わりにWHERE type = "Photo"を実行します...

考え?ありがとう!

4

12 に答える 12

76

私は自分のプロジェクトでそれを行いました。

秘訣は、プライマリ写真とセカンダリ写真を区別するために、写真には 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と、photo_type に「primary_photo」などの対応するタイプが自動的に事前入力されることです。ActiveRecord はそれを行うのに十分スマートです。

于 2010-06-20T05:05:37.657 に答える
6

次のようなクエリは機能しましたが、ユーザーからアドレスへの割り当ては機能しませんでした

ユーザークラス

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
于 2014-10-22T17:31:34.173 に答える
3

私はそれを使用しませんでしたが、ググってRailsのソースを調べたところ、あなたが探しているのは:foreign_type. 試してみて、うまくいくかどうか教えてください:)

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

あなたの質問のタイプは、モデルに割り当てられているのではPostなくPhoto、それぞれ、使用する方がよいと思います。SecondaryPostPost

編集:

上記の答えは完全に間違っています。:foreign_type関連付けられた多態性モデルで使用可能でbelongs_to、関連付けられたモデルのタイプを含む列の名前を指定します。

Rails のソースを見ると、次の行でこのタイプを関連付けに設定しています。

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

ご覧のとおりbase_class.name、型名を取得するために使用します。私の知る限り、あなたはそれで何もできません。

したがって、私の提案は、例のように Photo モデルに 1 つの列を追加することですphoto_type。そして、1枚目の写真なら0、2枚目の写真なら1に設定します。関連に:conditions => {:photo_type => 0}:conditions => {:photo_type => 1}をそれぞれ追加します。それがあなたが探している解決策ではないことはわかっていますが、それ以上のものは見つかりません。ところで、単にhas_many関連付けを使用した方が良いのでしょうか?

于 2010-03-22T20:47:02.377 に答える
2

あなたは、foreign_type の概念を has_one 関係にモンキー パッチする必要があります。これは、私が has_many に対して行ったことです。初期化フォルダーの新しい .rb ファイルで、私の add_foreign_type_support.rb という名前を付けました。これにより、attachable_type を指定できます。例: has_many photo, :class_name => "Picture", :as => attachable, :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
于 2010-06-10T06:39:15.973 に答える
1

モンゴイドの場合、このソリューションを使用します

この問題を発見した後、苦労しましたが、機能するクールなソリューションを手に入れました

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
于 2015-06-14T15:07:52.430 に答える
0

次のような SecondaryPhoto モデルを追加できますか:

class SecondaryPhoto < Photo
end

has_one :secondary_photo? から :class_name をスキップします。

于 2010-03-22T20:19:56.153 に答える