Как сохранить что-то в базе данных после неудачных проверок ActiveRecord? - PullRequest
5 голосов
/ 07 октября 2010

В основном я хочу записать действие на MyModel в таблице MyModelLog.Вот некоторый псевдокод:

class MyModel < ActiveRecord::Base
  validate :something

  def something
     # test
     errors.add(:data, "bug!!")
  end
end

У меня также есть модель, похожая на эту:

class MyModelLog < ActiveRecord::Base

  def self.log_something
    self.create(:log => "something happened")
  end

end

Чтобы войти, я попытался:

  • Добавить MyModelLog.log_something в методе something MyModel

  • Вызов MyModelLog.log_something в after_validation обратном вызове MyModel

В обоих случаях создание откатывается при сбое проверки, поскольку оно находится в транзакции проверки.Конечно, я также хочу войти, когда проверки не пройдены.На самом деле я не хочу входить в файл или куда-то еще, кроме базы данных, потому что мне нужны отношения записей журнала с другими моделями и возможность делать запросы.

Какие у меня варианты?

Ответы [ 6 ]

8 голосов
/ 22 октября 2010

Вложенные транзакции действительно работают в MySQL.

Вот что я попробовал для недавно сгенерированного проекта rails (с MySQL):

./script/generate model Event title:string --skip-timestamps --skip-fixture

./script/generate model EventLog error_message:text --skip-fixture

class Event < ActiveRecord::Base                                                                                                                                       
  validates_presence_of :title                                                                                                                                         
  after_validation_on_create :log_errors                                                                                                                               

  def log_errors                                                                                                                                                       
    EventLog.log_error(self) if errors.on(:title).present?                                                                                                             
  end                                                                                                                                                                  
end  

class EventLog < ActiveRecord::Base                                                                                                                                    
  def self.log_error(event)                                                                                                                                            
    connection.execute('BEGIN') # If I do transaction do then it doesn't work.
    create :error_message => event.errors.on(:title)                                                                                            
    connection.execute('COMMIT')                                                                                                                                       
  end                                                                                                                                                                  
end 

# And then in script/console:
>> Event.new.save
=> false
>> EventLog.all
=> [#<EventLog id: 1, error_message: "can't be blank", created_at: "2010-10-22 13:17:41", updated_at: "2010-10-22 13:17:41">]
>> Event.all
=> []

Может быть, я слишком упростила или упустила какой-то смысл.

3 голосов
/ 08 октября 2010

Подойдет ли это для Наблюдателя ? Я не уверен, но я надеюсь, что существует вне транзакции ... У меня есть аналогичная необходимость, где я мог бы хотеть удалить запись при обновлении ...

2 голосов
/ 24 октября 2010

Я решил такую ​​проблему, воспользовавшись переменной областью видимости Ruby. В основном я объявил переменную error вне блока транзакции, затем перехватил, сохранил сообщение журнала и снова поднял ошибку.

Это выглядит примерно так:

def something
    error = nil
    ActiveRecord::Base.transaction do
        begin
            # place codez here
        rescue ActiveRecord::Rollback => e
            error = e.message
            raise ActiveRecord::Rollback
        end
    end
    MyModelLog.log_something(error) unless error.nil?
end

Объявляя переменную error вне области транзакции, содержимое переменной сохраняется даже после завершения транзакции.

1 голос
/ 24 октября 2010

MyModelLog.log_something должно быть сделано с использованием другого соединения.

Вы можете заставить модель MyModelLog всегда использовать другое соединение, используя create_connection .

class MyModelLog < ActiveRecord::Base
  establish_connection Rails.env # Use different connection

  def self.log_something
    self.create(:log => "something happened")
  end
end

Notуверен, что это правильный способ ведения журнала !!

1 голос
/ 07 октября 2010

Я не уверен, относится ли это к вам, но я предполагаю, что вы пытаетесь сохранить / создать модель из вашего контроллера.В контроллере легко проверить результат этого действия, и вы, скорее всего, уже делаете это, чтобы предоставить пользователю полезную вспышку;так что вы можете легко зарегистрировать соответствующее сообщение там.

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

Как вы думаете?

0 голосов
/ 07 октября 2010

Вы можете использовать вложенную транзакцию.Таким образом, код в вашем обратном вызове выполняется в другой транзакции, чем неудачная проверка.Документация Rails для ActiveRecord :: Transactions :: ClassMethods обсуждает, как это делается.

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