在`before_save`期间更新其他记录的值时,Rails无限循环

时间:2015-12-14 10:23:59

标签: ruby-on-rails

我在Rails中有这个模型(修剪到相关部分)

class Session < ActiveRecord::Base
  belongs_to :user
  before_save :invalidate_existing_sessions


  def invalidate_existing_sessions
    Session.where(user_id: user.id, current: true).each { |sess| sess.update_attributes(current: false) }
  end
end

但是,当创建并即将保存记录时,服务器将进入无限循环。

以下是服务器日志

Processing by V1::SessionsController#create as */*
  Parameters: {"email"=>"user@example.com", "password"=>"[FILTERED]", "session"=>{}}
  User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = $1 LIMIT 1  [["email", "user@example.com"]]
   (0.2ms)  BEGIN
  Session Load (0.7ms)  SELECT "sessions".* FROM "sessions" WHERE "sessions"."user_id" = $1 AND "sessions"."current" = $2  [["user_id", 1
], ["current", true]]
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
  CACHE (0.0ms)  SELECT "sessions".* FROM "sessions" WHERE "sessions"."user_id" = $1 AND "sessions"."current" = $2  [["user_id", 1], ["cu
rrent", true]]
  CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
  CACHE (0.0ms)  SELECT "sessions".* FROM "sessions" WHERE "sessions"."user_id" = $1 AND "sessions"."current" = $2  [["user_id", 1], ["cu
rrent", true]]
  CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
  CACHE (0.0ms)  SELECT "sessions".* FROM "sessions" WHERE "sessions"."user_id" = $1 AND "sessions"."current" = $2  [["user_id", 1], ["cu
rrent", true]]

稍后,这就是日志变成

的内容
  app/models/session.rb:12:in `invalidate_existing_sessions'
  app/models/session.rb:12:in `block in invalidate_existing_sessions'
  app/models/session.rb:12:in `invalidate_existing_sessions'
  app/models/session.rb:12:in `block in invalidate_existing_sessions'
  app/models/session.rb:12:in `invalidate_existing_sessions'
  app/models/session.rb:12:in `block in invalidate_existing_sessions'
  app/models/session.rb:12:in `invalidate_existing_sessions'

有什么想法吗?我正在使用Rails 5 alpha。

3 个答案:

答案 0 :(得分:1)

这是因为您的before_save方法可以做到这一点......

sess.update_attributes(current: false)

由于update_attributes在无限循环中调用before_save(正如你所说)。

所以你需要跳过回调

class Session < ActiveRecord::Base
  attr_accessor :skip_callbacks
  before_save :invalidate_existing_sessions, unless: :skip_callbacks

  def invalidate_existing_sessions
    Session.where(user_id: user.id, current: true).each do |sess|  
      sess.skip_callbacks = true
      sess.update_attributes(current: false) 
    end
  end

答案 1 :(得分:1)

您在// private Files file = null;// create a tempfile private JsonNodeFactory factory; private JsonFactory jsonFactory; private ObjectMapper mapper; private JsonNode jsonRoot; private Queue<TrieNode> queue; // private JsonParser jsonParser = public JsonHelperClass() throws JsonProcessingException, IOException { this.factory = JsonNodeFactory.instance; this.jsonFactory = new JsonFactory(); this.mapper = new ObjectMapper(); this.jsonRoot = mapper.readTree(new File("json with data")); } public static void main(String[] args) throws Exception, Exception { JsonHelperClass helperClass = new JsonHelperClass(); helperClass.jsonCreator(); ObjectNode objectNode = null; ObjectNode result = helperClass.createJsonRecursively(objectNode); System.out.println(result.toString()); } public void jsonCreator() throws Exception { Trie trie = TrieBuilder.createSpec(); queue = trie.bfsTraversal(trie.root); } public ObjectNode createJsonRecursively(ObjectNode outputJson) throws Exception { TrieNode nodeOfQueue = queue.poll(); if(outputJson == null){ // create a root of the JSON outputJson = factory.objectNode(); outputJson.put(nodeOfQueue.target, createJsonRecursively(outputJson)); }else if (jsonRoot.get(nodeOfQueue.source).isObject()){ // create an object to conatin other values/object ObjectNode objectNode = factory.objectNode(); objectNode.put(nodeOfQueue.target,createJsonRecursively(outputJson)); outputJson.putAll(objectNode); }else if(jsonRoot.get(nodeOfQueue.source).isArray()){ // create an array node and call for to create value it contains ArrayNode arrayNode = factory.arrayNode(); int size = jsonRoot.get(nodeOfQueue.source).size(); for(int index = 0 ; index < size ; index++){ arrayNode.add(jsonRoot.get(nodeOfQueue.source).get(index)); } outputJson.put(nodeOfQueue.target,arrayNode); }else if(nodeOfQueue.isEnd){ // create leaf node outputJson.put(nodeOfQueue.target, jsonRoot.get(nodeOfQueue.source)); return outputJson; } return outputJson; } 中正在运行<data android:scheme="http" /> <data android:host="www.googal.com" /> <data android:pathPrefix="/"/> ,这意味着您在保存前正在保存。这就是它进入无限循环的原因。

答案 2 :(得分:1)

尽管以上所有答案对我有用,但这是我发现最简单的,我最终使用了。

def invalidate_existing_sessions
  Session.where(user_id: user.id, current: true).each { |sess| sess.update_column(:current, false) }
end

结果显示update_column不会调用任何回调,但如果您在模型中使用时间戳,则不会更新updated_at