2

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 %>
3
  • Let's see the controller code too. What action are you calling? Commented Sep 11, 2015 at 20:06
  • I added the code for my controllers. Commented Sep 12, 2015 at 15:55
  • Thanks. I would assume that you are making Contacts for a Customer. Can you post your Contact form? I think we need to get a Customer field in there somewhere. Is the Contact created in a nested form? Are you using nested routes? Commented Sep 12, 2015 at 16:14

3 Answers 3

2

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
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks Carl. I was testing this by trying to create the relationships in my seed file so maybe I need to look there. Using the try() method as recommended by asiniy shows all blanks for the name so maybe I do have an issue with the records.
I see your controllers now, thank you. Nowhere in your controllers are you creating the relationships. When you are creating or updating you will need to pass in the related objects, minimally the object ids.
thanks for your help. I'm moving in the right direction now but I'm unable to add a new contact still. The error I get is that company cannot be empty....yet I have a company selected.Any guesses as to why? I updated my contacts controller code also.
Your contact_params needs to permit :customer_id.
Thanks again @Karl Wilbur, I added that to the contact but I get the same error. I dropped to the rails console and tried to validate ActiveRecord::RecordInvalid: Validation failed: Customer can't be blank.
|
1

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) %>

1 Comment

Thanks @asiniy, when I use the try() method the page loads but the field is blank through the whole table. When I use the suggested change for the email address, it works perfectly. Any idea why the one would work but the other wouldn't?
1

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 %>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.