在Rails 3中使用around_destroy对数据库进行更新而不是删除

Sch*_*els 2 activerecord callback ruby-on-rails-3.1

我有一个包含几个表的数据库模型,如下所示:

date       value_1 value_2 value_3
----------------------------------
-infinity  5       6       7
12-01-2012 4       6       7
15-01-2012 4       7       8
16-01-2012 7       7       8
Run Code Online (Sandbox Code Playgroud)

在应用程序中,数据(大部分)用作单独的值; 列中的不同值很重要:

date       value_1 date       value_2 date       value_3
------------------ ------------------ ------------------
-infinity  5       -infinity  6       -infinity  7
12-01-2012 4       15-01-2012 7       15-01-2012 8
16-01-2012 7       
Run Code Online (Sandbox Code Playgroud)

这个设计带来了一些问题:我不能只是"插入",更新"和"删除" value_2psuedo表的某些值,因为这可能会影响其他列中的值:如果我删除value_215-01-2012,删除整个记录将修改both value_2value_3psuedo-table.

这个问题的明显解决方案(对我而言)是使用回调来使用回调增强使用正确行为创建,更新和销毁操作around_.我为这些回调创建了一个类:

class TemporalCallbacks
    def self.around_destroy(record)
        # modify the record: replace it with the values from the previous entry
        ...
        # do an update instead of the destroy
        record.save
        record.logger.debug "end of destroy callback"
    end

    def self.around_update(record)
       ...
    end
end

class SomeModel < ActiveRecord::Base

    ...

    around_update TemporalCallbacks
    around_destroy TemporalCallbacks

    ...

end
Run Code Online (Sandbox Code Playgroud)

使用此方法,我的控制器可以非常干净,因为业务逻辑对此数据库模型一无所知.我的模型也可以是干净的,因为回调可以在多个模型上使用.控制器:

class SomeController < ApplicationController
  def destroy
    @some_model = SomeModel.find(params[:id])
    @some_model.destroy
    logger.debug "destroyed!"

    # respond to the end user
    ...
  end

end
Run Code Online (Sandbox Code Playgroud)

不幸的是,事务在around_destroy回调之后回滚,如日志中所示:

... some successful sql update queries
end of destroy callback
   (0.2ms)  ROLLBACK
destroyed!
Run Code Online (Sandbox Code Playgroud)

我试图清空around_destroy回调,但这会导致相同的行为:在around_destroy回调之后的某个地方触发回滚.

为什么会触发此回滚,如何解决此问题?

我真的想用回调来解决这个设计问题:如果我不需要在我的所有模型/控制器中构建更新 - 而不是破坏,它将节省大量工作.我怎么解决这个问题?

tee*_*tee 6

我终于想通了.我会留下另一个答案,因为它也可以,但不能直接回答你的问题.

您必须在around_destroy方法中生成,并且如果您不想删除记录,还必须将模型的销毁行为更改为不调用delete.

有两种方法可以使用around_destroy:

(1)使用课程:

class TemporalCallbacks
  def self.around_destroy(model)
    puts "TemporalCallbacks::around_destroy"
    yield model
  end
end

class SomeModel
  around_destroy TemporalCallbacks
end
Run Code Online (Sandbox Code Playgroud)

(2)在模型中使用本地方法

class SomeModel
  around_destroy :do_something

  def do_something
    puts "do_something"
    yield
  end
end
Run Code Online (Sandbox Code Playgroud)

使用任一解决方案,如果您不想删除记录,还需要更改模型中的destroy方法:

class SomeModel
  def destroy
    _run_destroy_callbacks { puts "do nothing" }
  end
Run Code Online (Sandbox Code Playgroud)

通常毁灭这样做:

def destroy
  _run_destroy_callbacks { delete }
end
Run Code Online (Sandbox Code Playgroud)