我有徽章(類似StackOverflow)。ActiveRecord/Rails中的多列外鍵/關聯
其中一些可以附加到可加標籤的東西(例如,在帖子上添加了> X的評論徽章附加到帖子)。幾乎所有人都進入多個級別(例如> 20,> 100,> 200),並且每個徽章x徽章類型只能有一個級別(= 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}'
注意,條件是單引號,而不是增加一倍,這使得它在運行時進行解釋,而比裝載時間。
但是,當試圖通過:through關聯使用此功能時,出現錯誤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
雖然這個工作 - 這可能是一個更好的解決方案 - 我不認爲這是一個實際的回答如何做)多鍵的外鍵的問題,或b)通過關聯工作的動態條件關聯。所以如果有人有解決方案,請說出來。
這或多或少有用。這不是問題的答案,儘管它是問題的答案,我也是這樣認爲的。 我已經清理了你的代碼並把它放在了問題中。 – Sai 2009-10-28 00:10:46
我知道。你所問的似乎超出了Rails輕鬆完成的事情。你有沒有找過插件?乍看之下,http://compositekeys.rubyforge.org/似乎可以做你想要的。 – EmFi 2009-10-28 00:51:02