1

为了讨论的目的,我用两张表做了一个测试:

: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?

所以……真的是把整个东西吹走并用钥匙重建它真的是唯一的方法吗?

4

3 回答 3

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

于 2009-07-08T00:59:01.200 回答
1

你真的应该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。)

于 2009-07-08T14:17:15.993 回答
0

怎么样

bowl.stones.slice!(0)
于 2009-07-08T07:30:19.150 回答