Ruby on Rails has_many через объекты ассоциации перед сохранением - PullRequest
9 голосов
/ 16 марта 2012

в проекте Ruby on Rails Я пытаюсь получить доступ к объектам ассоциации в ActiveRecord до сохранения всего в базу данных.

class Purchase < ActiveRecord::Base

  has_many :purchase_items, dependent: :destroy
  has_many :items, through: :purchase_items

  validate :item_validation

  def item_ids=(ids)
    ids.each do |item_id|
      purchase_items.build(item_id: item_id)
    end
  end

  private

  def item_validation
    items.each do |item|
      ## Lookup something with the item
      if item.check_something
        errors.add :base, "Error message"
      end
    end
  end

end

Если я построю свой объект так: purchase = Purchase.new(item_ids: [1, 2, 3]) и попытайтесь сохранить его, метод item_validation пока не заполняет коллекцию элементов, поэтому, даже если элементы установлены, у него не будет возможности вызвать метод check_something для любого из них.

Можно ли получить доступ к коллекции предметов до того, как мои модель покупки и модели ассоциации будут сохранены, чтобы я мог провести проверки на них?

Если я поменяю свой item_validation метод на:

def item_validation
  purchase_items.each do |purchase_item|
    item = purchase_item.item
    ## Lookup something with the item
    if item.something
       errors.add :base, "Error message"
    end
  end
end

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

Ответы [ 5 ]

1 голос
/ 25 декабря 2014

Удалите свой собственный item_ids= метод - rails создаст его для вас (см. collection_singular_ids = ids ).Это может решить вашу проблему.

class Purchase < ActiveRecord::Base

  has_many :purchase_items, dependent: :destroy
  has_many :items, through: :purchase_items

  validate :item_validation

  private

  def item_validation
    items.each do |item|
      ## Lookup something with the item
      if item.check_something
        errors.add :base, "Error message"
      end
    end
  end

end

Второе, что приходит мне в голову, глядя на ваш код: переместите валидацию в класс Item.Итак:

class Purchase < ActiveRecord::Base
  has_many :purchase_items, dependent: :destroy
  has_many :items, through: :purchase_items
end

class Item < ActiveRecord::Base
  has_many :purchase_items
  has_many :purchases, through: :purchase_items

  validate :item_validation

  private

  def item_validation
      if check_something
        errors.add :base, "Error message"
      end
  end
end

Ваша запись Purchase также будет недействительной, если один из Item s недействителен.

1 голос
/ 21 мая 2014

Попробуйте добавить аргумент inverse_of: в определения has_many и own_to.Аргумент inverse_of - это имя отношения в другой модели, например:

class Post < ActiveRecord::Base
  has_many :comments, inverse_of: :post
end

class Comment < ActiveRecord::Base
  belongs_to :post, inverse_of: :comments
end

Не забудьте добавить его также в другие классы, такие как PurchaseItem и Item

Надеюсь, это поможет

0 голосов
/ 29 июня 2017

Я предполагаю, что вы не можете получить к ним доступ, потому что id из Purchase недоступно до сохранения записи.Но, как вы упоминаете, у вас есть доступ к ассоциации первого уровня purchase_items, поэтому вы можете извлечь все идентификаторы и передать их в where для Item:

items = Item.where(purchase_item_id: purchase_items.map(&:id))
0 голосов
/ 23 октября 2013

Вы можете использовать model.associations = [association_objects] и Association Callback http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Association+callbacks

0 голосов
/ 26 ноября 2012

Есть ли у вас документация, в которой указано, что purchase = Purchase.new(item_ids: [1, 2, 3]) делает то, что вы ожидаете?

Мне кажется, вы просто устанавливаете атрибут non-database 'item_ids' в массив (т.е.ассоциация).

Ваша модель покупки даже не должна иметь столбцов внешнего ключа для прямой установки.Вместо этого в таблице purchase_items есть записи, которые имеют purchase_id и item_id.Чтобы создать связь между вашей покупкой и тремя предметами, вам нужно создать три записи в столярной таблице.

Что произойдет, если вы просто сделаете это вместо этого?:

purchase = Purchase.new
purchase.items = Item.find([1,2,3]) 
...