List sections with books associated with them - ruby-on-rails

I'm fairly new to ruby on rails and this has been kind of an interesting problem since this seems easy to implement in other languages but I don't know how to tackle it in this one. There was a similar post to this but it had two separate models which I would like to avoid.
This is my end goal:
Section Name
Book A, author
Book B, author
Section Name
Book C, author
Book D, author
Ideally, I'd like to have books be one model, so my model looks like this:
Book Model
class Book < ApplicationRecord
validates :section, :title, :author, presence: true
Book Controller
def index
#books = Book.all
I'm assuming I would need some sort of view that has it list it like below but I'm not sure how to go from there.
<% #sections.each do |section| %>
<% Book.each do |book| %>
<%= book.name %>
<% end %>
<% end %>
Any help would be very appreciated!

Firstly you need migration and associations between these models
change_table :books do |t|
t.belongs_to :section, foreign_key: true, null: false
end
class Book < ApplicationRecord
belongs_to :section
class Section < ApplicationRecord
has_many :books, dependent: :destroy
And in view you can iterate through sections and separately through evert section books
<% #sections.each do |section| %>
<div><b><%= section.name %></b></div>
<ul>
<% section.books.each do |book| %>
<li>
<%= book.name %>, <%= book.author %>
</li>
<% end %>
</ul>
<% end %>

what you need is this:
<% #sections.each do |section| %>
<% section.books.each do |book| %>
<%= book.name %>
<% end %>
<% end %>

Related

Where's the textNode in the view coming from?

# controller
def index
#teams = TeamMember.where(user: current_user)
end
# view
<%= #teams.each do |t| %>
<h2><%= t.project.name %></h2>
<p>Where's the line below this coming from?</p>
<!-- what's happening here? -->
<% end %>
The result in the browser looks as follows. It returns #projects as a string. Where is this coming from and how do I remove it?
ERB
You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output.
You have used <%= %> for looping projects and the block form of Array#each returns the original Array object, which is why it prints the projects result at the end.
you have to use <% %> for #projects.each do |p| instead of <%= %>
# view
<% #projects.each do |p| %>
<h2><%= p.name %></h2>
<% end %>
Team member Model
class TeamMember < ApplicationRecord
belongs_to :user
belongs_to :project
end
User Model
class User < ApplicationRecord
has_many :team_members
end
Project Model
class Project < ApplicationRecord
has_many :team_members
end
Controller
def index
#teams = current_user.team_members
end
View
<% #teams.each do |t| %> // Remove '=' from the loop itereation
<h2> <%= t.project.name %> </h2>
<% end %>

Rails accepts_nested_attributes_for and a ternary association

tl, dr: Is it possible to populate a ternary association with accepts_nested_attributes and consequently pass the tests from this PR?
Basically I created four models A, B, C and Abc and the latter is a join table for a ternary association with the other models. The problem is using accepts_nested_attributes_for I can't seem to save the whole ternary association. Instead I either create two join models with binary associations (A-B, B-C or A-C) or the database complains about missing foreign keys. Checkout this test and the code:
class A < ApplicationRecord
has_and_belongs_to_many :bs, join_table: :abcs
has_and_belongs_to_many :cs, join_table: :abcs
accepts_nested_attributes_for :bs, :cs
end
class B < ApplicationRecord; end
class C < ApplicationRecord; end
class Abc < ApplicationRecord
belongs_to :a
belongs_to :b
belongs_to :c
end
class AsController < ApplicationController
def new
#a = A.new
#a.bs.build
#a.cs.build
end
def create
#a = A.new(a_params)
if #a.save
redirect_to(new_a_path)
else
render(:new)
end
end
def a_params
params.require(:a).permit(:name, bs_attributes: [:name], cs_attributes: [:name])
end
end
<%= form_for(#a) do |f| %>
<% if #a.errors.any? %>
<ul>
<% #a.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= f.label(:name) %>
<%= f.text_field(:name) %>
<ul>
BS:
<%= f.fields_for(:bs) do |ff| %>
<li>
<%= ff.label(:name) %>
<%= ff.text_field(:name) %>
</li>
<% end %>
</ul>
<ul>
CS:
<%= f.fields_for(:cs) do |ff| %>
<li>
<%= ff.label(:name) %>
<%= ff.text_field(:name) %>
</li>
<% end %>
</ul>
<%= f.submit %>
<% end %>
I've also tried creating it from the ternary table which works for one single association among the models (A-B-C), but it's impossible for multiple (A1-B1-C1, A1-B2-C1). Check out the code:
class AsController < ApplicationController
def new
#abc = Abc.new
#abc.build_a
#abc.build_b # 2.times { #abc.build_b } only overwrites #abc.b
#abc.build_c
end
end
It seems no matter what Rails can only create associations between two models leaving the ternary model missing one association.
I have isolated the issue as a repository's pull request as you can see HERE

Displaying User Avatar (without duplicates) for each post in a Collection List with Rails 5

👋 all,
I want to display a list of collections with many posts.
So I call all collections in the controller as:
def index
#collections = Collection.order("RANDOM()")
end
Then in the View:
<% #collections.each do |collection| %>
<%= link_to collection.title, collection %>(<%= collection.posts.count %>)
<!-- Designers (Users) -->
<% collection.posts.each do |post_designer| %>
<!-- I want to display designer avatars in here, I have designer_id from the post, but how do I access Designer table to pull avatar? -->
<%= post_designer.designer_id %>
<% end %>
<!-- Images -->
<% collection.posts.each do |post| %>
<%= link_to image_tag(post.image.thumb.url.to_s, class: "fr"), collection %>
<% end %>
<% end %>
My question is that:
I want to display designer avatars in here instead of designer_id, I have designer_id from the post, but how do I access Designer table to pull avatar?
Thank you!!!! 🙏
Relations:
models/collection.rb
class Collection < ApplicationRecord
belongs_to :designer
has_many :collectivizations
has_many :posts, through: :collectivizations
end
models/collectivization.rb
class Collectivization < ApplicationRecord
belongs_to :post
belongs_to :collection
end
models/post.rb
class Post < ApplicationRecord
belongs_to :category
belongs_to :designer
has_many :collectivizations
has_many :collections, through: :collectivizations
---------------
👍 SOLUTION
It looks like I just an obvious typo error! 🤦‍♂️ The code below works, but it gives dublicates if there is more than 1 post for an user. How can I fix the duplicates?
<% collection.posts.each do |post_designer| %>
<%= link_to image_tag(post_designer.designer.avatar.url.to_s, class: "avatar-small ml1"), post_designer, class: "fl" %>
<% end %>
Try change your code like below. It will fetch first post for designer and you won't see any duplicates for designer.
<% collection.posts.select("DISTINCT ON (designer_id) *").each do |post_designer| %>
<%= link_to image_tag(post_designer.designer.avatar.url.to_s, class: "avatar-small ml1"), post_designer, class: "fl" %>
<% end %>

How to put conditions on associated records?

Here are my models:
class Checklist < ActiveRecord::Base
has_many :checklists_tasks
has_many :tasks, through: :checklists_tasks
end
class ChecklistsTask < ActiveRecord::Base
belongs_to :checklist
belongs_to :task
end
class Section < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :section
has_many :checklists_tasks
has_many :checklists, through: :checklists_tasks
end
I then have a view like this:
<% #sections.each do |section| %>
<h2><%= section.name %></h2>
<ul>
<% section.tasks.each.do |task| %>
<li><%= task.name %></li>
<% end %>
</ul>
<% end %>
How do I query Section and make sure that the tasks associated with each Section are all associated with a certain Checklist?
To clarify, /checklists/1/show and /checklists/2/show should use the view above and ouput the same sections, but the tasks in the sections could be different.
If you mean to show the section's tasks which are also belong to current checklist, try this:
<% #sections.each do |section| %>
<h2><%= section.name %></h2>
<ul>
<% # get the checklist's id in url => /checklists/:id %>
<% check_list_id = params[:id] %>
<% # query tasks using section_id and check_list_id %>
<% tasks = Task.find_by(section_id: section.id, check_list_id: check_list_id) %>
<% tasks.each.do |task| %>
<li><%= task.name %></li>
<% end %>
<% end %>
Ended up solving this by adding a method to Section.
class Section < ActiveRecord::Base
has_many :tasks
def tasks_by_checklist(checklist)
if checklist != nil
Task.where(section_id: self.id, checklists_tasks: { checklist_id: checklist.id}).includes(:checklists_tasks)
else
self.tasks
end
end
end
Then updated the view to use this new method:
...
<% section.tasks_by_checklist(#checklist).each.do |task| %>
...
Since #checklist is nil in /tasks (index), but set in /checklist/:id/show, the same view works as intended for both controller actions.
I'm still curious if there is a way to do it by editing #sections = Section.all in the controller to something more clever.

Rails 3.1 :has_many, :through complex ordering

I have an orders model with line_items and vendors. When displaying an order, I want to group line_items by vendors.
class LineItem < ActiveRecord::Base
belongs_to :order
belongs_to :vendor
end
class Order < ActiveRecord::Base
has_many :line_items
has_many :vendors, :through => :line_items
end
class Vendor < ActiveRecord::Base
has_many :line_items
end
I want to display a sorted list of vendors and line items:
You have placed an order for the following items:
Vendor 1
Line item 1
Line item 2
Line item 3
Vendor 2
Line Item 4
Line Item 5
...
My current thought is
order.vendors.each do |a_vendor|
a_vendor.name
!!?? AND THEN WHAT GOES HERE ??!!
end
please help. I can't figure this out. maybe this could be done by sorting?
If the order only has one vendor, then I only want to show the one vendor.
How about this:
<% #order.line_items.all.group_by{|i| i.vendor}.each do |vendor, items| %>
<%= content_tag :h2, vendor.id %>
<ul>
<% items.each do |i| %>
<%= content_tag :li, i.id %>
<% end %>
</ul>
<% end %>
[edits]
sort_by(&:vendor) is the same as sort_by{|v| v.vendor}, but the block-style syntax gives you a little more flexibility. For example, you can sort by vendor name in the controller with:
#sorted = #order.line_items.all.group_by(&:vendor).sort_by{|vendor, items| vendor.name}
Then in the view:
<% #sorted.each do |vendor, items| %>
<%= content_tag :h2, vendor.name %>
<ul>
<% items.each do |i| %>
<%= content_tag :li, i.id %>
<% end %>
</ul>
<% end %>
Alternatively, can sort in the model by adding an SQL snippet
to the :order option of the has_many association.
(See: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many)
class Order < ActiveRecord::Base
has_many :line_items, :order => "item_number"
has_many :vendors, :through => :line_items, :order => "name"
end
Then your view is very simple:
<% #order.vendors.each do |vendor| %>
<h3><%= vendor.name %></h3>
<ul>
<% vendor.line_items.where(:order_id=>#order.id).each do |item| %>
<li><%= item.description %></li>
<% end -%>
</ul>
<% end -%>

Resources