как автоматически собрать связанный полиморфный объект activerecord в рельсах 3 - PullRequest
7 голосов
/ 27 сентября 2010
class ItemSource < ActiveRecord::Base
  belongs_to :product, :polymorphic => true
end

class RandomProduct < ActiveRecord::Base
  has_one :item_source, :as => :product, :autosave => true, :dependent => :destroy
end

То, что я хотел бы сделать, это позвонить:

a = RandomProduct.find(1)
a.item_source

, и если item_source еще не существует (= nil), то создать его автоматически (build_item_source).

ранее я делал это с помощью alias_chain_method, но это не поддерживается в Rails 3.
о, и я также пытался это сделать безрезультатно:

class RandomProduct < ActiveRecord::Base
  has_one :item_source, :as => :product, :autosave => true, :dependent => :destroy

  module AutoBuildItemSource
    def item_source
      super || build_item_source
    end
  end  
  include AutoBuildItemSource
end

Ответы [ 4 ]

10 голосов
/ 27 октября 2010

В Rails 3 alias_method_chainalias_method, и alias) работают нормально:

class User < ActiveRecord::Base
  has_one :profile, :inverse_of => :user

  # This works:
  # 
  #   def profile_with_build
  #     profile_without_build || build_profile
  #   end
  #   alias_method_chain :profile, :build
  #
  # But so does this:

  alias profile_without_build profile
  def profile
    profile_without_build || build_profile
  end
end

Но в качестве альтернативы всегда есть accept_nested_attributes_for, который вызывает build, когда profile_attributesустановлены.Объедините его с delegate (необязательно), и вам не придется беспокоиться, существует ли запись:

class User < ActiveRecord::Base
  has_one :profile, :inverse_of => :user
  delegate :website, :to => :profile, :allow_nil => true
  accepts_nested_attributes_for :profile
end

User.new.profile # => nil
User.new.website # => nil
u = User.new :profile_attributes => { :website => "http://example.com" }
u.profile # => #<Profile id: nil, user_id: nil, website: "http://example.com"...>

Если связь создается всегда, делегирование не требуется (но может быть полезнов любом случае).

(Примечание: я установил :inverse_of, чтобы заставить Profile.validates_presence_of :user работать и вообще сохранять запросы.)

2 голосов
/ 05 января 2014

(Rails 4, FYI)

Я лично предпочитаю настроить его на after_initialize

after_initialize :after_initialize

def after_initialize
  build_item_source if item_source.nil?
end

Это также хорошо работает, потому что вы можете автоматически использовать формы с тем, что в противном случае было быпустая ассоциация (HAML, потому что она лучше):

= form_for @product do |f|
  = f.fields_for :item_source do |isf|
    = isf.label :prop1
    = isf.text_field :prop1

Если у вас еще не был создан item_source, поле label и text не будет отображаться вообще.

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

Не то, что вам действительно нужен драгоценный камень для этого, поскольку это так просто сделать самостоятельно, но вот драгоценный камень, который еще проще объявить авто-сборку:

https://github.com/TylerRick/active_record_auto_build_associations

0 голосов
/ 28 сентября 2010

Как насчет создания item_source при создании RandomProduct:

class RandomProduct << ActiveRecord::Base
  after_create :create_item_source
end

Конечно, если вам нужно передать конкретные аргументы источнику элемента, вы можете сделать что-то вроде этого:

class RandomProduct << ActiveRecord::Base
  after_create :set_up_item_source

  protected
    def set_up_item_source
      create_item_source(
        :my => "options",
        :go => "here"
      )
    end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...