2013-03-27 3 views
9

У меня есть пример Действие в контроллере.RAILS 3 - Транзакции в контроллерах

def some_action 
product = Product.new 
product.name = "namepro" 
    if product.save 
    client.update_attribute(:product_id,product.id) 
    end 
end 

Как добавить транзакции к этому коду? Я пытаюсь с этим пример кода:

def some_action 
**transaction do** 
    product = Product.new 
    product.name = "namepro" 
    if product.save 
    client.update_attribute(:product_create,Time.now) 
    end 
**end** 
end 

Но он производит эту ошибку:

undefined method `transaction' 

Я читал об использовании транзакций в контроллерах является плохой практикой, но я не знаю, почему это причина (http://markdaggett.com/blog/2011/12/01/transactions-in-rails/)

В примере, если продукт был создан и сохранен, а обновление клиента не выполнено ... Рельсы не должны ничего делать.

спасибо.

ответ

25

Вы можете использовать транзакцию в контроллере, если хотите. Как вы отметили, это плохая практика, но если вы хотите это сделать, просто позвоните Product.transaction do вместо transaction do. transaction - метод класса на ActiveRecord::Base, поэтому вам нужно вызвать его в классе, основанном на ActiveRecord. Любой класс модели в вашем приложении будет делать (nit-picking caveat: если вы подключаетесь к различным базам данных для разных моделей, это может быть неверно ... но вы, вероятно, этого не делаете).

Причина, по которой это плохая практика, заключается в том, что она не разделяет озабоченность в соответствии с парадигмой MVC. Ваш контроллер не должен быть так озабочен реализацией ваших данных. Лучшим подходом было бы добавить метод к Product. Может быть, что-то вроде этого:

def save_and_update_create_time 
    transaction do 
    if save 
     client.update_attribute(:product_create, Time.now) 
    end 
    end 
end 

Тогда вместо вызова product.save в контроллере, вызовите product.save_and_update_client_create_time. Возможно, вам придется пройти client; это неясно из вашего кода, откуда приходит client. Если это атрибут на product, то метод выше должен работать.

Есть более, более Railsy способы сделать это, особенно если product знает о его client без каких-либо данных контроллера. Тогда вы можете просто использовать after_save обратный вызов, как это (добавить в Product класс):

after_save :update_client 

private 

def update_client(product) 
    product.client.update_attribute(:product_create, Time.now) 
end 

Тогда каждый раз, когда Product сохраняется, поле на соответствующем клиенте будет обновляться. Возможно, вам придется ввести код для проверки наличия client.

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

+0

Большое спасибо Джим, вы действительно поможете мне с вашим объяснением! – user1364684

+3

Если транзакционная логика помещается в Модель, не будут ли транзакции не ограничиваться одной моделью, чтобы не нарушать отдельные правила? Обычно существует высокая вероятность того, что транзакции охватывают несколько моделей, которые не обязательно связаны друг с другом на уровне БД. – xSNRG

+1

Да, у меня была смущение в этом аспекте моего комментария. Мне нравится идея держать его вне контроллера, но многомодельные взаимодействия должны быть где-то обернуты.Возможно, еще один класс, но в некоторых ситуациях контроллер, возможно, будет правильным местом. –

Смежные вопросы