JustNeph
JustNeph

Reputation: 781

Has_One through a model when the model has_many

I'm trying to set up a system where my Billing Profile can hold an Address and Payment Method through a User. With addresses, it works fine since User only has one but with Payment Method, since User has_many, I keep getting the error:

ActiveRecord::HasManyThroughSourceAssociationNotFoundError: 
Could not find the source association(s) :payment_method_id in model User. 
Try 'has_many :payment_method, :through => :user, :source => <name>'. 
Is it one of :address, :billing_profile, or :payment_methods?**

BillingProfile

class BillingProfile < ActiveRecord::Base
  attr_accessible :user, :payment_method
  belongs_to :user
  has_one :address, :through => :user
  has_one :payment_method, :through => :user
end

User

class User < ActiveRecord::Base
  ...

  has_one :billing_profile

  has_many :payment_methods

  has_one :address, :as => :addressable
  accepts_nested_attributes_for :address
end

Address

class Address < ActiveRecord::Base
  belongs_to :addressable, :polymorphic => true

  attr_accessible :city, :country, :state, :street_line_1, :street_line_2, :zip_code
end

PaymentMethod

class PaymentMethod < ActiveRecord::Base
  attr_accessible :user_id, :is_default

  belongs_to :user

  validates_presence_of :user_id
end

BillingProfile Table

create_table :billing_profiles do |t|
  t.integer :user_id
  t.integer :payment_method_id
  t.timestamps
 end

Any idea how if this is even possible or if there is a better way to approach it? I have dabbled in the idea of just manually setting the id's when I create the Billing Profile but then I'd have to create methods to get the payment method which is not terrible but it'd be nice if Rails was able to do it for me.

EDIT

So since it appears that what I hoped for is not possible. I simple added a method to Billing Profile to simulate the association.

def payment_method
    PaymentMethod.find(payment_method_id)
  end 

Upvotes: 0

Views: 130

Answers (1)

fotanus
fotanus

Reputation: 20106

The :through option is there to link relationships. So, the following hold true:

billing_profile.user.address == biling_profile.address

However, the following can't be true (because you want only one in one side and a list in the other):

billing_profile.user.payment_methods == billing_profile.payment_method

You need an extra column to make this relationship. You can only use the :through relationship if both returns the same, because there is no "extra memory" to hold only one from a list.

So, in short, you need to add a has_one or a belong_to without :trhough and add a new column (extra memory) to hold this relationship.

Upvotes: 2

Related Questions