Check for join in View - ruby-on-rails

I have something like
<% #users.each do |u| %>
...
<% #interests.each do |i| %>
<% if i joins u in interests_users %> <-- That is not working that way! = Problem
...
<% i.id %>
...
<% end %>
<% end %>
...
<% end %>
I need to output each interest-id that joins users in interests_users for every user. But i cannot use a sql query for each user, because that would be too much for the server, so i query the whole interests table and want to filter it in the view.
Anyone got a simple solution for that?

You're thinking about this too view-centric - this is a data issue, so the model(s) should take care of it.
I'm guessing you have an n-m relationship between users and interests, with the interests-users join table in the middle. The models should define that relationship something like this:
class User < ActiveRecord::Base
has_and_belongs_to_many :interests
end
class Interest ...
has_and_belongs_to_many :users
end
This is with has_and_belongs_to_many (HABTM). The models then already take care of "selecting" which interests are "on" each user, simply query them with user_instance.interests, i.e. do
<% #users.each do |u| %>
...
<% u.interests.each do |i| %>
If that generates a query for interests for each user, you can eager load the data when getting the users:
#users = User.includes(:interests).all
Edit:
Oh yeah and if you want to list all interests and mark those a user has associated, something like this should work:
#interests.each do |interest|
if u.interests.include?(interest) ...

Related

Rails 4 - How to populate an array through a form

I have three tables: illness, symptom and a secondary table to link the other two on a has_many through: relationship.
I wish to implement a system where users type in their symptoms and receive a list of possible illnesses.
Using the code below I can use find(1,2), for example, to show all illnesses that have symptoms 1 and 2 simultaneously. However, I want these two constants to be user-supplied data fed through a form. I'm somewhat new to RoR so I don't know how to do this.
symptoms = Symptom.find(params[:symptom_ids]) # symptom_ids is an array of ids
illnesses = Illness.joins(illness_symptoms: :symptom)
.where("symptoms.id in (?)", symptoms)
.group("illnesses.id")
.having("count(illnesses.id) >= ?", symptoms.length)
In the model:
scope :with_symptoms, -> (symptoms) { joins(illness_symptoms: :symptom)
.where("symptoms.id in (?)", symptoms)
.group("illnesses.id")
.having("count(illnesses.id) >= ?", symptoms.length) }
I use these with Illness.with_symptoms(Symptom.find(1,2)) (I'm looking for a way to replace 1,2 with user-supplied data).
You can do something like this in a form:
<% form_tag some_path do %>
<% #symptoms.each do |symptom| %>
<%= label_tag do %>
<%= check_box_tag "symptom_ids[]", symptom.id %>
<%= symptom.name %>
<% end %>
<% end %>
<% end %>
User will be able to use checkboxes to choose symptoms.

Access object in the join table

I have a #minisets model and a #miniatures model. They both have_many of each other through the #contents model.
As well as the foreign keys, the #contents model also has a quantity column.
From my #minisets show view I can show the associated #miniatures with the following:
<% #miniset.miniatures.each do |miniature| %>
<%= link_to miniature.name, miniature %>
<% end %>
I want to be able to show the quantity entered for those miniatures but can't work out how to call information from the join table rather than the table it is joining.
Something like <%= miniature.content.quantity %> except that won't work. I assume the joining model must be in play for it to be supplying the joined info but how do I interact with it itself in that instance?
Figured it out.
I need to be working with the join object in the instance variable rather than the joined object.
Find the #contents which belong to this #miniset and then get the #miniature info from there. Makes much more sense.
<% #miniset.contents.where(miniset_id: #miniset).each do |content| %>
<%= link_to content.miniature.name, content.miniature %>
x<%= content.quantity %>
<% end %>
Found some very complicated answers to similar questions but this is dead simple. Hope it helps someone.

Rails - Retrieve data from multiple tables

I have read so much about Rails in the past week in the process of trying to learn it. The problem is when I need something I find it difficult to recall where I read it! ha ha
I have two models: Guest and Booking where Guests has_many Bookings. Therefore the Booking model has a guest_id field included.
I would like to retrieve all the booking data and the guest data and display in a view as one object. I know I have seen a simple solution to this (in the Rails doc I think) that does just this.
At the moment I'm doing the following in my BookingsController:
#bookings = Booking.all
and in my associated view I have:
<% #bookings.each do |booking| %>
<p><%= booking.id %></p>
<p><%= booking.email %></p> //this does not work as email is in guests table!!!
<% end %>
but how can I get the linked Guest table included too?
If your booking belongs to guest
class Booking < ActiveRecord::Base
belongs_to :guest
...
end
you can first include guest to avoid n+1 queries
#bookings = Booking.includes(:guest).all
and then in view, traverse the association
<% #bookings.each do |booking| %>
<p><%= booking.id %></p>
<p><%= booking.guest.email %></p>
<% end %>
In your bookings controller:
#bookings = Booking.includes(:guest).all

Rails views based on HABTM Category

So I have this app where I'm using a HABTM association to determine "User Skills"; When a new user is created (via the new user view) the user can declare his/her skills via a group of HABTM Checkboxes available on that view with the form...
What I want to do is to have a view where I have Links based on the different skills, for example: "policemen", "doctors", "musicians" etc. And these links should point to other views where I can show to the visitor a list of only the users that belong to the specific category they clicked on.
My users/skills models (association part) look like this:
#User Model
class User < ActiveRecord::Base
has_and_belongs_to_many :skills
#Skill Model
class Skill < ActiveRecord::Base
has_and_belongs_to_many :users
And (if it's helpful) my HABTM checkboxes look like this:
<p> What Skills do you have?
<% for skill in Skill.find(:all) %>
<div>
<%= check_box_tag "user[skill_ids][]", skill.id, #user.skills.include?(skill) %>
<%= skill.name %>
</div>
<% end %>
</p>
Let's say the skills we have are: "policeman, doctor, musician" for example... How can I create links in a view wich point to the group of users that have X skill and then with what code could I render some views that display lists with only the users that belong to X skill category?
I bet the solution is really simple... But I'm missing something obvious, maybe. Could you point me in the right direction?
Thanks!
In config/routes.rb:
resources :skills
Generate a SkillsController with rails g controller skills and put there:
def index
#skills = Skill.all
end
def show
#skill = Skill.find(params[:id])
end
Then your views:
#app/views/skills/index.html.erb
<ul>
<% #skills.each do |skill| %>
<li><%= link_to skill.name, skill_path(skill) %></li>
<% end %>
</ul>
and
#app/views/skills/show.html.erb
<h1>Users who have the <%= #skill.name %> skill</h1>
<ul>
<% #skill.users.each do |user| %>
<li><%= user.full_name %></li>
<% end %>
</ul>
First of all dont use the has_and_belongs_to_many. Here is a link to RoR Guides showing how you are supposed to do the has_many :through assosiation. http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
Secondly if you want to show your user that have 'x' skill it just the skills#show action.
def show
#skill = Skill.find params[:id]
#users = #skill.users
end
And on your links to view this would be something like skill_path(skill)

How to access ActiveRecord includes programmatically

I have a Rails 3 ActiveRecord that belongs_to two different ActiveRecords. Example
class Animal < ActiveRecord::Base
belongs_to: species
belongs_to: zoo
...
end
where the animals table contains a species_id, zoo_id, name, and description and the tables species with a scientific_name and zoo has address.
In the controller, I have a query
#animals = Animal.includes(:species, :zoo).order(:name)
and a list of columns I want displayed in the view,
#columns = ["name", "description", "species.scientific_name", "zoo.address"]
In the view, I want the creation of a HTML table to be driven by the list of column names, e.g.
<table>
<tbody>
<tr>
<% #animals.each do |animal| %>
<% %columns.each do |col| } %>
<td><%= animal[</td>
<% end %>
<% end %>
</tr>
</tbody>
</table>
this works great for animals's name and description, but does not work for species.scientific_name and zoo.address.
I know I could special case the loop and access the included classes directly like animal.species['scientific_name'], but I was hoping there would be a way to access the included classes by name. Something like animal['species']['scientific_name']
Approach 1
Monkey patch the ActiveRecord class. Refer to this answer for details about monkey patching AR class.
class ActiveRecord::Base
def read_nested(attrs)
attrs.split(".").reduce(self, &:send)
end
end
Sample nested attribute access:
animal.read_nested("zoos.address")
user.read_nested("contacts.first.credit_cards.first.name")
product.read_nested("industry.category.name")
For your use case:
Controller:
#columns = %w(name color zoo.address species.scientific_name)
View
<% #animals.each do |animal| %>
<% #columns.each do |col| } %>
<td><%= animal.read_nested(col)%></td>
<% end %>
<% end %>
Approach 2
Add select clause to select the columns and alias them.
#animals = Animal.includes(:species, :zoo).select("
animals.*,
species.scientific_name AS scientific_name,
zoos.address AS zoo_address").
order(:name)
Now in your view, you can access attributes like scientific_name, zoo_address like regular model attributes.

Resources