Say I have the following models with associations:
House belongs_to User
User has_many House
Is there a way that I can do something like using SQL (not ruby group_by)
House.group(:user).each |houses_grouped_by_user|
#Each of these objects would be a set of houses with the same user id
end
Take a look at the docs for ActiveRecord associations.
As your commenter suggested, based on the associations in your model setup, you should lean on the functionality that the associations provide you!
Thus:
user.houses.each do |user_houses|
# do whatever you want to the user_houses
end
If you want to iterate through the houses in larger groups, you can use other iterative methods -- but start with the association and go from there.
Related
Here are my simplified models
class Offer < ApplicationRecord
has_many :rooms
end
class Room < ApplicationRecord
belongs_to :offer
end
class Kitchen < Room
end
I'm using STI for Kitchen, cause it seemed the right way to express what I wanted to do (I'm probably wrong).
I want to be able to create an Offer with Rooms in it. I have 'regular' rooms that are instances of Room directly, and more specified rooms such as a Kitchen which can have an extra attribute.
I'm using cocoon to create nested form, it works great to create an offer and add regular rooms. But how can I add kitchens ?
Maybe it's an architecture issue more than an implementation issue. How would you manage to do something like this ?
One solution would be to add JSONB column data to your rooms table. Then you can use jsonb_accessor gem to elegantly manage STI.
I assume you have type column already so you can do something like:
Kitchen.create(offer_id: 1, name: "Great Kitchen", description: "It is great!")
Notice: description would be attribute of JSONB data column. In addition you can create indices for JSONB attributes and even do queries. From my experience, you want to keep JSONB as place to store additional info and less for heavy queries.
I hope this helps.
The question below had a good answer to grab associated values of an activerecord collection in one hit using Comment.includes(:user). What about when you have multiple associations that you want to grab in one go?
Rails have activerecord grab all needed associations in one go?
Is the best way to just chain these together like below Customer.includes(:user).includes(:sales).includes(:prices) or is there a cleaner way.
Furthermore, when I am doing this on a loop on an index table. Can I add a method on the customer.rb model so that I can call #customers.table_includes etc and have
def table_includes
self.includes(:user).includes(:sales).includes(:prices)
end
For the record I tested the above and it didn't work because its a method on a collection (yet to figure out how to do this).
In answering this, I'm assuming that user, sales, and prices are all associations off of Customer.
Instead of chaining, you can do something like this:
Customer.includes(:user, :sales, :prices)
In terms of creating an abstraction for this, you do have a couple options.
First, you could create a scope:
class Customer < ActiveRecord::Base
scope :table_includes, -> { includes(:user, :sales, :prices) }
end
Or if you want for it to be a method, you should consider making it a class-level method instead of an instance-level one:
def self.table_includes
self.includes(:user, :sales, :prices)
end
I would consider the purpose of creating this abstraction though. A very generic name like table_includes will likely not be very friendly over the long term.
I have groups (Group model) in my app, which represent groups of people.
I want each group to have its own forum.
Should I just have the forum id in the groups table? It doesn't feel right. If I did it myself, the forum would have a polymorphic association to a "forumable" element (groups in this case, but I have other models that would need a forum).
Any opinions on what I should do? Modify the gem to fit my needs, or just have the forum_id in my models that need a forum? Or another solution maybe?
I'm the guy who started Forem (its the volunteers who did most of the hard work, though!), I think I can answer this question.
If you want only certain groups to have access to one and only one forum then you can put the forum_id field on the groups table and do it that way. What you can do then is override the can_read_forem_forum? method in your User model to perform a permission check for that user:
def can_read_forem_forum?(forum)
groups.where(:forum_id => forum.id).any?
end
This is used in Forem's ability model to determine whether or not a person can access a forum. What this method is going to do is that it will only return groups for that user that have link that specific forum. If there are any, then it's known that the user can access that forum.
Now if you're going the other route where a group may have access to many forums, well then you'd define a joins table between groups and forem_forums (called forum_groups) and define it as an association in your Group model like this:
has_many :forum_groups
has_many :forums, :through => :forum_groups, :class_name => "Forem::Forum"
You would need to also define a new model inside your application for this forum_groups association, it would be called ForumGroup and go a little like this:
class ForumGroup < ActiveRecord::Base
belongs_to :forum, :class_name => "Forem::Forum"
belongs_to :group
end
We're doing it this way so you have an easy way to manage the associations between forums and groups. If you did has_and_belongs_to_many, it generally only provides a gigantic pain in the ass when you want to delete one specific record from that join table.
Now, with that all nicely set up, the method you want to define in your User model is this one:
def can_read_forem_forum?(forum)
groups.joins(:forums).where("forem_forums.id = ?", forum.id).any?
end
Same thing, except this time we find all the groups that are linked to a specific forum through that association we set up earlier. This will do an INNER JOIN on the forum_groups table, and then another on the forem_forums table, getting the data required.
I hope this helps you, and thanks for using Forem!
I've got an association between models that is polymorphic.
Example:
class Review
belongs_to :review_subject, :polymorphic => true
end
However, I would like to make a call on this :review_subject association where I know all the results will be of a certain type.
In my case, I want to do this so I can join in the review_subject and then impose a condition upon it. Doing so on a polymorphic relation normally causes this to raise an EagerLoadPolymorphicError. The logic behind this is that it's not able to know what model to load in order to perform the join, but that does not apply in my case because I already know only one model will be involved in this query.
Example, where I know that all relevant reviews will belong_to a Book, and I want to only show reviews where the books have authors:
Review.joins(:review_subject)
.where(review_subject_type => "Book")
.where("reviewed.book_author IS NOT NULL")
Is there a way to temporarily disable the polymorphic relationship?
The best solution I've come up with is to add a second associationin the Review model, belongs_to :review_subject_books_only that is not polymorphic, and can be called on only in this situation. However, this is an ugly hack both in the model, and in that it also messes up include calls unless the views also refer to a Review's review_subject_books_only.
Do the query the other way around:
Book.joins(:reviews).where('book_author is not null')
In Rails, I want to override the behavior of an association. For example, by default, if Person has_many :hats, calling some_person.hats would do a simple join using person.id and hat.person_id.
I want to modify that query to include some other criteria. For example, maybe a person's collection of hats should be just the hats that are appropriate to their country.
It seems that I could do something like this:
class Person < ActiveRecord::Base
has_many :hats, :through => :country do
# John lives in Canada, so he gets a baseball cap and a hockey helmet
self.country.hats
end
end
Can I control what an association returns like this? If not, would a scope be the best solution?
I know this is a silly example, but explaining the domain logic that I need this for would be way too boring for everyone here. :)
Scopes are probably your best option because they're chainable and reusable outside your association. Otherwise, you could use association extensions. Check out this thread for more info. Association Extensions