1

I've been digging for a couple days and haven't found a good explanation for this behavior change. I'm in the process of upgrading a Rails application from 3.2 to 5.2 this is code in a passing test from my rails 3 app.

 ps = Project.includes(:rentals).where('rentals.id IN (?)', [1,2,3,4])

this spits out a big old left joins SQL query.
but if i do the same query in rails 5 I get a mysql error

ActiveRecord::StatementInvalid (Mysql2::Error: Unknown column 'rentals.id' in 'where clause': SELECT  `projects`.* FROM `projects` WHERE (rentals.id IN (1,2,3,4)) LIMIT 11 /*application:ConHQ*/)

my understanding of includes is that it should perform separate queries unless the included table is referenced in the where clause, in which case it should do a left join. but it seems like that is not happening here. using eager_load works in this case:

 ps = Project.eager_load(:rentals).where('rentals.id IN (?)', [1,2,3,4])

but i thought that includes should do the same.
I've also noticed that sometimes includes does perform the same query as eager_load

@project = Project.find(174)
@project.rentals.eager_load(:equipment_name).where('equipment_names.id IN (?)', [1,2,3,4])
@project.rentals.includes(:equipment_name).where('equipment_names.id IN (?)', [1,2,3,4])

In this case eager_load and includes both perform a left join. I haven't found any documentation that explains why includes acts differently in these situations. how exactly does includes choose the query that it performs?

Andrew Algard
  • 105
  • 1
  • 12

1 Answers1

3

includes just tells ActiveRecord that the data should be eager-loaded but it can achieve this however it likes. If you want to reference the other model in a query, you'll have to use references(:rentals), too.

The ActiveRecord API is extremely powerful and versatile, but as is often the case with Ruby and its libraries, there are many ways to achieve something. In this case, you can also use merge to merge in another relation.

rentals = Rental.where(id: [1,2,3,4])
projects = Project.joins(:rentals).merge(rentals) # you can additionally add includes, too, if you want to access the rentals

This has the added advantage that you don't need to care about the table name of the Rental model.

Marcus Ilgner
  • 6,935
  • 2
  • 30
  • 44
  • good to know! that answers the first part of my question, but I still don't understand why my two situations return different results. `Project.includes(:rentals).where('rentals.id = 2')` errors out but `@project.rentals.includes(:equipment_names).where('equipment_names.id =2') ` performs a left join with no errors. I guess i'm still confused as to why these situations would behave differently. – Andrew Algard Jan 03 '19 at 19:20
  • Are both associations `has_many`? Maybe one is `habtm`? To find out for certain and to also learn some good Ruby, I usually recommend looking at [the source](https://github.com/rails/rails/) . For me, this is often enough reason to answer a SO question: so I have a reason to dig into the Rails code some more ;) – Marcus Ilgner Jan 03 '19 at 19:24
  • 1
    aha, you are correct. it's a belongs_to, i misread when initially creating the question. Thanks for the help! – Andrew Algard Jan 03 '19 at 19:34