ارتباط متعدد الأشكال القضبان مع ارتباطات متعددة على نفس النموذج

StackOverflow https://stackoverflow.com/questions/2494452

سؤال

سؤالي هو نفس الشيء:ارتباط متعدد الأشكال مع ارتباطات متعددة على نفس النموذج

ومع ذلك ، فإن الحل المقترح/المقبول لا يعمل ، كما هو موضح من قبل المعلق لاحقًا.

لدي فصل صور يستخدم في جميع أنحاء تطبيقي. يمكن أن يكون المنشور صورة واحدة. ومع ذلك ، أريد إعادة استخدام العلاقة المتعددة الأشكال لإضافة صورة ثانوية.

قبل:

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

ومع ذلك ، فإن هذا يفشل لأنه لا يمكن العثور على الفئة "الثانوية". بناءً على ما يمكنني قوله من هذا الموضوع الآخر ، أريد أن أفعل:

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

باستثناء استدعاء Post#Secondary_photo ، يعيد ببساطة نفس الصورة المرفقة عبر جمعية الصور ، على سبيل المثال Post#Photo === Post#Secondary_Photo. بالنظر إلى SQL ، فإنه يفعل حيث النوع = "الصورة" بدلاً من ، على سبيل المثال ، "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, ، سيتم تلقائيا photo_type مسبقًا مع النوع المقابل ، مثل "primary_photo". ActivereCord ذكي بما يكفي للقيام بذلك.

نصائح أخرى

القضبان 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

تحتاج إلى توفير Office_key وفقًا .... Able'ness أو Rails سوف تطلب عمود post_id في جدول الصور. سوف يملأ عمود anganable_type بسحر القضبان SecondaryPhoto

المرجع المستقبلي للأشخاص الذين يفحصون هذا المنشور

يمكن تحقيق ذلك باستخدام الرمز التالي ...

القضبان 3:

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

القضبان 4:

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

لست متأكدًا من السبب ، ولكن في Rails 3 ، تحتاج إلى توفير قيمة أجنبية _key إلى جانب الشروط و class_name. لا تستخدم "AS:: قابلة للإنقاذ" لأن هذا سيستخدم تلقائيًا اسم فئة الاتصال عند إعداد النوع متعدد الأشكال.

ما ورد أعلاه ينطبق على 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

لم تساعدني أي من الإجابات السابقة في حل هذه المشكلة ، لذلك سأضع هذا هنا في حالة تشغيل أي شخص آخر. باستخدام القضبان 4.2 +.

قم بإنشاء الترحيل (على افتراض أن لديك جدول عناوين بالفعل):

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 مثيل أو 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!

على أمل أن يساعد شخص آخر.

لم أستخدمها ، لكنني غوغل حولها ونظرت إلى مصادر القضبان وأعتقد أن ما تبحث عنه :foreign_type. جربه وأخبر ما إذا كان يعمل :)

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

أعتقد أن هذا النوع في سؤالك يجب أن يكون Post بدلاً من Photo وعلى التوالي ، سيكون من الأفضل استخدام SecondaryPost كما تم تعيينه ل Post نموذج.

تعديل:

أعلاه الإجابة خاطئة تماما. :foreign_type متوفر في نموذج متعدد الأشكال في belongs_to جمعية لتحديد اسم العمود الذي يحتوي على نوع من النموذج المرتبط.

عندما أنظر في مصادر القضبان ، يحدد هذا الخط هذا النوع للارتباط:

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

كما ترونه يستخدم base_class.name للحصول على اسم النوع. بقدر ما أعرف أنه لا يمكنك فعل أي شيء به.

لذا فإن Sugestion هو إضافة عمود واحد إلى نموذج الصور ، على سبيل المثال: photo_type. وقم بتعيينه على 0 إذا كانت الصورة الأولى ، أو اضبطها على 1 إذا كانت الصورة الثانية. في الجمعيات تضيف :conditions => {:photo_type => 0} و :conditions => {:photo_type => 1}, ، على التوالى. أعلم أنه ليس حلاً تبحث عنه ، لكن لا يمكنني العثور على أي شيء أفضل. بالمناسبة ، ربما يكون من الأفضل استخدام فقط has_many جمعية؟

سيتعين عليك قرد تصحيح فكرة الأجنبية _type إلى علاقة HAS_ONE. هذا ما فعلته لـ Has_Many. في ملف .rb جديد في المجلد المهيمنات ، اتصلت بـ Mine add_foreign_type_support.rb يتيح لك تحديد ما يجب أن تكون عليه. مثال: has_many photo ،: class_name => "picture" ،: 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

ل mongoid استخدم هذا الحل

كانت أوقات عصيبة بعد اكتشاف هذه المشكلة ولكن حصلت على حل رائع يعمل

أضف إلى gemfile الخاص بك

جوهرة '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

لا يبدو أن أيًا من هذه الحلول يعمل على القضبان 5. لسبب ما ، يبدو أن السلوك المحيط بظروف الارتباط قد تغير. عند تعيين الكائن ذي الصلة ، لا يبدو أن الشروط تستخدم في الإدراج ؛ فقط عند قراءة الجمعية.

كان حلي هو تجاوز طريقة Setter للجمعية:

has_one :photo, -> { photo_type: 'primary_photo'},
        as: 'attachable',
        dependent: :destroy

def photo=(photo)
  photo.photo_type = 'primary_photo'
  super
end

هل يمكنك إضافة نموذج ثانوي مثل:

class SecondaryPhoto < Photo
end

ثم تخطي: class_name من HAS_ONE: ثانوي_فوتو؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top