Querying and linking to from join tables - ruby-on-rails

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 %>

Related

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 %>

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 %>

Rails nested_forms not displaying fields when dynamically adding extra objects

I'm currently using the nested_forms gem and I'm trying to be able to add multiple landlords to a property.
At the moment the associations are quite deep:
Property -> Landlord -> Contact_Detail -> Address
In my Property controller I'm building the associations and the initial form is displayed correctly. However, after using the add fields button, there are no fields. I know it is something to do with the object not getting built, but I can't understand why.
Here's my Property model:
belongs_to :address
belongs_to :estate_agent
belongs_to :property_style
has_and_belongs_to_many :landlord
has_and_belongs_to_many :tenancy_agreement
attr_accessible :landlord_attributes, :address_attributes, :estate_agent_attributes,
:property_style_attributes, :sector, :reference , :occupied, :available_date, :property_style_attributes,...
accepts_nested_attributes_for :landlord, :address, :estate_agent, :property_style, :tenancy_agreement
And here's the new function in the Property controller:
def new
#property = Property.new
#property.build_address
#property.landlord.build.build_contact_detail.build_address
#property.estate_agent_id = current_user.estate_agent_id
respond_to do |format|
format.html # new.html.erb
format.json { render json: #property }
end
end
I've had quite a few attempts at this, but can't see where I'm going wrong, is it a problem with the nested_form gem not supporting this many levels of association or the type of association?
Thanks!
EDIT
Changes made:
belongs_to :address
belongs_to :estate_agent
belongs_to :property_style
has_and_belongs_to_many :landlords
has_and_belongs_to_many :tenancy_agreements
attr_accessible :landlords_attributes, :address_attributes, :estate_agent_attributes,
:property_style_attributes, :sector, :reference , :occupied, :available_date, :property_style_attributes,...
accepts_nested_attributes_for :landlords, :address, :estate_agent, :property_style, :tenancy_agreements
Properties controller:
#property.landlords.build.build_contact_detail.build_address
Landlords model
has_and_belongs_to_many :properties
Here is my view:
<%= nested_form_for(#property) do |f| %>
<% if #property.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#property.errors.count, "error") %> prohibited this property from being saved:</h2>
<ul>
<% #property.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<h2>Landlords</h2>
<%= f.fields_for :landlords %>
<p><%= f.link_to_add "Add a Landlord", :landlords %></p>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Unless you've specified "landlord" as an irregular inflection, Rails will assume that it is singular. Many-to-many associations should be declared in the plural.
Try changing the many-to-many associations to:
has_and_belongs_to_many :landlords
has_and_belongs_to_many :tenancy_agreements
You'll also need to change all calls to these to be plural as well. In addition, you must change the accepts_nested_attributes_for to landlords, and the attr_accessible from landlord_attributes to landlords_attributes.
I attempted to use both awesome-nested-forms and cocoon and it still wouldn't work.
In the end, I found a workaround by building the object in the partial and not in the controller. Like this:
<% f.object.build_contact_detail.build_address %>
I hope this helps someone else!

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

Linking to Dynamic Content in 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 %>

Resources