ActiveRecord find with joins and associations? - ruby-on-rails

I have a model TwitterUser that has_one website as shown in the model below:
class TwitterUser < ActiveRecord::Base
has_one :website, :foreign_key => :id, :primary_key => :website_id
end
I'm trying to run a query that will join TwitterUser with Website and get all TwitterUsers' with a website that has an updated_at date > a certain date, limited to 10 rows.
I thought this would give me what I wanted, but apparently it's not. What's wrong with it?
TwitterUser.includes().find(:all, :limit => 10, :conditions => ["websites.updated_at >= '2013-05-12 05:31:53.68059'"], :joins => :website)
In my database, my twitter_users table consist of a website_id field.
My websites table has an id field.

This should work.
TwitterUser.joins(:website).where("websites.updated_at >= '2013-05-12 05:31:53.68059'").limit(10)

Related

How to Query Using a Model that Belongs To Itself in Rails

I'm using Rails 3.2. I have a setup similar to the following:
class User < ActiveRecord::Base
attr_accessible :is_admin
belongs_to :created_by, :foreign_key => :created_by_id, :class_name => 'User'
end
This works if not using ActiveRecord query, just like the following:
#rails console
User.first.created_by.is_admin
#=> true
#But I want to query like the following, but it doesn't work
User.where(:created_by => {:is_admin => true})
#ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'created_by.is_admin' in 'where clause'...
#This also doesn't work:
User.joins(:created_by).where(:created_by => {:is_admin => true})
#ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'created_by.is_admin' in 'where clause'
I would really be grateful for any help.
You could do it using 2 queries
admin_ids = User.where(:is_admin => true).pluck(:id)
#users = User.where(:created_by_id => admin_ids)
I'd do this because
A lot of times, 2 simple queries are faster than 1 complex join query
Readable & easy to understand

includes / joins with has_and_belongs_to_many and sort / ordering on both models

I'm having a little bit of a brain problem with what I think would be a simple call:
I've got:
class Channel < ActiveRecord::Base
has_and_belongs_to_many :shows, :join_table => :channels_shows
end
class Show < ActiveRecord::Base
has_and_belongs_to_many :channels, :join_table => :channels_shows
end
A channel has a :position and :hidden in the database (:hidden can be false, or nil if not saved as I had forgotten about defaulting to 0).
A show has :approved (same as :hidden) and of course :created_at.
I want to be able to get Channels that are (:hidden => [nil, false] ) with each channels included Shows where a show is :approved and by created_at, newest first.
I can't figure out if this is a join or an include. The closest I've gotten is this, but this doesn't sort the included shows in the right order:
Channel.order('channels.position').where(:hidden => [nil, false] ).includes(:shows).where(shows:{approved: true})
Still looking at docs and trying things in the irb; feel like it's crazy simple but I'm just not getting it.
To sort the join records, just include that sort in the order clause after your primary sort. Your channels will still have the primary sort order, but when they are equal (ie when comparing the same channel but a different show), it will fall back to sorting by the second order (effectively sorting your included table):
Channel.order('channels.position, shows.created_at').includes(:shows)...
I think you should be able to do something like this:
class Channel < ActiveRecord::Base
has_and_belongs_to_many :shows, :join_table => :channels_shows
has_and_belongs_to_many :active_shows, :class_name => 'Show', :join_table => :channels_shows, :conditions => ["approved = ?", true], :order => "created_at desc"
end
To allow you to go
Channel.order('channels.position').where(:hidden => [nil, false] ).includes(:active_shows)
This is rails 3 syntax by the way.

How to use joins using associations in Ruby on Rails

I am trying to create a Redmine plugin built on Ruby on Rails. I have the following query which fetches the sum of time_entries of all the issues during an invoice.
SELECT SUM( t.hours )
FROM time_entries t
JOIN invoices i ON t.project_id = i.project_id
WHERE t.project_id = <current project id----#project.id>
AND i.id = <billing invoice id>
AND t.updated_on > 'i.created_at'
AND t.updated_on < 'i.due_date'
How can I store this query data inside invoices table column called time_spent or retrieve the results in the invoices view which lists all invoices along with the above query by invoice ID
The associations in model I created goes like this
class Invoice < ActiveRecord::Base
set_table_name "invoices"
set_primary_key "invoice_id"
belongs_to :project
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
has_many :time_entries, :class_name => "TimeEntry", :foreign_key => "project_id"
In the Controller I am calling the model as
#invoices = Invoice.find(:all, :joins=>" INNER JOIN time_entries ON time_entries.project_id = invoices.project_id",:conditions => ["invoices.project_id = ? AND updated_on > ? AND updated_on < ?", #project.id, invoices.created_at, invoices.due_date])
I know the controller instance variable is totally messed up.
Somewhere I doing mistake. Can some one please help me with this.
I'm assuming Rails 3+ and MySQL, this should give you your each item in your #invoices collection an accessor named "time_spent" which will have the sum you are looking for. It will not persist this info in the db, but it retrieves it for your view:
Invoice.where("invoices.project_id = ? AND invoices.updated_on > ? AND invoices.updated_on < ?", #project.id, invoices.created_at, invoices.due_date).select("SELECT invoices.*, SUM( time_entries.hours ) as time_spent").joins(:time_entries).group("invoices.id")
I'm guessing at < Rails 3 below:
Invoice.find(:all,
:conditions => ["invoices.project_id = ? AND updated_on > ? AND updated_on < ?", #project.id, invoices.created_at, invoices.due_date],
:select=>"SELECT invoices.*, SUM( time_entries.hours ) as time_spent",
:joins=>[:time_entries],
:group=>"invoices.id")
(hoping I typed this correctly. If not, the gist is to use the "select" and "group" methods to get your result.)
Got it working with the following code.
Invoice.find(:all, :select => 'invoices.*, SUM( time_entries.hours ) as time_spent_on_issues', :joins=>" JOIN time_entries ON time_entries.project_id = invoices.project_id", :conditions => ["invoices.project_id = ? AND time_entries.updated_on > invoices.created_at AND time_entries.updated_on < invoices.due_date", #project.id])
BTW "miked" code helped a lot

Correct ActiveRecord joins query assistance

Need a little help with a SQL / ActiveRecord query. Let's say I have this:
Article < ActiveRecord::Base
has_many :comments
end
Comment < ActiveRecord::Base
belongs_to :article
end
Now I want to display a list of "Recently Discussed" articles - meaning I want to pull all articles and include the last comment that was added to each of them. Then I want to sort this list of articles by the created_at attribute of the comment.
I have watched the Railscast on include /joins - very good, but still a little stumped.
I think I want to use a named_scope, something to this effect:
Article < ActiveRecord::Base
has_many :comments
named_scope :recently_commented, :include => :comments, :conditions => { some_way_to_limit_just_last_comment_added }, :order => "comments.created_at DESC"
end
Using MySQL, Rails 2.3.4, Ruby 1.8.7
Any suggestions? :)
You have two solutions for this.
1) You treat n recent as n last. Then you don't need anything fancy:
Article < ActiveRecord::Base
has_many :comments
named_scope :recently_commented, :include => :comments,
:order => "comments.created_at DESC",
:limit => 100
end
Article.recently_commented # will return last 100 comments
2) You treat recent as in last x duration.
For the sake of clarity let's define recent as anything added in last 2 hours.
Article < ActiveRecord::Base
has_many :comments
named_scope :recently_commented, lambda { {
:include => :comments,
:conditions => ["comments.created_at >= ?", 2.hours.ago]
:order => "comments.created_at DESC",
:limit => 100 }}
end
Article.recently_commented # will return last 100 comments in 2 hours
Note Code above will eager load the comments associated with each selected article.
Use :joins instead of :include if you don't need eager loading.
You're gonna have to do some extra SQL for this:
named_scope :recently_commented, lambda {{
:select => "articles.*, IFNULL(MAX(comments.created_at), articles.created_at) AS last_comment_datetime",
:joins => "LEFT JOIN comments ON comments.article_id = articles.id",
:group => "articles.id",
:conditions => ["last_comment_datetime > ?", 24.hours.ago],
:order => "last_comment_datetime DESC" }}
You need to use :joins instead of :include otherwise Rails will ignore your :select option. Also don't forget to use the :group option to avoid duplicate records. Your results will have the #last_comment_datetime accessor that will return the datetime of the last comment. If the Article had no comments, it will return the Article's created_at.
Edit: Named scope now uses lambda

Rails, ActiveRecord: how do I get the results of an association plus some condition?

I have two models, user and group. I also have a joining table groups_users.
I have an association in the group model:
has_many :groups_users
has_many :users, :through=> :groups_users
I would like to add pending_users which would be the same as the users association but contain some conditions. I wish to set it up as an association so that all the conditions are handled in the sql call. I know there's a way to have multiple accessors for the same model, even if the name is not related to what the table names actually are. Is it class_name?
Any help would be appreciated, thanks
Use named_scopes, they're your friend
Have you tried using a named_scope on the Group model?
Because everything is actually a proxy until you actually need the data,
you'll end up with a single query anyway if you do this:
class User < ActiveRecord::Base
named_scope :pending, :conditions => { :status => 'pending' }
and then:
a_group.users.pending
Confirmation
I ran the following code with an existing app of mine:
Feature.find(6).comments.published
It results in this query (ignoring the first query to get feature 6):
SELECT *
FROM `comments`
WHERE (`comments`.feature_id = 6)
AND ((`comments`.`status` = 'published') AND (`comments`.feature_id = 6))
ORDER BY created_at
And here's the relevant model code:
class Feature < ActiveRecord::Base
has_many :comments
class Comment < ActiveRecord::Base
belongs_to :feature
named_scope :published, :conditions => { :status => 'published' }
This should be pretty close - more on has_many.
has_many :pending_users,
:through => :groups_users,
:source => :users,
:conditions => {:pending => true}
:pending is probably called something else - however you determine your pending users. As a side note - usually when you see a user/group model the association is called membership.
In the User model:
named_scope :pending, :include => :groups_users, :conditions => ["group_users.pending = ?", true]
That's if you have a bool column named "pending" in the join table group_users.
Edit:
Btw, with this you can do stuff like:
Group.find(id).users.pending(:conditions => ["insert_sql_where_clause", arguments])

Resources