Rails 5 has_many_through include pivot table in result - ruby-on-rails

I have a Rails 5 app with restaurants and products. One product has_many restaurant and one restaurant has many products. I created a pivot table and created and has_many_through relation since I want to work with the pivot table.
Product:
has_many :restaurant_products, dependent: :destroy
has_many :restaurants, through: :restaurant_products
Restaurant:
has_many :restaurant_products, dependent: :destroy
has_many :products, through: :restaurant_products
Since every restaurant can modify the price for each product I have added a custom_price column to my restaurant_products table.
Now I can get all products for a certain restaurant:
#restaurant.products
But this way, when I list the products, I don't have access to the custom price. How can I somehow include or access the correct pivot record to get the custom price?

You can use the following:
#restaurant.restaurant_products.joins(:products).select('restaurant_products.custom_price, products.*')
That will give you access to instances including all of a product's columns, as well as the join table's custom price.
The SQL generated will look similar to:
SELECT restaurant_products.custom_price, products.*
FROM `restaurant_products`
INNER JOIN `products` ON `products`.`id` = `restaurant_products`.`product_id`
WHERE `restaurant_products`.`restaurant_id` = 1
Quick tip: what's returned might look a little strange, something like:
[#<RestaurantProduct id: 1, custom_price: 10>]
...though if you call attributes, or a product attribute on an instance here, you'll have access to everything you want.
The above gives quick and efficient access to the records' data, though if you need to access methods on the product itself, you might prefer the following:
#product_details = #restaurant.restaurant_products.includes(:products)
And then to loop through the data:
#product_details.each do |product_detail|
product_detail.custom_price
product_detail.product.column_data
product_detail.product.a_method
end
Hope these help - let me know how you get on or if you have any questions.

Just provide a custom select that includes the row from the join table:
#products = #restaurant.products
.select('products.*, restaurant_products.custom_price')
This returns restaurant_products.custom_price as if it where a column on products. You can us AS to provide a alias if you want to call it something else than custom_price.
So you can do:
<table>
# ...
<% #products.each do |product| %>
<tr>
<td><%= product.name %></td>
<td><%= product.custom_price %></td>
</tr>
<% end %>
</table>

In such cases, it always better to query on pivot table as bellow
#restaurant_products = RestaurantProduct.includes(:product)
.where(restaurant_id: #restaurant.id)
You can display it in the view simply as follows.
<table>
<% #restaurant_products.each do |resta_product| %>
<tr>
<td><%= resta_product.product.name %></td>
<td><%= resta_product.custom_price %></td>
</tr>
<% end %>
</table>
You can even delegate all product methods to work directly on restaurant_product.

Related

Rails has_and_belongs_to_many on 2 level association

This is related to this previously asked question
I have a has_and_belongs_to_many in place between Product and Supplier.
In my view I use:
<td><%= product.suppliers.map {|supplier| supplier.NAME }.join(', ') %></td>
To show list of suppliers comma separated on each row for each product in my table.
I now need to show the same list on invoices index view. Invoices table has a column PRODUCT. I have already set belongs_to :product on Invoice model.
I tried in my invoices index view:
<td><%= invoice.product.suppliers.map {|supplier| product.supplier.NAME }.join(', ') %></td>
but it returns
error undefined local variable or method `product'
Why isn't that working? How can I fix it? Thanks in advance.
you build wrong .map, try
invoice.product.suppliers.pluck(:NAME).join(', ')
BTW
it's bad practice use logic in view, you should move your logic to models, and in view use something like:
<%= invoice.suppliers_names %>
what should return # => 'Name_1, Name_2, etc'

Rails table display and database design?

I have two tables, Beacon belongs to Category and Category has many beacons, in beacon table, I add a foreign key: category_id, but when i want display in table, i need the category_name in the category table, now I use a stupid way as following:
...
<% #beacons.each do |beacon| %>
<tr>
<td><%= beacon.beacon_uuid %></td>
<% #category_name = Category.find(beacon.category_id).category_name %>
<td><%= #category_name %></td>
...
but when beacon data get bigger, request get bigger, how to change my code to defence this stupid question?can someone give me some suggestion or reference? Thanks a lot.
It sounds like you're referring to an N+1 query, wherein the number of queries executed is equal to the number of beacons + 1. If so, your queries look similar those below.
select beacons.* from beacons;
select categories.* from categories where categories.id = 1;
select categories.* from categories where categories.id = 2;
select categories.* from categories where categories.id = 3;
Assuming your beacon model has the belongs_to :category relation declared, this can be avoided by using includes to eager load the association in the controller
def index
#beacons = Beacon.all.includes(:category)
end
The view can then be changed to take advantage of this, instead of pulling a fresh copy of the category from the database for each beacon
<%= beacon.category.category_name %>
There is a helper of the category and its attributes in the beacon itself.
In Rails, you can go both ways:
Category.beacons will return an array of all the beacons the category has and their attributes
Beacon.category will return the attributes of its category.
Following the same logic, you just need this:
<%= beacon.category.category_name %>

List data from two models

I have two models, Items and Calibrations. Items has many calibrations, meaning that every year the instruments have to be calibrated. Fields, date_calibration and date_expired, are located in the "calibrations" table. (Items: has_many :calibrations, calibration: belongs_to item)
I need list/show all the items that are expiring. I can list all the items without problem of course but, I don't know how to add date_expired to the list.
In the Items controller:
#items = Item.all.order("created_at DESC")
In the Index:
<% #items.each do |item| %>
<tr>
<td><%= item.cod %></td>
<td><%= item.number %></td>
<td><%= item.den_cont %></td>
<td><%= item.branch %></td>
<td><%= item.model %></td>
</tr>
<% end %>
I'm using Aptana and PostgreSQL version 9.1, Ruby 2.1 and Rails 4.1.
Can anyone of you suggest any solution or point me to the right direction?
UPDATE
What should I change to show the item using the sentence below..
Item_controller
Item.includes(:calibrations).where('calibrations.date_expired <= ?' , 2014/07/12)
Index
<% #items.each do |item| %>
Return undefined method each.
ALSO
Any idea on how to show a traffic light depending on how many days left to calibration_date ?? Tks again!
As long you have your relations properly defined in your models I do believe something similar to the following should do the trick!
Item.joins(:calibrations).where(date_expired <whatever condition>)
The equivalent SQL being:
SELECT Items.*
FROM Items
LEFT OUTER JOIN Calibrations
ON Calibrations.item_id = Items.item_id
WHERE date_expired <whatever condition>
With the equivalent SQL of the above statement being (using the includes method):
SELECT *
FROM Items
LEFT OUTER JOIN Calibrations
ON Calibrations.item_id = Items.item_id
WHERE date_expired <whatever condition>
Hope this helps!
Also, if you're not wanting to return any data related to the calibrations table (which it looks like this is the case), I would go with the joins method, however if you are, the includes method would be the way to go (http://tomdallimore.com/blog/includes-vs-joins-in-rails-when-and-where/)
Also, the following may be of interest: http://guides.rubyonrails.org/active_record_querying.html (particularly section 12)!
#items = Item.includes(:calibrations)
.where('calibrations.date_expired <= ?', some_date)
include will join the two tables and allow you to specify conditions on items based on columns from the calibrations table. I think that's what you wanted isn't it?

Replacing foreign keys with data from table in ruby on rails

I'm brand new to ROR and very new to programming.
I'm working on a DB and I want entering information to be easy and user friendly. In my index pages for my tables any foreign keys are shown as the id. I would like to learn how to have it display a different column value instead of the id. For example company_name instead of company_id.
From my very little experience I would guess that the .map method would be used. I'm not really sure how though. I've already messed around with it for a while with no success.
The lower half of one of my table's index.html looks like this:
<% #venture_rounds.each do |venture_round| %>
<tr>
<td><%= venture_round.raise_amount %></td>
<td><%= venture_round.company_id %></td>
<td><%= venture_round.investor_id %></td>
</tr>
What can I do to have it grab a value from the company and investor table and show it, instead of the id for those tables?
I hope this question makes sense.
Make sure your VentureRound model has company and investor as defined children
class VentureRound < ActiveRecord::Base
belongs_to :company, :investor
end
Read the information as such
venture_round.company.location # or whatever attributes you're seeking
venture_round.investor.name

Drawing information from relational databases in Rails

I am trying to pull the name of the Artist from the Albums database.
These are my two models
class Album < ActiveRecord::Base
belongs_to :artist
validates_presence_of :title
validates_length_of :title, :minimum => 5
end
class Artist < ActiveRecord::Base
has_many :albums
end
And here is the Albums Controller
def index
# albums = Album.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #albums }
end
end
And the View from the index:
<% #albums.each do |album| %>
<tr>
<td><%=h album.id %></td>
<td><%=h album.title %></td>
<td><%=h album.artist.name %></td>
</tr
<% end %>
My end result html is coming out like this for the artist field!
#<Artist:0x000001022e4868>
and if I set it to artist.name
I get this:
undefined method `name' for nil:NilClass
What am I doing wrong?
You need to do something like:
<%=h album.artist.name %>
The way you used it you are displaying whole object, so to speak.
Sounds like you have an Album without an Artist (either artist_id is null or set to an artist_id that no longer exist).
You can try:
<%= h album.artist ? album.artist.name : 'N/A' %>
Another way to write what was enumerated earlier.
<%= h album.artist.name unless album.artist.blank? %>
I would recommend going into script/console and manually stepping through the process of pulling all your articles and then printing out all the artist names.
BTW, if you're running this code in production you should probably use eager loading
# albums = Album.find(:all, :includes => [:artist])
This will be much more efficient.
Do you actually have data correctly set up in your database tables? If your Artist and Album models are being saved correctly then it should look something like this:
artists table
id|name
---------------------
1|The Beatles
2|The Rolling Stones
albums table
id|artist_id|title
--------------------------------------------------
1|1 |The White Album
2|2 |Exile on Main Street
3|1 |Sgt. Pepper's Lonely Hearts Club Band
Something is going on the saving of your artists->album relationship. If you're getting that error you're getting a nil artist back which means the key isn't being saved in the table. Check your table manually and make sure the relation is there if it's not then you're looking in the wrong place, you should be fixing your save.

Resources