mack
mack

Reputation: 2965

Ruby on Rails - Display field from related table

I'm learning RoR and I'm having trouble getting a value from a related table to display. I've tried the suggestions mentioned here and here but I still haven't gotten it to work.

I have a customer record that has a one-to-many relationship with contacts.

Here are snips of my models:

class Contact < ActiveRecord::Base
  belongs_to :customer

class Customer < ActiveRecord::Base
  has_many :contacts, dependent: :destroy

and this is my Contacts index.html.erb that is producing the error

<% @contacts.each do |contact| %>
<tr class="<%= cycle("even pointer", "odd pointer") %>">
  <td><%= contact.first_name %> <%= contact.last_name %></td>
  <td><%= contact.customer.name %></td> <!-- this is the error line -->
  <td><%= contact.office_phone %></td>
  <td><%= contact.cell_phone %></td>
  <td><a href="<%= contact.email %>"><%= contact.email %></a></td>
  <td>
    <% if current_user.admin? %>
    <%= link_to "edit", contact, method: :edit %>
    <% end %>
  </td>
</tr>
<% end %>

The error I get is: undefined method `name' for nil:NilClass

What am I doing wrong? It seems to me like the way I'm referencing the field I'm not actually getting to the data in the Customers table but I'm not really sure. Did I build the relationship correctly in my models?

UPDATE to add controller code

    class ContactsController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :new, :create]

  def index
    @contacts = Contact.paginate(page: params[:page])
  end

  def show
    @contact = Contact.find(params[:id])
  end

  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(contact_params)
    if @contact.save
      flash[:success] = "Contact created!"
      redirect_to @contact
    else
      render 'new'
    end
  end

  def edit
    @contact = Contact.find(params[:id])
  end

  def update
    @contact = Contact.find(params[:id])
    if @contact.update_attributes(contact_params)
      flash[:success] = "Contact updated"
      redirect_to @contact
    else
      render 'edit'
    end
  end

  private

    def contact_params
      params.require(:contact).permit(:first_name, :last_name, :email, :address1,
        :address2, :office_phone, :cell_phone, :website, :city, :zip, :facebook, :twitter)
    end
end


class CustomersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :new, :create]

  def index
    @customers = Customer.paginate(page: params[:page])
  end

  def show
    @customer = Customer.find(params[:id])
  end

  def new
    @customer = Customer.new
  end

  def create
  end

  def edit
    @customer = Customer.find(params[:id])
  end

  def update
    @customer = Customer.find(params[:id])
    if @customer.update_attributes(customer_params)
      flash[:success] = "Customer updated"
      redirect_to @customer
    else
      render 'edit'
    end
  end

  private

    def customer_params
      params.require(:customer).permit(:name, :email, :address1,
        :address2, :phone, :fax, :website, :city, :zip, :facebook, :duns_number)
    end
end

EDIT: This is my contact.html.erb form.

<%= form_for @contact, html: { class: "form-horizontal form-label-left" } do |f| %>
            <%= render 'shared/error_messages', object: f.object %>
              <div class="form-group">
                <%= f.label :first_name, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
                <div class="col-md-6 col-sm-6 col-xs-12">
                  <%= f.text_field :first_name, class: 'form-control col-md-7 col-xs-12' %>
                </div>
              </div>
              <div class="form-group">
                <%= f.label :last_name, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
                <div class="col-md-6 col-sm-6 col-xs-12">
                  <%= f.text_field :last_name, class: 'form-control col-md-7 col-xs-12' %>
                </div>
              </div>
              <div class="form-group">
                <%= f.label :customer_id, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
                <div class="col-md-6 col-sm-6 col-xs-12">
                  <%= select_tag :customer_id, options_for_select(Customer.all.collect {|c| [ c.name, c.id ] }), class: 'form-control' %>
                </div>
              </div>

              <div class="form-group">
                <%= f.label :office_phone, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
                <div class="col-md-6 col-sm-6 col-xs-12">
                  <%= f.text_field :office_phone, class: 'form-control col-md-7 col-xs-12' %>
                </div>
              </div>
              <div class="ln_solid"></div>
              <div class="form-group">
                <div class="col-md-6 col-sm-6 col-xs-12 col-md-offset-3">
                  <%= f.submit "Save changes", class: "btn btn-primary" %>
                </div>
              </div>
            <% end %>

Upvotes: 2

Views: 2816

Answers (3)

Adel Dr&#246;mmaren
Adel Dr&#246;mmaren

Reputation: 21

Generally this can help

For example, you have 2 tables City and Post, each post has city_id and you want to display in the views the name of the city associated with the post, you should call in the views the bloc then the name of the table then the name of column like that:

<% @post.each do |post| %>   
   <%= post.city.name %
<% end %>

Upvotes: 1

Karl Wilbur
Karl Wilbur

Reputation: 6187

You are referencing it correctly.

It looks like your contact has not yet been saved to the database. This would cause the relation to not yet exist.

You would might need to be creating the relationship differently.

I usually do something like:

 customer = Customer.create
 customer.contacts << Contact.create({first_name: 'John', last_name: 'Smith'})

which creates the associations and commits to the DB right away. Then, in the same request, your contact's customer will be set and accessible.

You could also do this, but it's a little redundant:

 customer = Customer.create
 customer.contacts << Contact.create({first_name: 'John', last_name: 'Smith', customer: customer})

EDIT

It seems that perhaps you need to be assigning a customer to the contact when it is created/updated. In your Contact form, you'll likely need something like this (seeing your existing forms would help here):

<%= select_tag :customer_id, options_for_select(Customer.all.collect {|c| [ c.name, c.id ] }) %>

or (using simple_form):

f.input :customer_id, collection: Customer.all, selected: @contact.customer_id
# or more simply
f.association :customer

Or maybe a hidden form field if you know the Customer when building the form:

f.input :customer_id, as: :hidden, value: @customer.id

Then in your controller, add customer_id to the contact_params like this:

def contact_params
  params.require(:contact).permit(
    :first_name, :last_name, :email, :address1,
    :address2, :office_phone, :cell_phone, 
    :website, :city, :zip, :facebook, :twitter,
    :customer_id
  )
end

Upvotes: 2

Alex Antonov
Alex Antonov

Reputation: 15146

Try .try() activesupport method:

<td><%= contact.customer.try(:name) %></td>

If contact.customer is nil (Contact doesn't belongs_to Customer), :name method wouldn't be invoked.

UPDATED Another hints:

a. That's a bad thing that you have a contact without customer. Consider to add this to your model:

class Contact < ActiveRecord::Base
  ...

  validates :customer, presence: true

b. You can replace code like that:

<tr class="<%= cycle("even pointer", "odd pointer") %>">

with

<%= content_tag :tr, class: cycle('event pointer', 'odd pointer') %>

c. Replace this:

<a href="<%= contact.email %>"><%= contact.email %></a>

with this:

<%= mail_to contact.email, contact.customer.try(:name) %>

Upvotes: 1

Related Questions