Linking to Dynamic Content in Rails? - ruby-on-rails

I have three models. User, Product, and Season.
I used a standard "has many through" approach:
user has_many :seasons
user has_many :products, :through => :seasons
product has_many :seasons
product has_many :users, :through => :seasons
seasons belong_to :product
seasons belong_to :user
On my "show" view for my users, I display the user's products. I do this as follows:
<% #user.seasons.each do |c| %>
<%=h c.product.name %>
<% end %>
This all works great.
Here's my question. How do I create a hyperlink to the show view of whatever product is generated by this code <%=h c.product.name %>? I followed the API and tried using a block, but none of my attempts worked properly.

<% #user.seasons.each do |c| %>
<%= link_to c.product.name, c.product %>
<% end %>

Related

Querying and linking to from join tables

Two Questions:
1) I have a retreats/:id view where I can display the team names that are affixed to a specific retreat. I can view the team names with the following query in the view:
<p>Teams: <%= #retreat.teams.pluck(:name).to_sentence %></p>
However, instead of just displaying the name, how would I both display the name of the team and link to the team team/:id
2) In this retreats/:id view, I would also like to display the users that are part of a team, but I am really stuck trying to go through sql joins, etc.
models
retreat.rb
class Retreat < ApplicationRecord
belongs_to :user
delegate :name, to: :user, prefix: true
belongs_to :account
validates :name, presence: true
has_many :retreat_teams
has_many :teams, through: :retreat_teams
accepts_nested_attributes_for :retreat_teams
end
team.rb
class Team < ApplicationRecord
belongs_to :account
has_many :team_members
has_many :users, through: :team_members
accepts_nested_attributes_for :team_members
has_many :retreats
has_many :retreats, through: :retreat_teams
end
team_members.rb
class TeamMember < ApplicationRecord
belongs_to :team
belongs_to :user
end
First part can be done this way
<% retreat.teams.each do |team| %>
<%= link_to(team.name, team_path(team.id)) %> # or whatever the path helper is
<% end %>
Second part, you can run this query instead
#teams = Team.where(retreat_id: #retreat.id).includes(:users)
Then in UI you can show like this
<% #teams.each do |team| %>
Team: <%= link_to(team.name, team_path(team.id)) %> # or whatever the path helper is
Team Users: <%= team.users.pluck(:name).to_sentence %>
<% end %>
Hope that helps!
1) I have a retreats/:id view where I can display the team names that
are affixed to a specific retreat. ...However, instead of just
displaying the name, how would I both display the name of the team and
link to the team team/:id
Don't use .pluck unless you actually want just a single column as an array or the raw data from several columns without instantiating model instances. .pluck is both overused and misused. It makes zero sense to pluck something if you then need to fetch the rest of the columns later anyways.
Instead just iterate though the model instances:
<% #resort.teams.each do |team| %>
<%= link_to team.name, team %>
<% end %>
If you have declared the route with resources :teams. Rails will figure out the route all by itself - that's the power of convention over configuration.
2) In this retreats/:id view, I would also like to display the users
that are part of a team, but I am really stuck trying to go through
sql joins, etc.
You don't really have to do any work joining. Just eager_load the association to avoid n+1 queries:
def show
#resort = Resort.eager_load(teams: :users).find(params[:id])
end
<% #resort.teams.each do |team| %>
<div class="team">
<h3><%= link_to team.name, team %></h3>
<h4>Members:</h4>
<% if team.users.any? %>
<ul>
<%= team.users.each do |user| %>
<li><%= link_to user.name, user %></li>
<% end %>
<ul>
<% end %>
</div>
<% end %>
Another note about naming
The name TeamMember is unfortunate as it implies that its the actual person that's a member and not just a join model.
Membership or Position are better name choices.
class Team
has_many :memberships
has_many :members, through: :memberships
end
class Membership
belongs_to :team
belongs_to :member, class_name: 'User'
end
class User
has_many :memberships, foreign_key: :member_id
has_many :teams, through: :memberships
end
This will let you iterate through team.members and actually get the users instead of some join model. The above example would read after refactoring:
def show
#resort = Resort.eager_load(teams: :members).find(params[:id])
end
<% #resort.teams.each do |team| %>
<div class="team">
<h3><%= link_to team.name, team %></h3>
<h4>Members:</h4>
<% if team.members.any? %>
<ul>
<%= team.members.each do |member| %>
<li><%= link_to member.name, member %></li>
<% end %>
<ul>
<% end %>
</div>
<% end %>

Iterate over has_many through relationship and include data from joining table

I have a very simple rails app with three models: Recipes, Ingredients, and a joining table Quantities that stores the amount of each ingredient in the recipe. For one recipe, I want to list all the associate ingredients and the amount found in the joining table. How do I iterate over the ingredients, but also include the data from the quantities table?
class Recipe < ActiveRecord::Base
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, :reject_if => :all_blank, :allow_destroy => true
end
and:
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, through: :quantities
end
and finally joining table:
class Quantity < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
accepts_nested_attributes_for :ingredient, allow_destroy: true
end
It seems like it should be really easy to do this iteration but I am not sure how.
show.html.erb:
<% #recipe.ingredients.each do |ingredient| %>
<% #I know the line below is wrong, but not sure how
# to iterate over the ingredients for the recipe and
# include the amount field from the quantities table
# as well as the ingredient name. %>
<li><%= ingredient.amount ingredient.name %></li>
<% end %>
Thank you!
In your controller's action do something like this:
#recipe = Recipe.includes(:ingredients, :quantities).find(params[:id]) # avoid N+1
and then, in your view:
<% #recipe.quantities.each do |quantity| %>
<%= quantity.ingredient.name %> -
<%= quantity.amount %>
<% end %>
The join table quantities is likely to have one one row for a combination of recipe and ingredient, even though the has_many :through implementation allows for multiple rows.
This allows for accessing the ingredient quantity and name as follows:
<% #recipe.ingredients.each do |ingredient| %>
<li>
<%= ingredient.quantities.first.amount %>
<%= ingredient.name %>
</li>
<% end %>

how to display data from a associated model

Currently stuck on a problem
I have a company.rb model that has_many :applications
The application.rb model belongs_to :company and has_many :answers
The answer.rb model belongs_to :application.rb and has_many :users
The user.rb model has_many :answers
I allow a company to create an application. There they can input questions. The user can view them and their answers will be stored in answer.rb.
What I'm trying to do now is display all current_company.applications that have received an answer.
I tried this:
<% #applications.all.each do |f| %>
<%= f.answers.answer_1 %><br>
<% end %>
whilst having my controller:
def applicants
#applications = current_company.applications
end
however I get undefined method `answer_1'. It doesn't seem I'm available to access it. I store it like this:
the applications has a company_id and the answers has an application_id and a user_id.
I thought that by doing i the way I do now I'm able to access all applications created by the current company. From there I can view all application_id in the answers as those are the one's I'm outputting but it's not working.
I think you understood the way you can access nested models wrongly.
When an application has_many: :answers, then #application.answers is a collection you can iterate through. For example (in your view):
<% #applications.each do |application| %>
<% application.answers.each do |answer| %>
<%= answer.text %>
<% end %>
<% end %>
(assuming that your answer model has a text attribute).

How do I save many-to-many relationships in a model

I have the following setup in my rails app:
A user registers and he chooses from a set of check boxes for Music Styles.
The Music Styles are only 4 right now but should be extendable. I'd like to have a list of MusicStyles that I can extend and change easily.
My approach would be to create a model 'MusicStyles' and a model 'UserMusicStyles' and then use a has_many_through association similar to:
class User < ActiveRecord::Base
has_many :user_music_styles
has_many :music_styles, :through => :user_music_styles
end
class UserMusicStyle < ActiveRecord::Base
belongs_to :user
belongs_to :music_style
end
class MusicStyle < ActiveRecord::Base
has_many :music_styles
has_many :users, :through => :user_music_styles
end
Now, during registration I would do something like MusicStyle.all.each do |m| ... to display the checkboxes but how do I save it to the database correctly in the user controller?
Any help much appreciated!
You can do it like this:
<%= form_for #user do |f| %>
<!-- User stuff -->
...
<% MusicStyle.all.each do |m| %>
<%= check_box_tag('user[music_style_ids][]', m.id, #user.music_styles.include?(m)) %>
<% end %>
<%= f.submit 'Save' %>
<% end %>

Compile data from different models into one list

My app has a user model, as well as multiple other date related models/tables such as anniversaries, holidays, birthdays, and and "other dates" custom model.
I have a user dashboard view that lists them all separately as shown below. How can i display all of these lists as one (call it upcoming events or something) that is listed chronologically and shows them upcoming dates for a certain period of time.
View
*note - These are displayed in a table/list but i stripped html for clarity
<h1>Holidays</h1>
<% if #user.holidays.any? %>
<% #user.holidays.each do |hld| %>
<%= hld.name %>
<%= hld.date %>
<% end %>
<h1>Friends Birthdays</h1>
<% if #user.friends.any? %>
<% #user.friends.each do |frd| %>
<%= frd.name %>
<%= frd.dob %>
<% end %>
<h1> Anniversary </h1>
<% if #user.anniversaries.any? %>
<% #user.anniversaries.each do |ann| %>
<%= ann.spouse_name %>
<%= ann.anniversary_date %>
<% end %>
Thanks!
Models
class User < ActiveRecord::Base
has_many :friends
has_many :occasions
has_many :user_holidays
has_many :holidays, :through => :user_holidays
has_many :anniversaries
class Holiday < ActiveRecord::Base
has_many :user_holidays
has_many :users, :through => :user_holidays
end
class Friend < ActiveRecord::Base
belongs_to :user
end
class Anniversary < ActiveRecord::Base
belongs_to :user
end
Assuming you want to be efficient (you could just combine the arrays, sort them and be done with it), there is no direct way to do it through the relations. I am assuming you have an events model which has a foreign key to the user, in that case,
Events.where(:user_id => #user.id).where(<EVENT DATE FILTERS>).order("event_date DESC")
-- EDIT --
This is quite dirty, but I cant think of any other direct db way of accomplishing this.
events = #user.holidays.map{|h| [h.name, h.date, :holiday]} + \
#user.friends.map{|f| [f.name, f.dob, :birthday]} + \
#user.anniversaries.map{|a| [a.spouse_name, a.anniversary.date, :anniversary]}
events.map!{|event| {:name => event[0], :date => event[1], :event_type => event[2]}}
# You now have an array of hashes with the events name, date and type.
events.sort{|a, b| a[:date] <=> b[:date]} # sort / filter

Resources