2009-10-27 21 views
7

Rozetlerim var (StackOverflow gibi sorta).Birden çok sütun yabancı anahtar/dernekler ActiveRecord/Rails

Bazıları (örneğin bir yayındaki> X yorumlar için bir rozet yayına eklendi) şeyleri badgeable eklenebilir. Hemen hemen hepsi birden fazla seviyeye (örneğin,> 20,> 100,> 200) gelir ve her bir rozetli x rozeti tipi için sadece bir seviyeye sahip olabilirsiniz (= badgeset_id). , Rozetler olsa oldukça birincil anahtar (badge_id) tarafından daha - badgeset_id ve level -

Ben badgings iki sütunlu yabancı anahtarın kendi rozetini belirtmek istiyorum daha kolay tek düzey başına rozeti kısıtı zorlamak için yapmak Standart bir birincil anahtarı da var. kodunda

: Ben has_many :through kullanabilmesi

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 

Ben, bunu yapar (ve bir badge_id kullanmayı deneyin değildir) bir belongs_to ilişki belirtebilirsiniz nasıl?

ETA: Bu kısmen (yani @ badging.badge çalışır) çalışır, ancak kirli hisseder: koşullar tek tırnak içinde, oldukça zamanında yorumlanır yapar katına olmadığını

belongs_to :badge, :foreign_key => :badgeset_id, :primary_key => :badgeset_id, :conditions => 'badges.level = #{level}' 

Not yükleme süresinden daha fazla. Bu kullanmaya çalışırken

Ancak: ilişkisinden dolayı hatasını undefined local variable or method 'level' for #<User:0x3ab35a8> olsun. Ve bariz bir şey (örneğin 'badges.level = #{badgings.level}') iş gibi görünüyor ...

ETA 2: EMFI kodunu alarak ve biraz işler o kadar temizlik. Roge'a badge_set_id eklemeyi gerektirir, bu gereksizdir, ama iyi.

kodu: Bu çalışır

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 

- ve muhtemelen daha iyi bir çözüm var - Bir) çoklu anahtar yabancı anahtarları nasıl yapılacağı sorusuna bu gerçek bir cevap düşünün veya yok b) dernekler aracılığıyla çalışan dinamik koşul dernekleri. Öyleyse bunun için bir çözümü varsa, lütfen konuşun.

cevap

1

Badge'i iki modele ayırırsanız en iyi şekilde egzersiz yapabilir. İstediğiniz işlevselliği elde etmek için nasıl bozacağım. Aslında bazı şeyleri temiz tutan kodu tutmak için bazı adlandırılmış kapsamlarda attım. az ya da çok, işler

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 
+0

. Sorunun bir cevabı olsa da, sorunun tam bir cevabı değil, ben de bunu kredilendiriyorum. Kodunuzu temizledim ve soruyu sordum. – Sai

+0

Biliyorum. Sorduğun şey, Rails ile kolayca yapılabilenin ötesine geçiyor gibiydi. Eklentileri aradınız mı? Bir bakışta, http://compositekeys.rubyforge.org/, aradığınız şeyi yapabileceği gibi görünüyor. – EmFi

İlgili konular