0

我正在开发一个带有几个扩展 ActiveRecord::Base 的模型类的 Rails 应用程序。我已经在其中一些模型类上实现了多种方法,这些方法可以用来更新数据库内容,并且这些方法都可以按照我希望的方式工作。例如,我有一个TeamStats将各种统计值存储到数据库的类,并且我定义了允许我通过应用游戏结果来更新这些属性的方法:

class TeamStats < ActiveRecord::Base

  belongs_to :team

  def << game_result
     # Lots of stuff here to update persisted attributes for 
     # wins, losses, total points scored, etc
  end

end

class Team < ActiveRecord::Base
    has_one :stats, :class_name => TeamStats, :dependent => destroy
end

现在我发现自己想要重用该逻辑,但使用一些我不想提交到数据库的临时数据。例如,我想使用游戏的子集而不是所有游戏重新计算团队的统计数据。所以我有这样的代码:

# relevant_game_results is an array containing game results I want considered
teams.each do |team|
    new_stats = TeamStats.new(:team => team)
    relevent_game_results.each do |results|
        new_stats << results
    end
end

# Do stuff to choose a team based on these un-persisted stats that have been 
# assigned to the team

# After I'm done, through all the team_stats I created and make sure all of the
# related team model objects still reference their original team stats values

我最初的计划是修改我的模型实例,然后reload在我完成后调用这些实例。例如:

teams.each do |team|
    team.reload
end

我认为这会起作用,但我必须为很多对象做这件事,如果可以的话,我宁愿在一次操作中做到这一点。

似乎我真正需要的是一个总是回滚而不是提交的事务。执行此操作的最合适的“rails 方式”是什么?我应该在一个transaction区块内执行此操作,然后ActiveRecord::Rollback在我的区块末尾提出一个吗?换句话说,像这样的东西?

Team.transaction do

  teams.each do |team|
      new_stats = TeamStats.new(:team => team)
      relevent_game_results.each do |results|
          new_stats << results
      end
  end

  # Do stuff to choose a team based on these un-persisted stats that have been 
  # assigned to the team

  raise ActiveRecord::Rollback
end

这对我来说似乎有点“肮脏”,但这只是我的 Java 背景;)是否有更符合 Rails 方式的更清洁的方法?

更新:事实证明,将其包装在事务中并回滚不仅丑陋,而且似乎很难使其正常工作。因为执行此代码的方法本身可以在另一个事务中,并且由于 ActiveRecord 关系对象的某些更改的某些趋势是自动保存的,所以我不得不跳过许多环节才能使其正常工作。

根据接受的答案中的建议,我已经采用了完全创建新 TeamStats 对象的方法,只是从不保存它。似乎对我来说工作得更好。

4

2 回答 2

1

您应该查看 ActiveAttr ( https://github.com/cgriego/active_attr ) 或 Virtus https://github.com/solnic/virtus之类的内容。您将创建完全在内存中的对象。这也有一个很好的 railscast:http ://railscasts.com/episodes/326-activeattr

于 2013-03-01T02:52:00.790 回答
1

与其回滚,不如先复制一份,然后再不保存?例如:

# in TeamStats

def hypothetically_add(games)
  copy = dup
  games.each {|game| copy << game }
  copy
end

如果您基于查询所有游戏的匹配来生成统计信息,那么一次将游戏添加到 Stats 对象可能不是正确的方法。对游戏集合进行操作可能会更好。

# in Team

def stats_from_games_where(*conditions)
  games.where(*conditions).reduce( Hash.new(0) ) do |stats, game|
    stats[:wins] += 1 if # we won
    ... 
  end
end
于 2013-03-01T06:57:07.230 回答