عمود متعددة الأجنبية مفاتيح / الجمعيات في أكتيفيريكورد / القضبان
-
06-07-2019 - |
سؤال
ولدي شارات (شيء على مثل ستاكوفيرفلوو).
والبعض منهم يمكن تركيبها على badgeable الأشياء (مثل شارة ل> X تعليقات على وظيفة تعلق على آخر). تأتي كلها تقريبا في مستويات متعددة (على سبيل المثال> 20> 100،> 200)، ويمكن أن يكون فقط مستوى واحد في badgeable نوع س شارة (= badgeset_id
).
لجعل الأمر أكثر سهولة لفرض قيود على مستوى واحد لكل شارة، أريد badgings لتحديد شارة من خلال مفتاح خارجي عمودين - badgeset_id
وlevel
- بدلا من المفتاح الأساسي (badge_id
)، على الرغم من شارات يفعل لديك مفتاح أساسي قياسي جدا.
في كود:
class Badge < ActiveRecord::Base
has_many :badgings, :dependent => :destroy
# integer: badgeset_id, level
validates_uniqueness_of :badgeset_id, :scope => :level
end
class Badging < ActiveRecord::Base
belongs_to :user
# integer: badgset_id, level instead of badge_id
#belongs_to :badge # <-- how to specify?
belongs_to :badgeable, :polymorphic => true
validates_uniqueness_of :badgeset_id, :scope => [:user_id, :badgeable_id]
validates_presence_of :badgeset_id, :level, :user_id
# instead of this:
def badge
Badge.first(:conditions => {:badgeset_id => self.badgeset_id, :level => self.level})
end
end
class User < ActiveRecord::Base
has_many :badgings, :dependent => :destroy do
def grant badgeset, level, badgeable = nil
b = Badging.first(:conditions => {:user_id => proxy_owner.id, :badgeset_id => badgeset,
:badgeable_id => badgeable.try(:id), :badgeable_type => badgeable.try(:class)}) ||
Badging.new(:user => proxy_owner, :badgeset_id => badgeset, :badgeable => badgeable)
b.level = level
b.save
end
end
has_many :badges, :through => :badgings
# ....
end
وكيف يمكنني تحديد جمعية belongs_to
أن يفعل ذلك (وليس محاولة لاستخدام badge_id
)، حتى أستطيع أن استخدام has_many :through
؟
وETA: هذا يعمل جزئيا (أي الأعمال @ badging.badge)، ولكن يشعر القذرة:
belongs_to :badge, :foreign_key => :badgeset_id, :primary_key => :badgeset_id, :conditions => 'badges.level = #{level}'
لاحظ أن الظروف هي في واحدة م> يقتبس، وليس مضاعفة، مما يجعل من تفسيرها في وقت التشغيل بدلا من loadtime.
ولكن، عند محاولة استخدام هذا مع: من خلال الجمعيات، وأحصل على undefined local variable or method 'level' for #<User:0x3ab35a8>
الخطأ. وليس واضحا (على سبيل المثال 'badges.level = #{badgings.level}'
) ويبدو ان العمل ...
وETA 2: أخذ كود EMFI وتنظيفه ويعمل قليلا. فهو يتطلب مضيفا badge_set_id
لشارة، التي لا لزوم لها، ولكن يا جيدا.
كود:
class Badge < ActiveRecord::Base
has_many :badgings
belongs_to :badge_set
has_friendly_id :name
validates_uniqueness_of :badge_set_id, :scope => :level
default_scope :order => 'badge_set_id, level DESC'
named_scope :with_level, lambda {|level| { :conditions => {:level => level}, :limit => 1 } }
def self.by_ids badge_set_id, level
first :conditions => {:badge_set_id => badge_set_id, :level => level}
end
def next_level
Badge.first :conditions => {:badge_set_id => badge_set_id, :level => level + 1}
end
end
class Badging < ActiveRecord::Base
belongs_to :user
belongs_to :badge
belongs_to :badge_set
belongs_to :badgeable, :polymorphic => true
validates_uniqueness_of :badge_set_id, :scope => [:user_id, :badgeable_id]
validates_presence_of :badge_set_id, :badge_id, :user_id
named_scope :with_badge_set, lambda {|badge_set|
{:conditions => {:badge_set_id => badge_set} }
}
def level_up level = nil
self.badge = level ? badge_set.badges.with_level(level).first : badge.next_level
end
def level_up! level = nil
level_up level
save
end
end
class User < ActiveRecord::Base
has_many :badgings, :dependent => :destroy do
def grant! badgeset_id, level, badgeable = nil
b = self.with_badge_set(badgeset_id).first ||
Badging.new(
:badge_set_id => badgeset_id,
:badge => Badge.by_ids(badgeset_id, level),
:badgeable => badgeable,
:user => proxy_owner
)
b.level_up(level) unless b.new_record?
b.save
end
def ungrant! badgeset_id, badgeable = nil
Badging.destroy_all({:user_id => proxy_owner.id, :badge_set_id => badgeset_id,
:badgeable_id => badgeable.try(:id), :badgeable_type => badgeable.try(:class)})
end
end
has_many :badges, :through => :badgings
end
ولئن كان هذا يعمل - وانها ربما أفضل حل - أنا لا أعتبر هذا جوابا الفعلية لمسألة كيفية القيام أ) متعددة الرئيسية مفاتيح خارجية، أو ب) جمعيات ديناميكية شرط أن تعمل مع: من خلال ذات الصلة. حتى إذا كان أي شخص لديه حل لذلك، يرجى الكلام.
المحلول
ويبدو أنه قد تجريب الأفضل إذا كنت فصل شارة إلى نموذجين. وهنا كيف كنت كسرها نزولا لتحقيق الوظيفة التي تريدها. رميت في بعض نطاقات اسمه للحفاظ على التعليمات البرمجية التي يفعل أشياء في الواقع نظيفة.
class BadgeSet
has_many :badges
end
class Badge
belongs_to :badge_set
validates_uniqueness_of :badge_set_id, :scope => :level
named_scope :with_level, labmda {|level
{ :conditions => {:level => level} }
}
named_scope :next_levels, labmda {|level
{ :conditions => ["level > ?", level], :order => :level }
}
def next_level
Badge.next_levels(level).first
end
end
class Badging < ActiveRecord::Base
belongs_to :user
belongs_to :badge
belongs_to :badge_set
belongs_to :badgeable, :polymorphic => true
validates_uniqueness_of :badge_set_id, :scope => [:user_id, :badgeable_id]
validates_presence_of :badge_set_id, :badge_id, :user_id
named_scope :with_badge_set, lambda {|badge_set|
{:conditions => {:badge_set_id => badge_set} }
}
def level_up(level = nil)
self.badge = level ? badge_set.badges.with_level(level).first
: badge.next_level
save
end
end
class User < ActiveRecord::Base
has_many :badgings, :dependent => :destroy do
def grant badgeset, level, badgeable = nil
b = badgings.with_badgeset(badgeset).first() ||
badgings.build(
:badge_set => :badgeset,
:badge => badgeset.badges.level(level),
:badgeable => badgeable
)
b.level_up(level) unless b.new_record?
b.save
end
end
has_many :badges, :through => :badgings
# ....
end