Mel
Mel

Reputation: 2715

Rails 4 - Polymorphic Associations & instances

I am making an app in Rails 4.

I have an address model, which is polymorphic.

My associations are:

profile.rb

has_many :addresses, as: :addressable

address.rb

 belongs_to :addressable, :polymorphic => true

In my address.rb, I have a method for:

def country_name
    self.country = ISO3166::Country[country]
    country.translations[I18n.locale.to_s] || country.name
  end

In my profile views folder, I have a partial to display a user's country, as:

<span class="profilesideinfo">
    <% if @addresses.country_name.present? %>
        <%= @addresses.country_name.titlecase %> 
    <% else %>
        <span class="profileeditlink">
            <%= link_to "Add your location", new_address_path %>
        </span>
    <% end %>       
</span>

I have also tried:

<% if @profile.addresses.country_name.present? %>
        <%= @profile.addresses.country_name.titlecase %> 
    <% else %>
        <span class="profileeditlink">
            <%= link_to "Add your location", new_address_path %>
        </span>
    <% end %>   

And I have also tried (changing country_name (which is the method in my model) to country (which is the attribute in my address table))

Im getting stuck, because I can successfully create an address, but then that country name should show in the profile show partial. It doesn't. Instead, I get an error that says:

undefined method `country_name' for nil:NilClass

Is there something else that needs to happen for a reference to a polymorphic instance to show in the parent.

Can anyone see what needs to happen to get this to work?

I have tried making a scope in my profile model as:

Profile.joins(:addresses).where("addresses.profile_id = ?", profile_id)

I'm not sure this is adding any value. I'm not sure how to use it or whether it works, given that the address model doesn't have a foreign key for profile_id.

When I save this and try to reload the page, I get this error:

undefined local variable or method `profile_id' for #<Class:0x007fbacaec2d18>

Taking SurreyMagpie's suggestion below, I change the partial to:

<% if @profile.addresses.country_name.any? %>
        <%= @profile.address.country_name.first.titlecase %> 
    <% else %>
        <span class="profileeditlink">
            <%= link_to "Add your location", new_address_path %>
        </span>
    <% end %>

I am checking if there are any addresses and then displaying the first country name.

When I try this, I get this error:

undefined method `country_name' for #<ActiveRecord::Associations::CollectionProxy []>

Upvotes: 0

Views: 72

Answers (1)

surreymagpie
surreymagpie

Reputation: 327

The polymorphic nature of the association is not responsible for your difficulties here.

When you define country_name as a method in your Address class, that is known as an instance method. As such, you are able to call it on a single Address object. So, if in the rails console you run Address.first.country_name you should receive a result for that specific instance of Address.

However, when you query a has_many association, in your case @profile.addresses, you get an ActiveRecord::Associations::CollectionProxy returned, even if there is only a single Address record. It is not exactly an array but has very similar behaviour.

The point is: you cannot call your instance method on the whole set at once. You need to iterate through the collection and call the method on each instance. For example:

<% if @profile.addresses.any? %>
  <% @profile.addresses.each do |address| %>
    <span class="profilecountry">
      <%= address.country_name.titlecase %>
    </span>
  <% end %>
<% else %>
  <span class="profileeditlink">
    <%= link_to "Add your location", new_address_path %>
  </span>
<% end %>

may be what you need - though how you choose to display multiple countries is up to you.

Upvotes: 0

Related Questions