Подтвердить что ты не робот

Ruby on Rails - вложенные атрибуты: как мне получить доступ к родительской модели из дочерней модели

У меня есть пара таких моделей

class Bill < ActiveRecord::Base
  has_many :bill_items
  belongs_to :store

  accepts_nested_attributes_for :bill_items
end

class BillItem <ActiveRecord::Base
  belongs_to :product
  belongs_to :bill

  validate :has_enough_stock

  def has_enough_stock
    stock_available = Inventory.product_is(self.product).store_is(self.bill.store).one.quantity
    errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity
  end
end

Вышеуказанная проверка так явно не работает, потому что, когда я читаю bill_items из вложенных атрибутов внутри формы счета, атрибуты bill_item.bill_id или bill_item.bill недоступны до сохранения.

Итак, как я могу сделать что-то подобное?

4b9b3361

Ответ 1

Вот как я решил это в конце концов; путем установки родителя при обратном вызове

  has_many :bill_items, :before_add => :set_nest

private
  def set_nest(bill_item)
    bill_item.bill ||= self
  end

Ответ 2

В Rails 4 (не тестировалось в более ранних версиях) вы можете получить доступ к родительской модели, установив опцию inverse_of на has_many или has_one:

class Bill < ActiveRecord::Base
  has_many :bill_items, inverse_of: :bill
  belongs_to :store

  accepts_nested_attributes_for :bill_items
end

Документация: Двунаправленные ассоциации

Ответ 3

Bill_item.bill должен быть доступен, вы можете попробовать сделать raise self.bill.inspect, чтобы увидеть, есть он там или нет, но я думаю, что проблема в другом месте.

Ответ 4

Я "исправил" его, установив родительский элемент в обратном вызове:

class Bill < ActiveRecord::Base
  has_many :bill_items, :dependent => :destroy, :before_add => :set_nest
  belongs_to :store

  accepts_nested_attributes_for :bill_items

  def set_nest(item)
    item.bill ||= self
  end
end

class BillItem <ActiveRecord::Base
  belongs_to :product
  belongs_to :bill

  validate :has_enough_stock

  def has_enough_stock
        stock_available = Inventory.product_is(self.product).store_is(self.bill.store).one.quantity
    errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity
  end
end

Метод set_nest сделал трюк. Пожелайте, чтобы он стал стандартным с accepts_nested_attributes_for.

Ответ 5

Да, такая проблема может быть раздражающей. Вы можете попробовать добавить виртуальный атрибут в свою модель Билла Item следующим образом:

class BillItem <ActiveRecord::Base
  belongs_to :product
  belongs_to :bill

  attr_accessible :store_id

  validate :has_enough_stock

  def has_enough_stock
   stock_available = Inventory.product_is(self.product).store_is(load_bill_store).one.quantity
   errors.add(:quantity, "only #{stock_available} is available") if stock_available < self.quantity
  end

  private

  def load_bill_store
    Store.find_by_id(self.store_id)
  end
end

И тогда, на ваш взгляд, вы можете добавить скрытое поле, подобное этому:

<%= bill_item.hidden_field :store_id, :value => store_id %>

Это не было тестом, но это может сработать. Возможно, вам не кажется желательным иметь store_id в html, но это может и не быть проблемой. Позвольте мне знать, если это помогает.