This is a classic inner join scenario
class Student < ApplicationRecord
has_many :subscriptions
end
class Subscription < ApplicationRecord
belongs_to :student
end
I find it helpful to break these problems into steps:
"Only Students where a Subscription record is present" is a standard inner join:
Student.joins(:subscriptions).uniq
"object must contain only the student's name, email, phone and status"
Student.joins(:subscriptions).select(:name, :email, :phone, :status).uniq
"in addition to the subscription id and recurrence_code"
students = Student.joins(:subscriptions)
.select(
'students.name, students.email,'\
'students.phone, students.status, '\
'subscriptions.id as subscription_id,'\
'subscriptions.recurrence_code as subscription_recurrence_code'
)
A few notes:
1. Using select
with joins
@vee's SO Answer here points out:
If the column in select
is not one of the attributes of the model
on which the select
is called on then those columns are not displayed. All of these attributes are still contained in the objects within AR::Relation
and are accessible as any other public instance attributes.
This means if you load an individual record (e.g. students.first
), you will only see the Student
attributes by default. But you can still access the Subscription
attributes by the as
name you set in the query. E.g.:
students.first.subscription_recurrence_code
2. Use .uniq
to eliminate duplicates.
Because of the has_many
relationship, the query Student.joins(:subscriptions)
will return a record for each subscription, which means each student will appear in the result the same number of times as they have subscriptions. Calling .uniq
(short for unique) will remove duplicates from the result.