【问题标题】:Rails associations - problems with altering values, and too much caching!Rails 关联 - 更改值和过多缓存的问题!
【发布时间】:2010-10-13 16:19:38
【问题描述】:

假设我有一个纸牌游戏应用程序,它有一个 Player 模型,它有一个 actions 整数列;和Card 模型。玩家可以打出他们拥有的牌,这需要花费一个动作;一张特定的牌在播放时会授予两个动作。

如果我这样编码:

class Player < ActiveRecord::Base
  has_many :cards

  def play_card(card)
    raise "Not yours!" unless cards.include? card
    self.actions -= 1
    card.play
    save!
  end
end

class Card < ActiveRecord::Base
  belongs_to :player

  def play
    player.actions += 2
  end
end

...然后Player#play_card 的净效果是将actions 减1。我发现使这两项更改应用于同一个对象的唯一方法,从而导致净增加1 个动作,就是这样定义函数:

class Player < ActiveRecord::Base
  has_many :cards

  def play_card(card)
    raise "Not yours!" unless cards.include? card
    self.actions -= 1

    // Stick that change in the Database
    save!

    card.play
  end
end

class Card < ActiveRecord::Base
  belongs_to :player

  def play
    // Force reload of the player object
    player(true).actions += 2

    // And save again
    player.save!
  end
end

但这会将单个数据库写入变成两次写入和一次读取!肯定有更好的方法。我错过了什么?

【问题讨论】:

    标签: ruby-on-rails caching associations


    【解决方案1】:

    在您的代码的第一个版本中,您正在加载桌面播放器的同一行,但是虽然您希望 rails 足够聪明,可以识别它已经将这一行加载到内存中,但 rails 不能以这种方式工作。因此,当您在播放器上发出 +=2 时,它会在另一个实例上 +=2,而不是您已在 -=1 上执行过的实例。

    我已经设置了一个小例子来表明同一行的实例太多:

    ruby-1.8.7-p174 > p_instance_1 = Player.first
     => #<Player id: 1, actions: -1, created_at: "2010-10-13 17:07:22", updated_at: "2010-10-13 17:11:00"> 
    ruby-1.8.7-p174 > c = Card.first
     => #<Card id: 1, player_id: 1, created_at: "2010-10-13 17:07:28", updated_at: "2010-10-13 17:07:28"> 
    ruby-1.8.7-p174 > p_instance_2 = c.player
     => #<Player id: 1, actions: -1, created_at: "2010-10-13 17:07:22", updated_at: "2010-10-13 17:11:00"> 
    ruby-1.8.7-p174 > p_instance_1.object_id
     => 2158703080 
    ruby-1.8.7-p174 > p_instance_2.object_id
     => 2156926840 
    ruby-1.8.7-p174 > p_instance_1.actions += 1
     => 0 
    ruby-1.8.7-p174 > p_instance_2.actions += 1
     => 0
    

    最后,由于您还没有保存应用 +=2 的实例,因此只有保存了 -1 的实例

    更新

    您可以尝试欺骗 rails 以一直使用相同的播放器实例。这有点难看,但它有效。

    class Player < ActiveRecord::Base
      has_many :cards
    
      def play_card(card)
        raise "Not yours!" unless cards.include? card
        new_self = card.player
        card.play
        new_self.actions -= 1
        new_self.save!
      end
    end
    
    class Card < ActiveRecord::Base
      belongs_to :player
    
      def play
        player.actions += 2
      end
    end
    

    所以当你输入这些命令时:

    ruby-1.8.7-p174 > p = Player.first
     => #<Player id: 1, actions: 0, created_at: "2010-10-14 13:33:51", updated_at: "2010-10-14 13:33:51"> 
    ruby-1.8.7-p174 > p.play_card(Card.first)
     => true 
    ruby-1.8.7-p174 > p
     => #<Player id: 1, actions: 0, created_at: "2010-10-14 13:33:51", updated_at: "2010-10-14 13:33:51"> 
    ruby-1.8.7-p174 > p.reload
     => #<Player id: 1, actions: 1, created_at: "2010-10-14 13:33:51", updated_at: "2010-10-14 13:34:40"> 
    

    您在播放器中有正确数量的操作,并且在日志中卡片仅加载一次:

      Player Load (0.5ms)   SELECT * FROM "players" LIMIT 1
      Card Load (0.2ms)   SELECT * FROM "cards" LIMIT 1
      Card Load (0.2ms)   SELECT "cards".id FROM "cards" WHERE ("cards"."id" = 1) AND ("cards".player_id = 1) LIMIT 1
      Player Load (0.1ms)   SELECT * FROM "players" WHERE ("players"."id" = 1) 
      Player Update (0.6ms)   UPDATE "players" SET "updated_at" = '2010-10-14 13:34:40', "actions" = 1 WHERE "id" = 1
    

    总结一下,我想说你的代码设计有问题。如果我理解得很好,您想要的是表行的每个 AR 实例都是 ObjectSpace 中的同一个对象,但我猜如果以这种方式构建 rails 会引入奇怪的行为,您可以在半支持对象更改时工作在验证和其他钩子中。

    【讨论】:

    • 好的,我知道这两个实例对于 Ruby 来说是不同的“对象”。是否有解决方法来防止执行三个 DB 操作(除了将 Player 实例传递给 Card#play,因为这会破坏关联的意义)?
    • 感谢您的解决方法。这有点难看,但很高兴知道我有这个选择。我对代码设计持观点,但我很好奇您是否可以提出更好的选择?我想(我正在使用 STI)我可以将动作减量放入卡片的超类中,并将增量放入派生类中......
    • 'wrong' 可能有点太强了。只是觉得卡牌模型里面更新玩家模型的action字段有点奇怪。
    • 接受您的回答,因为它是一种有效的解决方法。为了后代,我通过意识到actions 实际上只是另一个表中的行数来解决这个问题;所以我将它作为一个属性删除并立即计算它。
    猜你喜欢
    • 2015-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多