Rails has_one: through. Creating a Linked Object

I have the following data model in a Rails 2.3 application

class PortraitSubject has_many :portraits has_one :primary_portrait, :through => :portraits, :source => :asset, :conditions => ['portraits.primary = ?', true] has_many :supplementary_portraits, :through => :portraits, :source => :asset, :conditions => ['portraits.primary = ?', false] ... end class Portrait belongs_to :portrait_subject belongs_to :asset ... end 

I want to create related proxy models using Rails, but an attempt to create primary_portrait will fail. I.e.

 # This works subject = PortraitSubject.new subject.supplementary_portraits.build subject.save # This doesn't subject = PortraitSubject.new subject.build_primary_portrait # => NoMethodError: undefined method `build_primary_portrait' for #<PortraitSubject:0x007ff16fe38948> 

I'm not sure what I'm doing wrong. Looking through the Rails guides, it looks like this is possible with the has_one relation. Any help would be greatly appreciated.

+6
source share
3 answers

You will go crazy over these naming conventions. A PrimaryPortrait and a SecondaryPortrait must be special instances of Portrait not assets owned by Portrait . This already violates your design, that you cannot create it.

Try the following:

 class PortraitSubject has_many :portraits has_one :primary_portrait, :conditions => {:primary => true} has_many :supplementary_portraits, :conditions => {:primary => false} has_many :portrait_assests, :through => :portraits has_one :primary_portrait_asset, :through => :primary_portrait has_many :supplementary_portrait_assets, :through => :supplementary_portraits end 

then if you need to build primary_portait_asset write the instance method

 def build_primary_portrait_asset primary_portrait || build_primary_portrait primary_portrait.asset || primary_portrait.build_asset end 
+1
source

Why not do the following.

 class Portrait belongs_to :portrait_subject belongs_to :asset ... end 

-

 class PrimaryPortrait < Portrait ... end 

-

 class SupplementaryPortraits < Portrait ... end 

-

 class PortraitSubject has_one :primary_portrait has_many :supplementary_portraits ... end 

This is more consistent with rail design patterns. You will need to add a type column.

0
source

I would suggest dividing this into two associations:

 class PortraitSubject has_many :portraits has_one :primary_portrait, :class_name => "Portrait", :conditions => ['portraits.primary = ?', true] has_one :primary_portrait_asset, :through => :primary_portrait, :source => :asset has_many :supplementary_portraits, :class_name => "Portrait", :conditions => ['portraits.primary = ?', false] has_many :supplementary_portrait_assets, :through => :supplementary_portraits, :source => :asset ... end 

You can then use subject.build_primary_portrait to create a portrait model, and access its resource through subject.primary_portrait_asset .

0
source

All Articles