Get children of three different parents in polymorphic association - ruby-on-rails

I have the model Post, that via polymorphism belongs to either Organization, Team or User.
For each parent I'm fetching the posts like this:
current_user.posts
current_user.organization.posts
current_user.team.posts
How would I merge these queries returning a single AR object with all posts?
I have tried the following:
Post.where(trackable: current_user).where(trackable: current_user.team) # returns 0 objects
current_user.posts + current_user.organization.posts # this returns an array

This should do the trick:
Post.where(trackable: [current_user, current_user.organization, current_user.team])

Related

How to access variables in model

I am trying to get access to a property contained inside my user object.
My user model has_many: posts. In the controller how would i gain access to these posts? Would i create a method in the model?
def posts
#posts = Post.find(User_id: params[:id])
end
or can i directly access the posts for the user. User.posts Since i am currently residing in the controller, is the controller aware of the currently selected model? Or do i have to pull the information again?
You can query the database for all the posts with a specific user_id, like this:
#posts = Post.where(user_id: params[:id])
Alternatively, you can find the user first and then fetch all posts associated with that user, like this:
user = User.find(params[:id])
#posts = user.posts
Assuming your id in params is the id of your user, you can use user = User.find(params[:id]) to get the user and #posts = user.posts to get all the posts of this user.
So, it is not about where you are, It is about what you are calling.
I'm sure you are familiar with relationships...
When you have relationships, it means that you can get to one relation from the other through whatever association exists between them.
If I am my father's son, then you can get me directly by checking my father's children. ( you don't necessarily have to get all children in the village first )
So, bringing all my story above together, with the association between your Post and User, you can always call user.posts (user being an instance of User) and post.user ( with post being an instance of Post)
The Ruby on Rails guides have a section on associations, which is what you want. It's here: http://guides.rubyonrails.org/association_basics.html
In a nutshell, because you have added an association in your user model to a number of post records, Rails will build a helper method in your user model called posts. You can use that to access all the posts associated with that user.
When you create a post, the post record needs to have a column called user_id. This will provide the 'physical' link between the user and post models. You can access the posts from a user like so:
user.posts each do |post|
# do something with post.content
end
To get posts that match some criteria in the posts collection you can query like this:
posts = user.posts.where(:something => 'matches criteria')
If you know there's only one post that matches the criteria, you can do this:
post = user.posts.where(:something => 'matches criteria').first
The post model also needs a belongs_to :user association. (The belongs_to will generate a helper method called user in the post model which you can then use to access the user record from the post.) For example:
user_email = post.user.email
The user record does not require a post_id column since Rails knows that user.post refers to the post table and automagically generates a query using user_id.
Anyway, the guide I linked to above will give you all the information you need and more too.

Ordering by a column in an associated model

I am trying to make an ActiveRecord query that will order the results by the value of one of the columns in an associated model:
I have a Chats model that has a one to many relationship with the messages model.
Chats has_many messages and Message belongs_to chat
And from my controller, I want to get a list of chats, ordered by the created_at of the associated message.first, eg:
#chats = current_user.chats.includes(:messages).order("messages.first.created_at").paginate(page: params[:page])
or something like that.
My question is that how can I achieve this kind of ordering from an associated model with this relationship? All contributions are appreciated.
Also add .references(:messages)
This will pull messages in as a join rather than a separate query.
You can define your order in your association. Try the following:
has_many messages, -> { order(:created_at => :asc) }
# change how you need
So when you call chat.messages it will return messages with the given order.
Thank you all, #Rubyrider and #Andrew. I have been able to order the columns as follow:
#chats = current_user.chats.includes(:messages).order("messages.created_at desc").paginate(page: params[:page])
without the inclusion of either .first or .last on messages. Surprisingly, I did not have to specify which of the messages of the chat is to be used for the ordering. I guess ActiveRecord automatically looks and just takes the latest of the associated model to use for the ordering.

Rails HABTM - Properly removing an association

I am developing a feature for creating specials, for a shopping website. One product can have more than one special, and obviously a special can have more than one product..
I am using a has_and_belongs_to_many relationship, so i have declared:
Product.rb
has_and_belongs_to_many :specials
Special.rb
has_and belongs_to_many :products
Now, with a product #product and a special #special, an association is created like so..
#special.products << #product
After doing this, the following is true:
#special.products.first == #product
and, importantly:
#product.specials.first == #special
When i delete the association using this
#special.products.delete(#product)
then #product is removed from specials, so #special.products.first==nil, however #product still contains #special, in other words #products.specials.first==#special
Is there any proper way, apart from writing a delete method, to do this in a single call?
According to the Rails documentation:
collection.delete(object, …)
Removes one or more objects from the
collection by removing their associations from the join table. This
does not destroy the objects.
Brilliant reference here for you
You can use:
product = Product.find(x)
special = product.specials.find(y)
product.specials.delete(special)
This creates ActiveRecord objects for both the object you're trying to remove, which gives clear definition to the function
collection.clear
Removes all objects from the
collection by removing their associations from the join table. This
does not destroy the objects.
In this example:
product = Product.find(x)
product.specials.clear

Getting count by drilling down through relations

I'm using Rails 4 and I want to get a count of votes for each individual post review for a given post.
Schema:
Post PostReview PostReviewVote
id post_id post_review_id
user_id user_id
voted_on_date
I have something like:
table.table.table-striped.table-hover
thead
tr
th Post Name
th Reviews Created For Post
th Total Votes in All Reviews For Post
tbody
- for post in #posts
tr
td= post.name
td= PostReview.where(post_id: post.id).size
td= PostReview.where(post_id: post.id).PostReviewVotes.size
But it's not fetching, citing a runtime error:
undefined method `PostReviewVotes' for #<ActiveRecord::Relation []>
Any suggestions? My models have the proper associations, FYI.
You could simply do this:
post.post_reviews.count
post.post_review_votes.count
If you have all the associations defined as you say.
or if you want a method for it...
In your Post model:
def post_votes_total
self.post_review_votes.count
end
As long as you have defined the relationship in Post:
has_many :post_review_votes, through: :post_reviews
Since you are passing the #posts variable to the view :
post.post_votes_total
Using the built in association methods in the views is fine, but if logic gets more complicated you should really use a model method or a helper method.
Couple of issues I see:
PostReview.where(post_id: post.id) can return more than one record.
Your relationship should be defined as post_review_votes, so you'd want to call .post_review_votes rather than PostReviewvotes
You might want one of the following:
`PostReview.where(post_id: post.id).first.post_review_votes.size- If you only expect one PostReview this is probably what you want.
PostReview.where(post_id: post.id).collect{|pr| pr.post_review_votes.size}.sum - This will take all of the post reviews, get the size of their votes, and add them together.
Or pure SQL:
PostReviewVotes.includes(:post_review).where("post_reviews.post_id: ?", post.id).count

Unique values of a parent in has_many relationship

I have a model projectuser which has_many activities, and activities belongs_to projectuser. Now I have a collection of activities as #activities in a controller and I want to get the unique projectusers. How can I do this?
If #activities is a simple ruby collection (e.g. Array), then use this:
Projectuser.where(id: #activities.map(&:projectuser_id).uniq)
However, if your #activities object is an ActiveRelation (the result of an association call or query api call), you can do a more efficient lookup like this:
Projectuser.joins(:activities).merge(#activities)
The reason it is more efficient is because it avoids building a literal list of ids, which can get significantly harder on a db engine at it gets larger.

Resources