Ruby - access parent property from child - one to many relation - ruby-on-rails

class PriceList < ActiveRecord::Base
has_many :prices, :dependent => :destroy
end
and Price:
class Price < ActiveRecord::Base
belongs_to :price_list
belongs_to :material
belongs_to :unit
end
Now in price_list index I want to show Price list name instead of id:
<tbody>
<% #prices.each do |price| %>
<tr>
<td><%= price.price_list.price_list_short_name %></td>
<td><%= price.materials_id %></td>
<td><%= price.units_id %></td>
<td><%= link_to 'Show', price %></td>
<td><%= link_to 'Edit', edit_price_path(price) %></td>
<td><%= link_to 'Destroy', price, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
What I am doing wrong that price.price_list.price_list_short_name does not work?

At least one of your Price records doesn't have its price_list associated. The solution depends on your intentions. If you want to force all prices to have price_list, you can add validation:
class Price < ActiveRecord::Base
validates :price_list, presence: true
# ...
end
If you want to allow price_list-less prices, you could make use of try method, which will fix the error in view:
price.price_list.try(:price_list_short_name)
BTW, naming your column price_list_short_name in price_lists table is a bit redundant.

I was missing proper references.
Adding to migration file:
class AddReferencesToPrices < ActiveRecord::Migration
def change
remove_column :prices, :price_list_id
remove_column :prices, :materials_id
remove_column :prices, :units_id
add_reference :prices, :price_list, :index => true
add_reference :prices, :material, :index => true
add_reference :prices, :unit, :index => true
end
end
solved problem.

Related

Sort order for ActiveRecord::Associations::CollectionProxy in table rails

I want to set up sort order for active record collection proxy in table.
It should be sorted by number of available rooms (from highest to lowest).
The trick is that #rooms.reserved is a boolean and to calculate quantity of free/reserved rooms I have to use helper method to avoid record collection proxy errors. I get proper results, but I need to sort table by number of available rooms.
I have two models: Room and Hotel.
class Room < ApplicationRecord
belongs_to :hotel, optional: true # avoiding rails 5.2 belongs_to error
accepts_nested_attributes_for :hotel
end
and
class Hotel < ApplicationRecord
has_many :rooms, dependent: :destroy
accepts_nested_attributes_for :rooms
end
I have table:
<table>
<tr>
<th>Name</th>
<th>Rooms count</th>
<th>Rooms status: in reserve || free</th>
</tr>
<% #hotels.each do |hotel| %>
<tr>
<td><%= hotel.name %></td>
<td><%= hotel.rooms_count %></td>
<td><%= rooms_reservation_status(hotel.rooms) %></td> <!-- rooms_reservation_status helper method in application_helper.rb -->
<td ><%= link_to 'Show', hotel_path(hotel) %></td>
<td><%= link_to 'Destroy', hotel, method: :delete, data: { confirm: 'Are you sure?' } %>
</tr>
<% end %>
</table>
Helper method
# rooms_reservation_status iterates throught ActiveRecord::Associations::CollectionProxy
# and calculates the sum of free rooms aswell as a sum of reserved rooms
def rooms_reservation_status(rooms)
reserved = 0
free = 0
rooms.each do |r|
r.reserved == true ? reserved+=1 : free+=1
end
"#{reserved} || #{free}"
end
Active Record table for rooms:
class CreateRooms < ActiveRecord::Migration[5.1]
def change
create_table :rooms do |t|
t.boolean :reserved, :default => false
t.belongs_to :hotel, index: true
t.timestamps
end
end
end
I would add a class method on the Room model in order to return for a given collection the number of free rooms and reserved rooms:
class Room < ApplicationRecord
belongs_to :hotel, optional: true
accepts_nested_attributes_for :hotel
def self.reserved_count
where(reserved: true).count
end
def self.free_count
where(reserved: false).count
end
end
Once you have implemented, you can call it from the relationship declared in Hotel model:
class Hotel < ApplicationRecord
has_many :rooms, dependent: :destroy
accepts_nested_attributes_for :rooms
def reserved_rooms
rooms.reserved_count
end
def free_rooms
rooms.free_count
end
end
Your view will look finally like this:
<table>
<tr>
<th>Name</th>
<th>Rooms count</th>
<th>Rooms status: in reserved || free</th>
</tr>
<% #hotels.each do |hotel| %>
<tr>
<td><%= hotel.name %></td>
<td><%= hotel.rooms_count %></td>
<td><%= "#{hotel.reserved_rooms} || #{hotel.free_rooms}" %></td>
<td ><%= link_to 'Show', hotel_path(hotel) %></td>
<td><%= link_to 'Destroy', hotel, method: :delete, data: { confirm: 'Are you sure?' } %>
</tr>
<% end %>
</table>
Sorting the Hotels in your controller
In your controller make sure that you eager load Rooms for Hotel:
#hotels = Hotel.includes(:rooms).sort_by { |h| h.free_rooms.to_i }.reverse
You could eventually implement it as Hotel.includes(:rooms).sort_by(&:free_rooms).reverse.
In this way you won't need any join or helper.
Regarding your comment, free_rooms is implemented as an instance method (e.g. Hotel.first.free_rooms), so it will not be available for an ActiveRecord_Relation (e.g. Hotel.all.free_rooms)

display "name" insted of "_id" in index view

I've got "demand", "shift" and "parent" (this is going to be a baby sitter thingy).
Now the models look like this:
class Demand < ActiveRecord::Base
belongs_to :parent
belongs_to :shift
end
&
class Parent < ActiveRecord::Base
has_many :demands, :dependent => :destroy
has_many :shifts, :through => :demands
accepts_nested_attributes_for :demands, allow_destroy: true
# Returns fullname of parent
def fullname
"#{firstname} #{name}"
end
end
&
class Shift < ActiveRecord::Base
has_many :supps, :dependent => :destroy
has_many :nanns, :through => :supps
has_many :demands, :dependent => :destroy
has_many :parents, :through => :demands
end
If I now want to display a shift's description (a param of the shift table) instead of its _id, I get the following error:
undefined method `description' for nil:NilClass
Here is some code from the corresponding demands index view:
<td><%= demand.parent.name %></td>
<td><%= demand.demand %></td>
<td><%= demand.shift.description %></td> <----THIS LINE PRODUCES THE ERROR
<td><%= link_to 'Show', demand %></td>
<td><%= link_to 'Edit', edit_demand_path(demand) %></td>
<td><%= link_to 'Destroy', demand, method: :delete, data: { confirm: 'Are you sure?' } %></td>
I think that I gave the models the correct has_many and belongs_to associations so I don't really find the mistake here. Thanks in advance for any help!
You have a demand that has no associated shift. If you want to identify which one in your table, replace...
<td><%= demand.shift.description %></td>
with
<td><%= demand.shift ? demand.shift.description : 'missing shift!' %></td>
The lines with missing shifts will now tell you that shift is missing.

When iterating through has_many, the result shows the whole array not only the requested column

I have this code:
class Property < ActiveRecord::Base
has_many :owners, dependent: :destroy
has_many :residents, through: :owners
end
class Resident < ActiveRecord::Base
has_many :owners, dependent: :destroy
has_many :properties, through: :owners
end
class Owner < ActiveRecord::Base
belongs_to :resident
belongs_to :property
end
and default generated controllers.
I searched for how to list data from has_many through relationships and it works for me, but don't know why it also shows the whole array.
Here is the view code:
<tbody>
<% #residents.each do |resident| %>
<tr>
<td><%= resident.name %></td>
<td><%= resident.birthdate %></td>
<td><%= resident.birthId %></td>
<td><%= resident.address %></td>
<td><%= resident.email %></td>
<td><%= resident.tel %></td>
<td><%= resident.properties.each do |property| %>
<%= property.name %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
and when I show the data from that iteration it shows the column, in this the "case name", and in [] the whole array like this:
Dostojevskeho 15
[#<Property id: 2, name: "Dostojevskeho 15", registerNumber: "9845", propertyList: 6977, propertyType: 8, created_at: "2016-01-09 20:20:04", updated_at: "2016-01-09 20:20:04">]
I can't find what am I doing wrong. When I compared it to all tutorials my view looks fine to me.
This is part of the residents controller with index and show:
class ResidentsController < ApplicationController
before_action :set_resident, only: [:show, :edit, :update, :destroy]
def index
#residents = Resident.all
end
def show
#residents = Resident.find(params[:id])
end
You need to change this line:
<%= resident.properties.each do |property| %>
to this:
<% resident.properties.each do |property| %>
Removing the = tells Rails that you only want to iterate over the resident.properties. Otherwise you get this weird behavior, where Rails is trying to print the iterator itself onto the page - which is rarely what you want.

Ruby on Rails Accessing attributes of record found using .last

In my asset index view, when it loops through each Asset, I want to show the most recent scene name. I am using .last to pull out the most recent record. When I .inspect what is returned, I can see the values. The problem is, when I try to access one of the attributes of what is returned I get an undefined method.
So for instance if I do this:
<%= (asset.scene_assignments.where(asset_id: asset).order("created_at").last).scene_id %>
I get:
NoMethodError in Assets#index undefined method `scene_id' for SceneAssignment:0x4bc2c28
But if I call #inspect instead of #name, I can see what is contained inside. So if I do this:
<%= (asset.scene_assignments.where(asset_id: asset).order("created_at").last).inspect %>
It prints this:
SceneAssignment id: 4, scene_id: 3, asset_id: 1, arrival_time: nil, created_at: "2014-10-16 01:43:50", updated_at: "2014-10-16 01:43:50", location_id: 1, asset_role_id: 1
Why can't I access one of the attributes from what is returned?
In my asset index view, I have this:
<% #assets.each do |asset| %>
<tr>
<td><%= asset.name %></td>
<td>
<%= (asset.scene_assignments.where(asset_id: asset).order("created_at").last).inspect %>
</td>
<td><%= link_to 'Show', asset %></td>
<td><%= link_to 'Edit', edit_asset_path(asset) %></td>
<td><%= link_to 'Destroy', asset, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
My relationship between an asset and scene is set up like this:
class SceneAssignment < ActiveRecord::Base
belongs_to :scene
belongs_to :asset
belongs_to :location
belongs_to :asset_role
belongs_to :incident
accepts_nested_attributes_for :asset
end
class Scene < ActiveRecord::Base
has_many :scene_assignments
has_many :assets, :through => :scene_assignments
belongs_to :incident
belongs_to :scene_type
accepts_nested_attributes_for :scene_assignments, :allow_destroy => true
end
class Asset < ActiveRecord::Base
has_many :scene_assignments
has_many :scenes, :through => :scene_assignments
end
I believe it is .scene_id, not .last that is causing your error. The result of `.where(…) is a collection of records, not a single record.
To fix that, you could say asset.scene_assignments.where(asset_id: asset).order("created_at").last).first.scene_id though that makes some fairly messy code just a little bit worse. :)

Accessing Attributes in a Many-to-Many

I have a rails app and I'd like to be able to do something like
task.labels.first.label_name to get the label name of a task. However, I get an undefined method label_name. I did a t = Task.first; t.labels.first.label_name in the console, and that worked so I'm not sure what's going on. Here's the models then the locations of the error:
class Categorization < ActiveRecord::Base
belongs_to :label
belongs_to :task
end
class Label < ActiveRecord::Base
attr_accessible :label_name
has_many :categorizations
has_many :tasks, :through => :categorizations
end
class Task < ActiveRecord::Base
attr_accessible :task
has_many :categorizations
has_many :labels, :through => :categorizations
end
The error is in the index
<% for task in #tasks %>
<tr>
<td><%= task.task %></td>
<td><%= task.labels.first.label_name %></td>
<td><%= link_to "Show", task %></td>
<td><%= link_to "Edit", edit_task_path(task) %></td>
<td><%= link_to "Destroy", task, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
My guess would be that one of the tasks in #tasks does not have any labels so when you call task.labels.first it returns nil and then you try to call label_name for nil which of course does not work.
The easiest solution would be to do a check like this:
<td><%= task.labels.first.label_name unless task.labels.first.nil? %></td>
Now that does not look so good in the view so you might want to place that check in your Task model instead, perhaps like this:
class Task < ActiveRecord::Base
attr_accessible :task
has_many :categorizations
has_many :labels, :through => :categorizations
def label_name
self.labels.first.label_name unless self.labels.first.nil?
end
end
And in the view:
<td><%= task.label_name %></td>
And another thing, just in case you would like to view all the associated labels, you could do something like this:
task.labels.map(&:label_name).join(", ")

Resources