Active record query relationship - ruby-on-rails-3

I have models called project, gold_task and submissions.
Relationship goes like this:
project has_many gold_tasks
gold_tasks has_many submissions
How can i get all the submissions of a gold_task through project id.
I tried in the below way
p.gold_tasks.joins(:submissions)
Here i'm getting all gold_tasks but i need all submissions.
Thanks in advance.

I believe this is your question right here "How can i get all the submissions of a gold_task through project id?"
If you have set up your Rails associations correctly, you should be able to do something like this:
#project = Project.find(1)
#gold_tasks = #project.gold_tasks
Rails automatically will look up all the rows in the gold_tasks table for all rows with a Project id of 1 and return an array of what it finds. Furthermore, building objects is easy as well. Let's assume we have a project row again:
#project = Project.find(1)
#gold_task = #project.gold_task.build(hash)
This will build a new gold_task with a parameter hash and assign the #project id to it.
Check out this guide:
http://guides.rubyonrails.org/association_basics.html

Try this:
p.gold_tasks.submissions

Related

Rails ActiveRecord Access association's(children?) latest created objects

as title said i am trying to access an array of objects of an association
This is a has_many association
here is my class
class Keyword < ApplicationRecord
has_many :rankings
end
class Ranking < ApplicationRercord
belongs_to :keyword
end
There are a attribute in ranking called position:integer, i want to be able to access all latest created rankings from all keyword here is what i got so far
Keyword.all.joins(:rankings).select( 'MAX(rankings.id) ').pluck(:created_at, :keyword_id, :position)
i've read some other post suggesting me to use MAX on rankings.id, but i am still not able to return the array
At the moment Keyword.count return 4597
Ranking.count return 9245
Each keyword has generated about 2 rankings, but i just want the latest ranking from each keyword in array format, so to get latest of each i should expect around 4597
Not sure if i explained clear enough, hope u guys can help me :'( thanks really appreciate it
If you are using Postgres. You can use DISTINCT ON
Keyword.joins(:rankings)
.select("DISTINCT ON(ratings.keyword_id) keywords.*, ratings.position, ratings.created_at AS rating_created_at")
.order("ratings.keyword_id, ratings.id DESC")
Now you can access position, rating_created_at
#keywords.each do |k|
k.position
....
#keywords.map { |k| [k.id, k.rating_created_at, k.position] }
If you have enough rankings you might want to store the latest ranking on the on keywords table as a read optimization:
class Keyword < ApplicationRecord
belongs_to :latest_ranking, class_name: :ranking
has_many :rankings, after_add: :set_latest_ranking
def set_latest_ranking(ranking)
self.update!(latest_ranking: ranking)
end
end
Keyword.joins(:latest_ranking)
.pluck(:created_at, :id, "rankings.position")
This makes it both very easy to join and highly performant. I learned this after dealing with an application that had a huge row count and trying every possible solution like lateral joins to improve the pretty dismal performance of the query.
The cost is an extra write query when creating the record.
Keyword.joins(:rankings).group("keywords.id").pluck("keywords.id", "MAX(rankings.id)")
This will give you an array which elements will include an ID of a keyword and an ID of the latest ranking, associated with that keyword.
If you need to fetch more information about rankings rather than id, you can do it like this:
last_rankings_ids_scope = Ranking.joins(:keyword).group("keywords.id").select("MAX(rankings.id)")
Ranking.where(id: last_rankings_ids_scope).pluck(:created_at, :keyword_id, :position)

Get relation of a collection in rails

So simple but can't find how to do it in rails. I have a specific active record collection of users. Something like users = User.where(blabla). Given this collection is there a simple way to get all posts that those users have? Similar to User.posts, only for all users in that collection.
Post belongs_to User, User has_many posts.
Thanks!
Assuming that your Post model has a user_id with an association called "user", you can do something like this:
Post.where(user_id: User.where(blablah))
or
Post.joins(:user).where(users: {<user conditions>})
You'll need to be able to use the Hash form for the user conditions to use the second option. For example:
Post.joins(:user).where(users: {role: 'member'})
If your users query is more complex, you can create a scope for it:
class User < ApplicationRecord
scope :special, -> { where(< user conditions go here>) }
end
And then merge it with the Post query:
Post.joins(:user).merge(User.special)

Has_and_belongs_to_many in Rails 3

I've tried to look through stackoverflow to remedy my situation but unfortunately I've just made myself thoroughly confused.
I have two has_many to has_many relationships: Users has_and_belongs_to_many Roles, and Roles has_and_belongs_to_many Pods. I've already set up the correct tables and everything (i.e. the role_users and pod_roles tables): I know it's at least partially working because I'm able to do #user.roles and get all of that user's roles.
However, I want to take it one step further: I want to be able to do something akin to #user.roles.pods to get all of the pods pertaining to that user. After #user.roles.pods didn't work: here's what I tried to do:
#current_user_roles = current_user.roles
#pods = Array.new
#current_user_roles.each do |role|
#pods.push(role.pods)
end
#pods.each do |pod|
puts(pod.name+"-----------------")
end
Didn't work. This is just to try to get something super simple -- to try to get all of the pods' names to appear to I can tell if it's working.
Here's something you could try:
#pods = current_user.roles.collect { |role| role.pods }.flatten

Problem with active-record and sql

I have a little problem: I can't compose sql-query inside AR.
So, I have Project and Task models, Project has_many Tasks. Task has aasm-field (i.e. "status"; but it doesn't matter, i can be simple int or string field).
So, I want on my projects index page list all (last) projects and for every project I want count it's active, pending and resolved (for example) tasks.
Like this, just look:
First project (1 active, 2 pending,
10 resolved)
Second projects (4
active, 2 pending, 2 resolved)
So, sure I can do it with #projects = Project.all and then in view:
- #projects.each do |project|
= project.title
= project.tasks(:conditions => {:status => "active"}).count #sure it should be in model, just for example
= project.tasks(:conditions => {:status => "pending"}).count
# ...
- end
This is good, but makes 1+N*3 (for 3 task statuses) queries, i want 1. The question is simple: how?.
You could do a find with grouping and counting. Something like:
status_counts = project.tasks.find(:all,
:group => 'status',
:select => 'status, count(*) as how_many')
This will return you a list of Task-like objects with status and how_many attributes which you can then use to give your summary. E.g.
<%= status_counts.map { |sc| "#{sc.how_many} #{sc.status} }.to_sentence %>
Maybe you could, in your Project controller:
Fetch all your projects: Project.all
Fetch all your tasks: Task.all
Then, create a hash with something like
#statuses = Hash.new
#tasks.each do |t|
#statuses[:t.project_id][:t.status] += 1
end
And then use it in your view:
First project (<%= #statuses[:#project.object_id][:active] %> active)
This is not the prefect solution, but it is easy to implement and only use two (big) queries. Of course, this would re-create a hash every time, so you might want to look into database indexes or cache systems.
Also, named scopes would be interesting, like Task.active.
I'd suggest using a counter cache in your project model to prevent needing to recount all tasks on each display of the index page - have an active_count, pending_count and resolved_count, and update them whenever the task changes state.
If you just want to modify your existing code, try:
project.tasks.count(:conditions => "status = 'active'")
You could also add a scope to your task model that would enable you to do something like:
project.tasks.active.count
EDIT
Ok so I'm half asleep - got the wrong impression from your question :/
Yep, you can do it in one query - use find_by_sql to get your projects along with the grouped counts for the tasks. You'll be able to access the group counts in the resulting array of projects.
So, the right answer is:
Projects.all(:joins => :tasks,
:select => 'projects.*,
sum(tasks.status="pending") as pending_count,
sum(tasks.status = "accepted") as accepted_count,
sum(tasks.status = "rejected") as rejected_count',
:group => 'projects.id')

Help with Rails find_by queries

Say if #news_writers is an array of records. I then want to use #news_writers to find all news items that are written by all the news writers contained in #news_writers.
So I want something like this (but this is syntactically incorrect):
#news = News.find_all_by_role_id(#news_writers.id)
Note that
class Role < ActiveRecord::Base
has_many :news
end
and
class News < ActiveRecord::Base
belongs_to :role
end
Like ennen, I'm unsure what relationships your models are supposed to have. But in general, you can find all models with a column value from a given set like this:
News.all(:conditions => {:role_id => #news_writers.map(&:id)})
This will create a SQL query with a where condition like:
WHERE role_id IN (1, 10, 13, ...)
where the integers are the ids of the #news_writers.
I'm not sure if I understand you - #news_writers is a collection of Role models? If that assumption is correct, your association appears to be backwards - if these represent authors of news items, shouldn't News belong_to Role (being the author)?
At any rate, I would assume the most direct approach would be to use an iterator over #news_writers, calling on the association for each news_writer (like news_writer.news) in turn and pushing it into a separate variable.
Edit: Daniel Lucraft's suggestion is a much more elegant solution than the above.