How to find instance of an object in a collection within a collection in rails
How to find instance of an object in a collection within a collection in rails
I'm trying to write an if statement that will find an instance of an object in a collection within another collection...
House
has_many :occupants
Occupant
has_many :shirts
belongs_to :house
Shirt
belongs_to :occupant
So if I want check if any of the occupants of house own a white shirt, I want to do something like this:
<% if @house.occuptants.shirts.where(:color => 'white') %>
However when I do that I get an error:
undefined method `shirts' for #< Occupant::ActiveRecord_Associations_CollectionProxy
I believe because occupants is a collection in this case, but I'm not sure what the correct approach or syntax should be.
2 Answers
2
Something simpler and that will help you later for different use cases is to add more to the relationships:
class House
has_many :occupants
has_many :shirts, through: :occupants
end
class Occupant
has_many :shirts
belongs_to :house
scope :females, -> { where(...) } # This is homework for you: http://guides.rubyonrails.org/active_record_querying.html#scopes
end
class Shirt
belongs_to :occupant
end
If you have a House
instance:then you can check for occupants with white shirts as below:
House
<% if @house.shirts.where(color: 'white').exists? %>
and to check for female occupants with white color shirts, do:
<% if @house.occupants.females.select { |o| o.shirts.where(color: 'white').exists? } %>
if
where(color: 'white')
.exists?
you are right, edited, thanks
– xploshioOn
Jun 29 at 23:51
This is helpful, but I've also got a similar scenario where I need to check just for females with white shirts, ie. I need to validate an attribute on occupant as well.... in this case would the decorator approach be necessary?
– phauwn
Jun 30 at 0:20
@phauwn i have updated the answer to include your further query. For next times, please try to include all your queries in the question itself. This answer was perfect in itself.
– Jagdeep Singh
Jun 30 at 7:08
Awesome @Jagdeep, don't worry, we are all here to help.
– xploshioOn
Jun 30 at 14:07
I would go for a presenter/decorator in this scenario.
Use draper to decorate the house object like:
# /app/decorators/house_decorator.rb
class HouseDecorator < Draper::Decorator
def count_occupants_with_white_shirts
object.occupants.joins(:shirts).where(shirts: { color: 'white' } ).count
end
end
Then in your view:
<% if @house.count_occupants_with_white_shirts > 0 %>
Hope that helps.
NOTE: If you don't want an additional dependency(Draper) you can also put that method inside you House
model
House
This is not exactly decorating a model. It is more like adding a helper method to avoid calculations in the view. Should not go into the decorator. A good example of a decorator method is where you want to display a
Time
/Date
object in pretty format.– Jagdeep Singh
Jun 30 at 6:59
Time
Date
Also, you will have to call the method on
@house.decorate
.– Jagdeep Singh
Jun 30 at 6:59
@house.decorate
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
The condition of the
if
will always be true.where(color: 'white')
cannot return a falsy value, it will always return an active record result wrapping 0 or more records. You need to use.exists?
to return a boolean true/false.– meagar♦
Jun 29 at 23:49