Displaying has_many, :through association records - ruby-on-rails

I am working on an application a deep association. A Story belongs to a Planning Level, the Planning Level belongs to one or many Programs.
class Program < ActiveRecord::Base
has_and_belongs_to_many :planning_levels
has_many :stories, :through => :planning_levels
end
class PlanningLevelsPrograms < ActiveRecord::Base
end
class PlanningLevel < ActiveRecord::Base
has_and_belongs_to_many :programs
has_many :stories
end
class Story < ActiveRecord::Base
belongs_to :planning_level
end
Within the Program show page I'd like to display the Program, each Planning Level and aggregate Story count for each Planning Level.
I'm not sure how to access the Story model from the Program show page.
The following works great for displaying each Planning Level belonging to the Program.
<% #program.planning_levels.each do |p| %>
<p><%= p.name %></p>
<% end %>
...but I have no idea how to make the following work for each displayed Planning Level. How do I access the Story model from with the Program? Is there something needed in the Program controller that I'm missing. Thanks in advance!
#program.planning_level.stories.count(:id)

Each Planning Level:
<% #program.planning_levels.each do |planning_level| %>
<p><%= planning_level.name %></p>
# Aggregate Story count for each planning_level
<p><%= planning_level.stories.count %></p>
<% end %>
Aggregate Story count for #program (if you want):
#program.stories.count
Hope this can help you.

In your view, you can simply use the model associations by name to do this. Try this code as a starting point for your display needs:
<% #program.planning_levels.each do |planning_level| %>
<p><%= planning_level.name %> with <%= planning_level.stories.count %> stories</p>
<% planning_level.stories.each do |story| %>
<p><%= story.name %></p>
<% end %>
<% end %>
You can output any details that you choose for the stories loop. You can add styling to get the presentation that you need.
For instance, you might consider formatting this as a nested list, like so:
<% #program.planning_levels.each do |planning_level| %>
<ul>
<li>Planning Level: <%= planning_level.name %> with <%= planning_level.stories.count %> stories
<ul>
<% planning_level.stories.each do |story| %>
<li>Story: <%= story.name %></li>
<% end %>
</ul>
</li>
</ul>
<% end %>
Adding CSS class and id attributes would give you the ability to add styling to the elements to give your UI some flair.

Related

How to loop through a self referential model in Ruby on Rails

I'm new to ruby on rails and I'm building a wiki app where the navigation is to be sorted by categories. Each article, or page, can belong to a category, but a category can also be a sub-category of another category. An administrator will be able to create new categories or sub-categories calling for a dynamic approach to generating a list of categories for the menu. I'm trying to figure out how to display a list of all parent categories and all of their children and grandchildren categories where the menu would look something like this:
1. Parent1
1.a Child1
1.b Child2
2. Parent2
2.a Child1
2.a.1 Grandchild1
I currently have some nested loops in my view which kind of work, but it's not dynamic since it will only show the first two generations, and I would have to repeat the code to show more.
Model:
class Category < ApplicationRecord
has_many :sub_categories, class_name: "Category", foreign_key: "category_id"
belongs_to :category, class_name: "Category", optional: true
end
Controller:
class CategoriesController < ApplicationController
def index
#sorted_categories = Category.order(:sort_number).where("category_id IS NULL")
#sub_categories = Category.order(:sort_number).where("category_id IS NOT NULL")
end
end
View:
<% if #categories.nil? %>
<h3>There are currently no categories.</h3>
<% else %>
<ul>
<% #sorted_categories.each do |c| %>
<li><%= c.name %><%= link_to 'Move Up', categories_move_up_path(c) %> Sort:<%= c.sort_number %></li>
<% #sub_categories.each do |s| %>
<% if s.category_id == c.id %>
<ul>
<li>
<%= s.name %><%= link_to 'Move Up', categories_move_up_path(s) %> Sort:<%= s.sort_number %>
</li>
</ul>
<% end %>
<% end %>
<% end %>
</ul>
<% end %>
Any advice would be greatly appreciated, thanks!
Have a look at the acts_as_list gem, it does exactly what you want.
It will define a parent_id column, and each object will be a child of a parent, so that you can create infinite tree of categories ans sub-categories.
It also provides the methods to move objects up and down.

Rails association works in the console but not in the view

to-many association between 2 modles. It works perfectly in the console but in the view just I get object-references appears like this:
#<Author:0x0000000434bf80>
#<Author:0x000000043485b0>
This appears in my view which has this code:
<h1 class="page-title">Articles</h1>
<hr>
<div class="category-container">
<ul class="category-titles">
<% #cat.each do |c| %>
<li><%= link_to c.catName, category_path(c) %></li>
<% end %>
</ul>
</div>
<br><br><br><hr>
<% #art.each do |t| %>
<p class="articles-list-page"><%= link_to t.artTitle, article_path(t) %></p>
<p><%= t.author %></p>
<% end %>
Here is my association in Author Model
class Author < ActiveRecord::Base
has_many :articles
end
and Here is my association in Article Model
class Article < ActiveRecord::Base
belongs_to :category
belongs_to :author
end
I could not understand why it is working well in the console but not in the view
It works fine in the view.
This line:
<p><%= t.author %></p>
outputs the author model. What you probably want to do is output the author name - something like
<p><%= t.author.name %></p>
You're attempting to output an ActiveRecord relation to the view. There's probably no situation ever where you'd want to display an entire ActiveRecord object in a view. Instead, you'd want to display particular attributes of the object.
Such as:
t.author.created_at
t.author.name
t.author.whatever
However, if there was some strange reason you wanted to output the entire object to the view, you could use inspect like so:
t.author.inspect
UPDATE:
To answer the other issue you're running into, you'll need to make sure that you actually have a related Author for each of the Articles before trying to output an Author attribute to the view. You can accomplish that like so:
<% if t.author.present? %>
<p><%= t.author.authName %></p>
<% else %>
<p>No author available</p>
<% end %>
Or like so, if you want to use a terniary operator to keep things on one line:
<p><%= t.author.present? ? t.author.authName : 'No author available' %></p>
Or if you don't care about returning a default value such as "No author available" if an author isn't available, then you could just do something like this:
<p><%= t.author.try(:authName) %></p>
You should delegate that author attributes to Article model
class Article < ActiveRecord::Base
belongs_to :category
belongs_to :author
delegates :authName, allow_nil: true
end
Also in your controller use following code
class ArticleController < ApplicationController
def index
#art = Article.includes(:author).all
end
end
And in your view use like bellow
<% #art.each do |t| %>
<p class="articles-list-page"><%= link_to t.artTitle, article_path(t) %></p>
<p><%= t.authName %></p>
<% end %>

Using associations in Ruby on Rails

I'm learning RoR and I'm trying to understand associations. I've got two models - Company [name] and Note [company_id, notes]. As shown, the Note model has a company_id field to reference the primary key in the Company model.
Within a Notes view, I'm trying to display the Company name but I can't seem to get this to work.
I want to display
(SELECT name FROM Company WHERE Company.id=Note.company_id)
instead of note.company_id in the code below.
company.rb
class Company < ActiveRecord::Base
has_many :notes
end
note.rb:
class Note < ActiveRecord::Base
belongs_to :company
default_scope -> { order(date: :desc) }
end
notes/index.html.erb
....
<% #notes.each do |note| %>
<% if note.active %>
<p>
<%= note.date %>
</br>
<%= note.company_id %> - <%= note.contact %>
</br>
<%= note.notes %>
<!-- <td><%= note.active %></td> -->
</p>
<% end %>
<% end %>
....
To answer your specific question, try:
note.company.name
I would also recommend reading up on Rails partials, particularly how to render a collection: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

Rails: how to count number of users?

here is my models.
User
unit_id
Unit
block_id
Block
postalcode_id
Postalcode
neighbourhood_id
Neighbourhood
name
the relations is for all is top belongs to bottom
this is my current index.html.erb file, i wish to output the number of user in each neighbourhood.
<% provide(:title, 'Neighbourhoods') %>
<ul class="thumbnails">
<% #neighbourhoods.each do |neighbourhood| %>
<li class="span3">
<div class="thumbnail">
<div style="position:relative;">
<%= link_to "Join", '#', class: "btn-join" %>
<%= image_tag(neighbourhood.name+".jpg", alt: neighbourhood.name) %>
</div>
<h2 style="margin-bottom:0px"><%= neighbourhood.name.titleize %></h2>
<% neighbourhood.postalcodes.each do |postalcode| %>
<%= postalcode.blocks.map(&:block).join(", ") %>
<% end %>
<br>
<%= neighbourhood.streetname.titleize %>
</div>
</li>
<% end %>
</ul>
Thanks in advance.
Assuming a Neighborhood has_many Users:
<%= neighbourhood.users.size %>
Note that counting is a relatively slow option, so you can optionally cache the number of users for speed using counter_cache:
class User < ActiveRecord::Base
belongs_to :neighborhood, :counter_cache => true
end
Then in a migration:
add_column :neighborhoods, :users_count, :integer, :default => 0
Seems like an awfully deeply nested set of associations. You may want to take a second look at your models and see if you can 'trim them down' a little. Maybe something like just have a User and Unit model, then add block, postal code and neighbourhood to Unit where you could do Unit.block, and Unit.postal_code...ect.
That being said with your current configuration (assuming correct associations of has_many/belongs_to) you should be able to do something like:
Neighbourhood.postal_code.block.unit.users.count
Good luck!

Rails: check presence of nested attribute

If I have the following nested model relationships (all has_many):
Countries < Cities < Streets < Homes
In a show view, how can I check if a particular Country has any homes?
Edit:
Adding the suggested method of chaining with the map method (first try to map to streets). So far it's not restricting the records
<% #countries.each do |country| %>
<% if country.cities.map(&:streets).any? %>
....
<% end %>
<% end %>
You can call or #country.cities.map(&:streets).flatten.map(&:homes).present? or #country.cities.map(&:streets).map(&:homes).any?
<% if #country.cities.map(&:streets).flatten.map(&:homes).flatten.any? %>
Tro-lol-lo yo-lo-puki
<% end %>
Also you can wrap this long line into your model method:
class Country < ActiveRecord::Base
def any_homes?
cities.map(&:streets).flatten.map(&:homes).flatten.any?
end
end
Usage
<% if #country.any_homes? %>
Tro-lol-lo yo-lo-puki
<% end %>
And of course it looks like a good data structure for refactoring! It wants to be refactored!

Resources