I have 3 models:
class Student < ActiveRecord::Base
has_many :student_enrollments, dependent: :destroy
has_many :courses, through: :student_enrollments
end
class Course < ActiveRecord::Base
has_many :student_enrollments, dependent: :destroy
has_many :students, through: :student_enrollments
end
class StudentEnrollment < ActiveRecord::Base
belongs_to :student
belongs_to :course
end
I wish to query for a list of courses in the Courses table, that do not exist in the StudentEnrollments table that are associated with a certain student.
I found that perhaps Left Join is the way to go, but it seems that joins() in rails only accept a table as argument. The SQL query that I think would do what I want is:
SELECT *
FROM Courses c LEFT JOIN StudentEnrollment se ON c.id = se.course_id
WHERE se.id IS NULL AND se.student_id = <SOME_STUDENT_ID_VALUE> and c.active = true
How do I execute this query the Rails 4 way?
Any input is appreciated.
If you want OUTER JOINs without all the extra eagerly loaded ActiveRecord objects, use
.pluck(:id)
after.eager_load()
to abort the eager load while preserving the OUTER JOIN. Using.pluck(:id)
thwarts eager loading because the column name aliases (items.location AS t1_r9
, for example) disappear from the generated query when used (these independently named fields are used to instantiate all the eagerly loaded ActiveRecord objects).A disadvantage of this approach is that you then need to run a second query to pull in the desired ActiveRecord objects identified in the first query:
There is actually a "Rails Way" to do this.
You could use Arel, which is what Rails uses to construct queries for ActiveRecrods
I would wrap it in method so that you can call it nicely and pass in whatever argument you would like, something like:
There is also the quick (and slightly dirty) way that many use
eager_load works great, it just has the "side effect" of loding models in memory that you might not need (like in your case)
Please see Rails ActiveRecord::QueryMethods .eager_load
It does exactly what you are asking in a neat way.
It'a join query in Active Model in Rails.
Please click here for More info about Active Model Query Format.
Use Squeel:
Adding to the answer above, to use
includes
, if you want an OUTER JOIN without referencing the table in the where (like id being nil) or the reference is in a string you can usereferences
. That would look like this:or
http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-references
Combining
includes
andwhere
results in ActiveRecord performing a LEFT OUTER JOIN behind the scenes (without the where this would generate the normal set of two queries).So you could do something like:
Docs here: http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations