Использование around_destroy для обновления вместо удаления в базе данных в Rails 3 - PullRequest
2 голосов
/ 18 января 2012

У меня есть модель базы данных с парой таблиц, которые выглядят так:

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

В приложении данные (в основном) используются как отдельные значения;важны различные значения в столбцах:

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       

Этот дизайн вызывает некоторые проблемы: я не могу просто «вставить», «обновить» и «удалить» некоторое значение для value_2 псевдо-таблицы, так какможет повлиять на значения в других столбцах: если я удаляю value_2 для 15-01-2012, удаление всей записи изменит как value_2, так и value_3 псевдо-таблицу.

Очевидное решениедля этой проблемы (для меня) является использование обратных вызовов для улучшения действий создания, обновления и уничтожения с правильным поведением, используя обратные вызовы * 1013. * Я создал класс для этих обратных вызовов:

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

Используя этот метод, мой контроллер может быть очень чистым, поскольку бизнес-логика ничего не знает об этой модели базы данных. Мои модели также могут быть чистыми, поскольку обратные вызовы могут использоваться на нескольких моделях. Контроллер:

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

    # respond to the end user
    ...
  end

end

К сожалению, транзакция откатывается после обратного вызова around_destroy, как показано в журналах:

... some successful sql update queries
end of destroy callback
   (0.2ms)  ROLLBACK
destroyed!

Я пытался очистить обратный вызов around_destroy,но это приводит к тому же поведению: откат срабатывает, где-то после обратного вызова around_destroy.

Почему этот откат срабатывает и как я могу это исправить?

Я действительно хочу решить этот дизайнпроблема с использованием обратных вызовов: это сэкономило бы много работы, если бы мне не пришлось создавать обновление вместо взлома во всех моих моделях / контроллерах.Как я могу решить это?

Ответы [ 2 ]

6 голосов
/ 29 января 2012

Я наконец понял это. Я оставлю другой ответ, так как он тоже работает, но не дает прямого ответа на ваш вопрос.

Вы должны уступить в методе 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

(2) Использовать локальный метод в модели

class SomeModel
  around_destroy :do_something

  def do_something
    puts "do_something"
    yield
  end
end

При любом решении вам также необходимо изменить метод уничтожения в модели, если вы не хотите удалять запись:

class SomeModel
  def destroy
    _run_destroy_callbacks { puts "do nothing" }
  end

Обычно уничтожить делает это:

def destroy
  _run_destroy_callbacks { delete }
end
1 голос
/ 25 января 2012

Когда вы добавляете обратный вызов around_destroy к модели, вам необходимо указать yield, который возвращает обратные вызовы before_ и after_, и сам метод destroy. С before_ и after_ вам не нужно уступать. Когда вы добавляете это к вашей модели:

around_destroy { puts "in around"; yield; puts "out around" }
before_destroy { puts "before" }
after_destroy { puts "after" }

$ model.destroy
=> before
=> in around
=> (actual destroy happens here)
=> out around
=> after  

Чтобы изменить фактическое поведение уничтожения / удаления модели, взгляните на Параноик , который архивирует записи, а не уничтожает их.

Вы можете сделать что-то вроде параноика, чтобы изменить поведение уничтожения и удаления:

def destroy
  _run_destroy_callbacks { delete }
end

def delete    
  self.update_attribute(:deleted_at, Time.now) if !deleted? && persisted?
  freeze
end

Вот еще более простой вариант: https://gist.github.com/1474640/3571c53159d3b3ae3506d4e625e7cd8fb5e3f9d6

Также см. Этот вопрос: Каков наилучший способ переопределить поведение уничтожения Rails ActiveRecord?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...