عمود متعددة الأجنبية مفاتيح / الجمعيات في أكتيفيريكورد / القضبان

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

سؤال

ولدي شارات (شيء على مثل ستاكوفيرفلوو).

والبعض منهم يمكن تركيبها على 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top