I am upgrading a 13 year old Rails 3 application to a Rails 7 application and ran into some hiccups with using associations in where clauses in Rails 5.
Users are going to have many companies, but we only want to look at the company that is set as the parent
, or the one we're marking as "we currently care about". Not only that, but we want to ONLY return the users where this parent company that "we currently care about" IS the master_company.
User has many companies through UserCompanies
UserCompanies has a field called is_parent_company
Company has a field called is_master_company
The entire thing revolves around one association:
has_many :parent_company, -> {where(user_companies: {is_parent_company: true})}, through: :user_companies, source: :company
Which is supposed to represent the parent_company
we talked about above, and it does.
irb(main):067:0> User.first.parent_company
=> #<ActiveRecord::Associations::CollectionProxy [ #<some valid data> ] >
Though it is tempting to use this association and filter with the framework, I am interested in a fast application and reusable queries, so I need one query that can be built upon. So that takes us to the problem: Attempting to use this association inside of a where clause, such as:
User.joins(:parent_company).where(parent_company: {is_master_company: true})
ActiveRecord is looking for a table called parent_company
and throws me an error about: missing FROM-clause entry for table "parent_company"
, which seems reasonable: after all we are joining on it and attempting to use it in the where clause like we would a table.
User.joins(user_companies: :company).where(user_companies: {is_parent_company: true, companies: {is_master_company: true}})
Maybe this is how it should've been done in Rails 3 and 4 in the first place and the original code was just workaround code. What bugs me is that this is been "working" and spitting out the correct users for Rails 3 and 4, but I'm thrown this error coming into Rails 5. Did Rails 3 and 4 support this type of usage of associations inside of where clauses?
It's rather confusing and I am making this question to see if anyone can point me in the right direction for Active Record Query Interface changes from Rails 4 to Rails 5 that is responsible for throwing this error.
Thanks for anyone who takes part!
If the question is "what changed between v4.x and v5.x", you'd have to dig through the codebase
But you'll wind up having to chase down minor changes in underlying methods and may still not get a clear answer until you have a total understanding of the core internals of ActiveRecord. The up-side of this approach is... you'll have a total understanding of the core internals of ActiveRecord.
However, I think you've hit on the more important issue:
Can I write code that is backwards-compatible so I have some confidence that it's producing the same result?
Maybe this is how it should've been done in Rails 3 and 4 in the first place and the original code was just workaround code.
Yeah, it seems like Rails < 4 was allowing inference for what :parent_company
means, but the change in Rails 5+ indicates that was problematic.
You could try @spickermann's suggestion of clarifying your relations to match Rails convention. Because Rails is "convention over configuration", pluralization in relationships does have importance.
class User < ApplicationRecord
has_many :user_companies
has_many :companies, through :user_companies
# a collection is returned, even if it's always a collection of one, so it should be pluralized
has_many :parent_companies, -> {where(user_companies: {is_parent_company: true})}, through: :user_companies, source: :company
end
There's a chance this alone provides the same behavior in Rails 3, 4, and 5.