teresa
teresa

Reputation: 366

Rails how to access belongs_to table without using iteration

This seems like a common but tricky problem, but I wasn't able to find any clue on how to solve this online. I have 2 tables, userprofile and programs. Each userprofile has_many :programs and programs belongs_to :userprofile.

Userprofile.rb

class Userprofile < ActiveRecord::Base
  attr_accessible :user_id, :programs_attributes
  has_many :programs

  accepts_nested_attributes_for :programs

Program.rb

class Program < ActiveRecord::Base
    attr_accessible :userprofile_id, :program_name, :program_role, :program_year

    belongs_to :userprofile
end

My problem is, I want to show the heading ("Programs") only if userprofile.programs is not empty and if each of the program_name, program_role and program_year is not empty.

When I use an iterator, I can do a check on the program_name, program_role, program_year. But the "Program" heading prints multiple times (depending on the number programs that the user has fully filled in).

<% @userprofile.programs.each do |program| %> 
  <% if program.blank? || program.program_name.blank? || program.program_role.blank? || program.program_year.blank? %>
  <% else %>
    <h3>Program </h3> <br />
    <%= program.program_name %><br />
    <%= program.program_role %><br /> 
    <%= program.program_year %>
   <% end %>
 <% end %>

But when I move the heading out of the loop, I am not sure how to do checks:

<% if @userprofile.programs.blank? || @userprofile.programs.program_name.blank? %>
<% else %>
  <h3>Program </h3> <br />
  <% @userprofile.programs.each do |program| %> 

    <%= program.program_name %><br />
    <%= program.program_role %><br /> 
    <%= program.program_year %>

  <% end %>
<% end %>

This will result in a undefined method 'program_name' error. I thought taking the heading out of the iteration and somehow accessing the program_name, program_role, program_year would solve the problem but I'm not sure how to achieve that properly.

I also tried mapping from (how to access fields in belongs_to association in rails)

<%= @program_names = @userprofile.programs.map(&:program_name) %>
<%= @program_years = @userprofile.programs.map(&:program_year) %>
<%= @program_roles = @userprofile.programs.map(&:program_role) %>

  <% if @userprofile.programs.blank? || @program_names.empty? || @program_years.empty? || @program_roles.blank? %>
    <% else %>

      <h3>Program </h3> <br />

       <% @userprofile.programs.each do |program| %> 

         <% if program.program_name.blank? %>
         <% else %>

           <%= program.program_name %><br />
           <%= program.program_role %><br /> 
           <%= program.program_year %>
         <% end %>
       <% end %>
     <% end %>

Which seemed to work, except when all the arrays are, for example: ["","",""] or [nil, nil,nil]. The "Program" heading still shows up when it should be hidden.

Any suggestions would help. Thank you!

Upvotes: 0

Views: 56

Answers (1)

oreoluwa
oreoluwa

Reputation: 5623

For an effecient query, you should do:

@userprofile.programs.where.not(program_name: nil, program_role: nil, program_year: nil)

The query is not all encompassing though as empty strings will still be included, but you can filter that by adding an extra logic to your model.

#models/program.rb
...
def displayable?
  program_name.present? && program_role.present? && program_year.present?
end
...

Then in your view, you should be able to do

<% programs.each do |program| %>
   <% if program.displayable? %>
      <%= program.program_name %><br />
      <%= program.program_role %><br /> 
      <%= program.program_year %>
   <% end %>
<% end %>

UPDATE

As @Éric-côté pointed out, you should be able to do:

scope :displayable_programs, -> { where.not(program_name: nil, program_role: nil, program_year: nil) }

With this approach, in your view, you would do:

<% programs = @userprofile.programs.displayable_programs %>
<% if programs.exists? %>
    <h3>Program </h3> <br />
    <% programs.each do |program| %>
       <% if program.displayable? %>
          <%= program.program_name %><br />
          <%= program.program_role %><br /> 
          <%= program.program_year %>
       <% end %>
    <% end %>
<% end %>

Also following your previous approach, you could use a kind of tracker, e.g,

<% program_shown = false %>
<% @userprofile.programs.each do |program| %> 
  <% if program.blank? || program.program_name.blank? || program.program_role.blank? || program.program_year.blank? %>
  <% else %>
    <% unless program_shown %>
      <h3>Program </h3> <br />
    <% program_shown = true %>
    <% end %>
    <%= program.program_name %><br />
    <%= program.program_role %><br /> 
    <%= program.program_year %>
   <% end %>
 <% end %>

Upvotes: 1

Related Questions