为了讨论的目的,我用两张桌子做了一个测试:
: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 :(得分: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
...假设count
上的StonePlacement
字段增加和减少。
答案 1 :(得分:0)
怎么样
bowl.stones.slice!(0)
答案 2 :(得分: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。)