Eager-loading with many-to-many relationship - ruby-on-rails

I have 3 models:
class Thing < ActiveRecord::Base
has_many :products
has_many :shops, through: :products
end
class Product < ActiveRecord::Base
belongs_to :thing
belongs_to :shop
end
class Shop < ActiveRecord::Base
has_many :products
has_many :things, through: :products
end
Shop sales many things. Every shop has its own page with the list of its things. Products table has shop_id, thing_id, things quantity and thing price.
Here is controller:
def show
#shop = Shop.find(params[:id])
end
And view:
<% #shop.things.each do |thing| %>
<%= link_to thing.name, shop_thing_path(id: thing.id, shop_id: #shop.id) %><br>
<%= thing.products.find_by(shop_id: #shop.id).price %><br>
<%= thing.products.find_by(shop_id: #shop.id).quantity %>
<% end %>
I can't understand how to eager load this right. Now i get N * 2 queries (N = things count):
SELECT "products".* FROM "products" WHERE "products"."thing_id" = ? AND "products"."shop_id" = 1 LIMIT 1

def show
#shop = Shop.includes(:things).find(params[:id])
end
Here is the documentation related to eager loading.
http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

I tried to go from another point and use #products instead of #shop.things:
Controller:
def show
#shop = Shop.find(params[:id])
#products = #shop.products.includes(:thing).joins(:thing)
end
View
<% #products.each do |product| %>
<tr>
<td><%= link_to product.thing.name, shop_thing_path(id: product.thing_id, shop_id: product.shop_id) %></td>
<td><%= product.price %></td>
<td><%= product.quantity %></td>
</tr>
<% end %>
Now that works. But i still can't understand why
def show
#shop = Shop.includes(:things).find(params[:id])
end
doesn't work.

Related

Rails 5 many to many index

I am having a problem in the eyes when presenting the data.
I have 3 user models, sponsors and pets Result that sponsors in a table join between users and pets that are nan, my problem is in the hour to show all the sponsors of the mascot in sight and achievement but in a wrong way to La time to msotrar the data. I hope you can tell me how to fix it. Thank you.
Index.html.erb
<h1>Sponsors#index</h1>
<table class="table table-bordered table-hover table-striped" id="histories">
<thead>
<tr>
<th>Mascota</th>
<th>Padrinos</th>
<th>Apadrinar</th>
</tr>
</thead>
<tbody>
<% #pets.each do |pet| %>
<tr>
<td><%= pet.name %></td>
<td>
<% #users.each do |user| %>
<% #sponsors.each do |sponsor| %>
<% if user.id == sponsor.user_id and pet.id == sponsor.pet_id %>
<%= user.email%>
<% else %>
<p>No Tengo Padinos =-( </p>
<% end %>
<% end %>
<% end %>
</td>
<td><button>Apadrinar</button></td>
</tr>
<% end %>
</tbody>
</table>
My controller has the three models that I am sending to view.
def index
#sponsors = Sponsor.all
#users = User.all
#pets = Pet.all
end
pet.rb
class Pet < ApplicationRecord
has_many :adoptions
belongs_to :race, required: false
has_many :users, through: :sponsors
end
user.rb
class User < ApplicationRecord
has_many :pets, through: :sponsors
end
sponsor.rb
class Sponsor < ApplicationRecord
belongs_to :user
belongs_to :pet
end
The output that now shows me the attachment in the image.enter image description here
I also wonder if there is a better way to make the query, to show the respective data.
Another thing is how would I do to no longer be able to sponsor pets that I have already given a sponsor?
You are using a through table to join users and pets, but your associations aren't setup properly. For a through model, you have to have_many of the through table along with a has_many: :through association. Your associations should look like this:
class Pet < ApplicationRecord
has_many :adoptions
belongs_to :race, required: false
**has_many :sponsors**
has_many :users, through: :sponsors
end
class User < ApplicationRecord
**has_many :sponsors**
has_many :pets, through: :sponsors
end
Afther working and following diferent recomendations here is my solution.
<% #pets.each do |pet| %>
<tr>
<td> <%= pet.name %></td>
<td>
<% if pet.sponsors.any? %>
<% pet.sponsors.each do |sponsor| %>
| <%= sponsor.user_email %> |
<% end %>
<% else %>
<p>No Tengo Padinos =-( </p>
<% end %>
</td>
<td>
<%= link_to "Apadrinar", {:controller => "sponsors", :action => "new", :mascot => pet.id }%>
</td>
</tr>
<% end %>
I changed also my models.
sponsor.rb
class Sponsor < ApplicationRecord
belongs_to :user
belongs_to :pet
has_many :gifts
delegate :email, to: :user, prefix: true, allow_nil: true
end
user.rb
class User < ApplicationRecord
has_many :sponsors
has_many :pets, through: :sponsors
end
pet.rb
class Pet < ApplicationRecord
has_many :sponsors
has_many :users, through: :sponsors
end
And finally mi index on Controller.
sponsors_controller.rb
def index
# #sponsors = Sponsor.all
# #users = User.all
# #pets = Pet.all
# #pets_no_sponsors = Pet.where.not(id: Sponsor.select("pet_id")).select("id")
#pets = Pet.includes(:sponsors)
end
By making a delegation now I only make a query which sends everything necessary to my index.

Calling attributes of an associated model in the view

I have three relevant models: Vendor, Item, InventoryItem. I'm having difficulty understanding how to tap into associations to return associated attributes.
class Item < ActiveRecord::Base
has_many :inventory_items
has_many :vendors, through: :inventory_items
accepts_nested_attributes_for :inventory_items, :vendors
class InventoryItem < ActiveRecord::Base
belongs_to :item
belongs_to :vendor
class Vendor < ActiveRecord::Base
has_many :inventory_items
has_many :items, through: :inventory_items
I'm trying to return the vendors who sell an item, and the price they sell it for. Here's my SearchResults index view:
<table>
<tr class="search-table">
<td>Product</td>
<td>Details</td>
<td>Brand</td>
<td>Code</td>
<td>Vendors</td>
<td>Price</td>
</tr>
<% #items.each do |item| %>
<tr class="search-table">
<td><%= item.product %></td>
<td><%= item.details %></td>
<td><%= item.brand %></td>
<td><%= item.code %></td>
<td><%= #how to return vendors? %></td>
<td><%= #how to return price? %></td>
</tr>
<% end %>
</table>
Here is my SearchResultsController:
class SearchResultsController < ApplicationController
def index
#search = Item.solr_search do
fulltext params[:search]
end
#items = #search.results
end
end
I'm newish at RoR so any input is welcome. Thanks in advance!
EDIT
Here is what is returned from rails console when given Item.first.vendors
Item Load (0.7ms) SELECT "items".* FROM "items" LIMIT 1
Vendor Load (0.9ms) SELECT "vendors".* FROM "vendors" INNER JOIN "inventory_items" ON "vendors"."id" = "inventory_items"."vendor_id" WHERE "inventory_items"."item_id" = 1
=> []
SOLUTION EDIT
I had some fundamental errors in my model associations that wouldn't allow me to utilize those relationships. I cleaned up those associations by getting rid of duplicate fields (in this case :item_id and :product_code) and the answer below worked perfectly.
In order to list vendors of a specific item, just replace :
<td><%= #how to return vendors? %></td>
With :
<% item.vendors.each do |vendor| %>
<%= vendor.name %><br/>
<% end %>

Rails: Polymorphic Associations: How to list associations

I have a polymorphic assocation:
class User < ActiveRecord::Base
belongs_to :companiable, :polymorphic => true
end
class Agency < ActiveRecord::Base
has_many :users, :as => :companiable
end
class Publisher < ActiveRecord::Base
has_many :users, :as => :companiable
end
and now I want to list all users and show the company they belong to. Is there a more elegant solution than this (strongly hope there is)?
def index
#publishers = Publisher.all
#agencies = Agency.all
#users = User.all
end
...
<td><% unless user.companiable_id.nil? %>
<% if user.companiable_type == "Agency" %>
<%= #agencies[user.companiable_id].name %>
<% elsif user.companiable_type == "Publisher"%>
<%= #publishers[user.companiable_id].name %>
<% end %>
<% end %>
</td>
You can acces the company from user, since the belongs_to adds a method so that you can access the other object directly by doing user.companiable
def index
#users = User.all
end
and in your view
<td><% unless user.companiable.nil? %>
<%= user.companiable.name %>
<% end %></td>

Ruby on Rails, How to add projects to users/show.html.erb with has_many names :through blueprints

For each project that a user has blueprints for, I want to show the projects name and link to the project_path. Thanks.
These are my ActiveRecords
class User < ActiveRecord::Base
attr_accessible :id, :name
has_many :blueprints
has_many :projects, :through => :blueprints
end
class Project < ActiveRecord::Base
attr_accessible :id, :name
has_many :blueprints
has_many :users, :through => :blueprints
end
class Blueprint < ActiveRecord::Base
attr_accessible :id, :name, :project_id, :user_id
belongs_to :user
belongs_to :project
end
My Users show Controller
def show
#user = User.find(params[:id])
#project = Project.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
My Views/Users/Show.html.erb Table
<table>
<tr>
<% #projects.each do |p| %>
<td><%= p.name %></td>
<% end %>
</tr>
</table>
For each project that a user has blueprints for, I want to show the projects name and link to the project_path.
For these requirements, you can remove the #project assignment from your action. You need to look at the blueprints relation for #user, and get the associated project for each of those blueprints.
<table>
<tbody>
<% #user.blueprints.each do |blueprint| %>
<tr>
<td><%= link_to blueprint.project.name, project_path(blueprint.project) %></td>
</tr>
<% end %>
</tbody>
</table>

form_for for relation table with type of many to many relation

My goal is to display select box for each relation for users and specific project.
All users need to be listed but only project users have some type of relation. Other users have none selected in theirs select box.
I have this model:
class Project < ActiveRecord::Base
belongs_to :company
has_many :tasks, :order => 'state_type_id ASC'
has_many :project_user_relations
has_many :users, :through => :project_user_relations
def t_name
name.camelcase
end
end
class User < ActiveRecord::Base
belongs_to :company
has_many :tasks , :foreign_key => :assigned_user_id
has_many :project_user_relations
has_many :projects, :through => :project_user_relations
def full_name
firstname + ' ' + lastname
end
def relation_to(project)
relation=ProjectUserRelation.find_by_project_id_and_user_id(project.id, id)
relation ||= relation=ProjectUserRelation.new
end
end
class ProjectUserRelation < ActiveRecord::Base
belongs_to :project
belongs_to :user
has_one :project_user_relation_type
end
class ProjectUserRelationType < ActiveRecord::Base
def t_name
I18n.t("app.projects.users.relation.type."+code)
end
end
I want make a form to display all users, with collection_select.
I used code:
def edit_all
#project = Project.find(params[:project_id])
#users = User.all
....
in my controler
routes works ok.
in my view:
<% #users.each do |user| %>
<%= f.fields_for :users, user do |user_fields| %>
<tr class="reference" rel="<%= parent_user_path(user) %>" >
<td class="name"><%= link_to user.full_name, parent_user_path(user) %></td>
<td class="email"><%= mail_to user.email %></td>
<td class="type">
<%= user_fields.fields_for user.relation_to #project do |relation_fields| %>
<%= relation_fields.collection_select :project_user_relation_type, ProjectUserRelationType.all, :id, :t_name, {:include_blank => false, :prompt => t("helpers.select.prompt") } %>
<% end %>
</td>
</tr>
<% end %>
<% end %>
or for test:
<%= f.fields_for :users, #users do |xuser_fields| %>
<% logger.debug "#{self.to_s} xuser_fields = #{xuser_fields.object.inspect} ;" %>
<tr>
<td><%= xuser_fields.text_field :firstname %></td>
<td></td>
<td></td>
<td></td>
</tr>
<% end %>
but notnihng woks right
first one generates wrong name in html:
select id="project_users_project_user_relation_project_user_relation_type" name="project[users][project_user_relation][project_user_relation_type]"
second one generates error:
undefined method `firstname' for # Array:0x4d03658
Can you help me to solve this situation.
PS:sorry for long code :(
SOLUTION (probably - solved by reading RoR sources)
I found sollution i thing.
A method
def name_attributes=(attributes)
# Process the attributes hash
end
in Project model was missing.
It is unbelievable sollution :].
There is also exact syntax after fields_for: :name, #some_collection, where name must be exactly same name as in the beginign of mentioned def in Model.

Resources