#ruby-on-rails #ruby #activerecord
#ruby-on-rails #ruby #activerecord
Вопрос:
В моем приложении Rails 4 у меня есть следующие модели:
class Invoice < ActiveRecord::Base
has_many :allocations
has_many :payments, :through => :allocations
end
class Allocation < ActiveRecord::Base
belongs_to :invoice
belongs_to :payment
end
class Payment < ActiveRecord::Base
has_many :allocations, :dependent => :destroy
has_many :invoices, :through => :allocations
after_save :update_invoices
after_destroy :update_invoices # won't work
private
def update_invoices
invoices.each do |invoice|
invoice.save
end
end
end
Проблема в том, что мне нужно обновлять invoice
, когда один из его payments
уничтожается.
Очевидно, что приведенный update_invoices
выше обратный вызов никогда не может быть запущен, потому что в момент его вызова соединение с invoice
уже было уничтожено.
Итак, как это можно сделать?
Прямо сейчас я делаю это в своем PaymentsController
:
def destroy
@payment.destroy
current_user.invoices.each do |invoice|
invoice.save
end
...
end
Однако, конечно, это очень дорого, потому что оно проходит через все, invoice
что есть у a user
.
Что может быть лучшей альтернативой этому?
Спасибо за любые отзывы.
Комментарии:
1. что именно вы пытаетесь сохранить в счете-фактуре? приведенный выше код, похоже, не загрязняет счет-фактуру
2. @blotto: Много чего. Например
outstanding_amount
, иpayment_status
.
Ответ №1:
Одним из решений было бы захватить счета-фактуры перед уничтожением экземпляра платежа. Однако это добавляет немного больше логики к контроллеру, но именно здесь возникает цель обоих действий (уничтожение оплаты и обновление счетов-фактур). Это также сокращает итерацию только до тех счетов, на которые повлиял уничтоженный платеж.
def destroy
invoices = @payment.invoices
@payment.destroy
invoices.each do |invoice|
invoice.save
end
...
end
Предположительно, вы переопределяете save
метод модели Invoice (или также выполняете обратный вызов), хотя я бы выбрал более явный метод для этого намерения. Например, removed_payment
может быть метод для обработки этого конкретного сценария и обновления соответствующих атрибутов — outstanding_amount
и payment_status
и т. Д.
def destroy
invoices = @payment.invoices
@payment.destroy
invoices.map(amp;:removed_payment)
...
end
Комментарии:
1. Это странно. Я не могу заставить работать ни одно из ваших предложений. Возможно ли, что
invoices
переменная каким-то образом изменяется при@payment
уничтожении? Тем не менее, он все еще работает, когда я используюcurrent_user.invoices
.2. попробуйте переименовать
invoices
в примере выше. хотя я сомневаюсь в его столкновении — возможно, вызовите егоaffected_invoices
. попробуйте также вывести на консоль, чтобы убедиться, что платеж действительно имеет какие-либо счета. напримерp affected_invoices.count
3. Я понял. В моем первоначальном сообщении я присвоил
:dependent => :destroy
вызов вPayment
модели. Таким образом, при@payment
уничтожении больше нет связи между платежами и счетами, а счета, хранящиеся в переменнойinvoices
, больше не могут быть доступны. Извините, я должен был включить эту строку в свой первый пост. Есть ли альтернативный подход к вашему?
Ответ №2:
Проблема в том, что связанное распределение также уничтожается при уничтожении платежа. Если вместо этого вы переместите обновление счета в модель распределения, оно будет работать так, как задумано.
class Allocation < ActiveRecord::Base
belongs_to :invoice
belongs_to :payment
after_destroy :update_invoice
def update_invoice
if destroyed?
invoice.save!
end
end
end
Вот тестовый проект Rails 4.1 с тестами для этого:
Комментарии:
1. Вот и все! Большое вам спасибо.