ارتباط متعدد الأشكال القضبان مع ارتباطات متعددة على نفس النموذج
-
21-09-2019 - |
سؤال
سؤالي هو نفس الشيء:ارتباط متعدد الأشكال مع ارتباطات متعددة على نفس النموذج
ومع ذلك ، فإن الحل المقترح/المقبول لا يعمل ، كما هو موضح من قبل المعلق لاحقًا.
لدي فصل صور يستخدم في جميع أنحاء تطبيقي. يمكن أن يكون المنشور صورة واحدة. ومع ذلك ، أريد إعادة استخدام العلاقة المتعددة الأشكال لإضافة صورة ثانوية.
قبل:
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: ثانوي_فوتو؟