Атрибут Rails attr_accessor в родительской модели доступен в дочерних

Контекст:

Каждый заказ имеет много предметов и логистики. Каждый товар и логистика (а также сам заказ) имеют много доходов.

Я создаю заказ + товары и логистику одновременно, используя accepts_nested_attributes_for в заказе. Однако доходы создаются с использованием обратного вызова after_create для каждой из моделей Order, Item и Logistics. Почему? Потому что, учитывая разницу в интерпретации этих моделей, так код читается чище. (Но если этот способ сделать это является причиной того, что этот вопрос задают, я, очевидно, пересмотрю свое решение!)

Один ключевой атрибут, который мне нужно сохранить в Revenues, — это pp_charge_id. Но pp_charge_id это не то, о чем должны беспокоиться ни Order, ни Items, ни Logistics. Я прикрепил attr_accessor :pp_charge_id к заказу, так что он работает нормально, однако, когда я нахожусь в дочерних моделях товаров или логистики, у меня больше нет доступа к pp_charge_id, который снова мне нужен для сохранения связанного дохода. Как мне это сделать?

Код контроллера:

@order = Order.new(params) #params includes Order params, and nested params for child Item & Logistics
@order.pp_charge_id = "cash"
@order.save #I need this to not only save the Order, the children Item & Logistics, but then to also create the associated Revenue for each of the aforementioned 3 models

Код модели ЗАКАЗА:

has_many :items
has_many :revenues

attr_accessor :pp_charge_id
after_create :create_revenue

def create_revenue
  self.revenues.create(pp_charge_id: self.pp_charge_id)
end

#This WORKS as expected because of the attr_accessor

Код модели ПУНКТ/ЛОГИСТИКА:

has_many :revenues
belongs_to :order

after_create :create_revenue

def create_revenue
  self.revenues.create(pp_charge_id: self.order.pp_charge_id)
end

 #This DOES NOT work because self.order.pp_charge_id is nil

ЗАКАЗАТЬ код модели:

belongs_to :order
belongs_to :item
belongs_to :logistic

Опять же, я понимаю, что attr_accessor не предназначен для сохранения по запросу или даже при перезагрузке самого Ордена. Но также не имеет смысла сохранять его избыточно в таблице, которая ему не нужна. Если единственный способ сделать это — указать pp_charge_id в параметрах заказа и сохранить все сразу (включая доходы), дайте мне знать, потому что я знаю, как это сделать. (Опять же, лучше бы этого избежать из-за того, как это интерпретируется: параметры поступают от пользователя, данные о доходах - это то, что я предоставляю)


person james    schedule 11.02.2016    source источник


Ответы (2)


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

# order.rb
def create_revenue
  revenues.create(pp_charge_id: pp_charge_id)
  items.each {|i| i.revenues.create(pp_charge_id: pp_charge_id)}
  logistics.each {|l| l.revenues.create(pp_charge_id: pp_charge_id)}
end

EDIT. В качестве альтернативы вы можете добавить inverse_of к своим объявлениям belongs_to, и тогда, я думаю, Item#create_revenue увидит тот же экземпляр Order, который вы установили в контроллере. Итак, если вы также добавили attr_accessor к классу Item, вы могли бы написать его create_revenue следующим образом:

# item.rb
def create_revenue
  revenues.create(pp_charge_id: pp_charge_id || order.pp_charge_id)
end

Это должно охватывать новое требование, которое вы упомянули в своем комментарии.

person Paul A Jungwirth    schedule 11.02.2016
comment
Ах, это единственный способ? Еще одна вещь, которую я должен был упомянуть, это то, что иногда я создаю дочерний элемент для существующего родителя, например @order.items.create(pp_charge_id:""), и в этом случае я напрямую передаю pp_charge_id и хотел бы использовать обратный вызов after_create для дочернего элемента. (Это также напоминает мне, что мне нужно использовать attr_accessor :pp_charge_id для дочернего элемента) - person james; 12.02.2016
comment
@james Я добавил альтернативу, которая должна соответствовать обоим подходам. - person Paul A Jungwirth; 12.02.2016
comment
inverse_of сработало! хотя это странно... я думал, что это может быть ответ, но потом я прочитал и увидел, что Rails 4 автоматически выводит inverse_of. Угадайте, в этом случае, магия не поняла - person james; 12.02.2016

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

Order.create_with_charge(:cash, params)

меня беспокоит сохранение избыточной информации в базе данных только потому, что так код читается чище!

person phoet    schedule 11.02.2016
comment
Это справедливо, но в вашем пользовательском методе, как бы вы передали первый параметр cash в Revenue, созданный в дочерней модели Item/Logistic? - person james; 12.02.2016