2009-07-08 63 views
1

爲了討論的目的我做了一個測試有兩個表:刪除habtm關係中的許多重複條目之一?

:stones and :bowls (both created with just timestamps - trivial) 

create_table :bowls_stones, :id => false do |t| 
    t.integer :bowl_id, :null => false 
    t.integer :stone_id, :null => false 
end 

該機型是不言自明,而基本的,但在這裏,他們是:現在

class Stone < ActiveRecord::Base 

    has_and_belongs_to_many :bowls 

end 

class Bowl < ActiveRecord::Base 

    has_and_belongs_to_many :stones 

end 

中,問題是:我希望每個碗裏都有許多相同的石頭。我希望能夠只移除一個,而將其他相同的石頭留在後面。這看起來很基本,我真的希望我都能找到解決方案,而且當我這樣做的時候不會覺得自己太白癡。

這是一個測試運行:

@stone = Stone.new 
@stone.save 
@bowl = Bowl.new 
@bowl.save 

#test1 - .delete 
5.times do 
    @bowl.stones << @stone 
end 

@bowl.stones.count 
=> 5 
@bowl.stones.delete(@stone) 
@bowl.stones.count 
=> 0 
#removed them all! 

#test2 - .delete_at 
5.times do 
    @bowl.stones << @stone 
end 

@bowl.stones.count 
=> 5 
index = @bowl.stones.index(@stone) 
@bowl.stones.delete_at(index) 
@bowl.stones.count 
=> 5 
#not surprising, I guess... delete_at isn't part of habtm. Fails silently, though. 
@bowl.stones.clear 

#this is ridiculous, but... let's wipe it all out 
5.times do 
    @bowl.stones << @stone 
end 

@bowl.stones.count 
=> 5 
ids = @bowl.stone_ids 
index = ids.index(@stone.id) 
ids.delete_at(index) 
@bowl.stones.clear 
ids.each do |id| 
    @bowl.stones << Stone.find(id) 
end 
@bowl.stones.count 
=> 4 
#Is this really the only way? 

所以......被吹走了整個事情,從鍵重建它真正的唯一途徑?

回答

0

你真的應該在這裏使用has_many :through關係。否則,是的,要完成你的目標的唯一方法是創建一個方法來計算一個特定石頭的當前數量,將它們全部刪除,然後將石頭加回到N - 1

class Bowl << ActiveRecord::Base 
    has_and_belongs_to_many :stones 

    def remove_stone(stone, count = 1) 
    current_stones = self.stones.find(:all, :conditions => {:stone_id => stone.id}) 
    self.stones.delete(stone) 
    (current_stones.size - count).times { self.stones << stone } 
    end 
end 

記住LIMIT規則都不能在DELETE報表支撐實在是沒有辦法來完成你想在SQL但不爲表某種其他標識符。

(MySQL的實際上不支持DELETE ... LIMIT 1但AFAIK的ActiveRecord不會爲你做的,你需要執行原始的SQL。)

1

這種關係是否必須是habtm

你可能有這樣的事情......

class Stone < ActiveRecord::Base 
    has_many :stone_placements 
end 

class StonePlacement < ActiveRecord::Base 
    belongs_to :bowl 
    belongs_to :stone 
end 

class Bowl < ActiveRecord::Base 
    has_many :stone_placements 
    has_many :stones, :through => :stone_placements 

    def contents 
    self.stone_placements.collect{|p| [p.stone] * p.count }.flatten 
    end 

    def contents= contents 
    contents.sort!{|a, b| a.id <=> b.id} 
    contents.uniq.each{|stone| 
     count = (contents.rindex(stone) - contents.index(stone)) + 1 
     if self.stones.include?(stone) 
     placement = self.stone_placements.find(:first, :conditions => ["stone_id = ?", stone]) 
     if contents.include?(stone) 
      placement.count = count 
      placement.save! 
     else 
      placement.destroy! 
     end 
     else 
     self.stone_placements << StonePlacement.create(:stone => stone, :bowl => self, :count => count) 
     end 
    } 
    end 
end 

...假設你有一個StonePlacementcount遞增和遞減。

+0

哦,不......我想這並不一定如此。我當然認爲你的解決方法...最終,我寧願移動實際的物體而不是保持計數器。愛你的照片。 – 2009-07-08 01:10:01

+1

如果你想這樣做,你也可以使用`StonePlacement.find_by_bowl_and_stone(@bowl,@stone).first.destroy`或類似的東西。 – wombleton 2009-07-08 01:13:32

+1

當然可以......有一箇中間對象來操縱開放各種可能性。那麼,我的問題的答案是否定的? – 2009-07-08 01:48:24

0

如何

bowl.stones.slice!(0)