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
What I'd like to do is is call:
a = RandomProduct.find(1)
a.item_source
and if item_source doesn't already exist (= nil), then build it automatically (build_item_source).
previously, I did this with alias_chain_method, but that's not supported in Rails 3.
oh, and I also tried this to no avail:
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
Polymorphic relationship in Rails refers to a type of Active Record association. This concept is used to attach a model to another model that can be of a different type by only having to define one association.
They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile , then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user . To determine who "has" the other object, look at where the foreign key is.
ActiveRecord::Base indicates that the ActiveRecord class or module has a static inner class called Base that you're extending.
In Rails 3, alias_method_chain
(and alias_method
, and alias
) work fine:
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
But there's always accept_nested_attributes_for
as an alternative, which calls build when profile_attributes
are set. Combine it with delegate
(optional) and you won't have to worry if the record exists or not:
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"...>
If the association is always created, delegation isn't necessary (but may be helpful, anyhow).
(Note: I set :inverse_of
to make Profile.validates_presence_of :user
work and to generally save queries.)
(Rails 4, FYI)
I personally prefer setting it up with after_initialize
after_initialize :after_initialize
def after_initialize
build_item_source if item_source.nil?
end
This also works well because you can automatically use forms with what would otherwise be an empty association (HAML because it's nicer):
= form_for @product do |f|
= f.fields_for :item_source do |isf|
= isf.label :prop1
= isf.text_field :prop1
If you didn't have the item_source built already, the label and text field wouldn't render at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With