bkong28
bkong28

Reputation: 15

RAILS 4 - How to show NAME instead of ID in index view?

rails newbie here. I want my questions/view/index to show the name of the language associated with a question, rather than the language_id.

My question model is:

class Question < ActiveRecord::Base
  validates :phrase, presence: true
  has_many :answers, dependent: :delete_all
  belongs_to :language
end

class CreateQuestions < ActiveRecord::Migration
  def change
    create_table :questions do |t|
      t.string :phrase
      t.string :language

      t.timestamps
    end
  end
end

class AddLanguageIdToQuestions < ActiveRecord::Migration
  def change
    add_column :questions, :language_id, :integer
  end
end

My language model is:

class Language < ActiveRecord::Base
end

class CreateLanguages < ActiveRecord::Migration
  def change
    create_table :languages do |t|
      t.string :name

      t.timestamps
    end
  end
end

In my questions controller:

def index
  @questions = Question.all
  @language = Language.find(@questions.language_id)
end

In the questions/_form.html.erb:

<p>
  <%= f.label :language_id %><br>
  <%= f.select :language_id, @languages.map { |l| [l.name, l.id] }, {:prompt => 'select language'} %>
</p> 

And in the questions/view/index.html.erb:

<% @questions.each do |question| %>
    <li>"<%= link_to question.phrase, question %>" in <%= question.language.name %>?%></li>
<% end %>

The error I keep getting, despite trying several variations of "question.language.name" (which works just fine in the show view) is "undefined method "language_id" in the index view.

Any help would be very much appreciated.

Upvotes: 0

Views: 2321

Answers (3)

Abdul Baig
Abdul Baig

Reputation: 3721

just change your index action to:

def index
  @questions = Question.all.includes(:language)
end

edit

<% @questions.each do |question| %>
    <% unless question.language.nil? %>
    <li>"<%= link_to question.phrase, question %>" in <%= question.language.name %>?%></li>  <% end %>
<% end %>

OR

<% @questions.each do |question| %>
    <li>"<%= link_to question.phrase, question %>" in <%= question.language.name unless question.language.nil? %>?%></li>
<% end %>

Both will work fine depends on you what do you want.

feel free to ask if problem continues or not solved.

Your Problem

What you were doing wrong was:

finding all questions and then this line was wrong finding all question's language at once.

  @language = Language.find(@questions.language_id)

And to avoid this: better solution is to avoid N + 1 query problem using includes

Upvotes: 1

Nicholas.V
Nicholas.V

Reputation: 1936

@questions.language_id is incorrect because you are trying to retrieve a single id from an array. i.e. Question.all returns an array. Also, Language.find requires a single id parameter to return a single language.

What exactly are you trying to return with Language.find(@questions.language_id) ? An array of languages that have a question that belong to it?

Also, where is the _form.html.erb partial called? By default, rails scaffold will call the partial in the new and edit actions and therefore if you are trying to set @languages for the select field in the form partial, then you would not do it in the index action. Also, Consider using a collection select for this field as it is for an association.

Upvotes: 0

Richard Peck
Richard Peck

Reputation: 76774

ActiveRecord Associations

This sounds like a job for ActiveRecord Associations, specifically the has_many association:

enter image description here

ActiveRecord associations basically use a foreign_key in your database to pull relational data & append to your object. Currently, you're only focused on using a single object, without any associated data.

--

Your problem can be fixed using the following:

#app/models/question.rb
Class Question < ActiveRecord::Base
   belongs_to :language
end

#app/models/language.rb
Class Language < ActiveRecord::Base
   has_many :questions
end

This will allow you to call the following in your controller & view:

#app/controllers/questions_controller.rb
Class QuestionsController < ApplicationController
   def index
       @questions = Question.all
   end
end

#app/views/questions/index.html.erb
<% @questions.each do |question| %>
   <%= question.language.name %>
<% end %>

--

Bonus

You can use the .delegate method to provide you with the ability to stop the law of dementer issue:

#app/models/question.rb
Class Question < ActiveRecord::Base
   belongs_to :language
   delegate :name, to: :language, prefix: true #-> @question.language_name
end

Upvotes: 1

Related Questions