Reputation: 722
I have three models:
Invitation.rb
belongs_to :department
Department.rb
belongs_to :organisation
has_many :invitations
Organisation.rb
has_many :departments
Organization table has tag
field.
I need to write a query to search for Invitations, for the organization of which there is a tag
field with the content 'First tag'
Something like this:
Invitation.joins(:department).joins(:organisation).where(tag: 'First tag')
But I don't know how to build such a query.
Upvotes: 1
Views: 43
Reputation: 482
To do what you are trying to do without changing your models you should pass a hash to the joins method like so:
Invitation.joins(department: :organisation).where(organisations: { tag: 'First tag' })
This tells ActiveRecord to use the department association as defined on the Invitation model and the organisation association from the Department model
To make your life a bit easier you can add a has_many through
association like this:
class Invitation < ApplicationRecord
belongs_to :department
has_one :organisation, through: :department
end
class Department < ApplicationRecord
belongs_to :organisation
has_many :invitations, inverse_of: :department
end
class Organisation
has_many :departments, inverse_of: :organisation
has_many :invitations, through: :departments
end
Now you can use the invitations relation on Organisation as follows:
Invitation.joins(:organisation).where(tag: 'First tag')
To see the sql (e.g. in the console) you can use the #to_sql method:
Invitation.joins(:organisation).where(tag: 'First tag').to_sql
Finally, if you use a scope (and dont like the hash in the where method in which you have to specify the table) you can use a "scope" and merge as follows:
class Organisation
class << self
def just_tagged
where(tag: 'First tag')
end
end
end
Now you can refer to the scope within your query using merge
:
Invitation.joins(:organistion).merge(Organisation.just_tagged)
Upvotes: 1
Reputation: 33430
Invitation.joins(department: :organisation).where(organisations: { tag: 'First tag' })
Why? Because to "reach" the organisations table from invitations you can use the relationship between the departments table.
Table.joins(...).joins(...)
Is "joining" twice the same table, what you need is to reference the relatinoship between departments and organisations. That you can do it as:
{ department: :organisation }
Passing that to join loads a JOIN clause in the receiver with departments
and in departments
with organisations
.
Notice the where clause can be used as { organisations: { tag: ... } }
or ["organisations.tag = ?", ...]
, they can be used interchangeably. What you use is just a matter of preference yours or from your colleagues.
Upvotes: 2