Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord: query not using correct type condition for STI subclass

I have a set of STI subclasses inheriting from a User base class. I am finding that under certain conditions inside a subclass' definition, queries on the subclasses do not correctly use the type condition.

class User < ActiveRecord::Base
  # ...
end

class Admin < User
  Rails.logger.info "#{name}: #{all.to_sql}"
  # ...
end

When loading the Rails console in development, it does what I would expect:

Admin: SELECT `users`.* FROM `users` WHERE `users`.`type` IN ('Admin')

But when hitting the app (localhost / pow), it is missing the type condition and I get this:

Admin: SELECT `users`.* FROM `users`

But not from the app when when deployed to a staging server:

Admin: SELECT `users`.* FROM `users` WHERE `users`.`type` IN ('Admin')

This, of course, causes any queries executed here in the dev app (but not from the console) to be incorrect. Specifically, I am trying to preload a (small) cache of existing db values in order to create a few helpful methods based on those data. Without the type scope, the cache is obviously incorrect!

From the same location (Admin), we get the following confusing contradiction:

[11] pry(Admin)> Admin.finder_needs_type_condition?
=> true
[12] pry(Admin)> Admin.send(:type_condition).to_sql
=> "`users`.`type` IN ('Admin')"
[13] pry(Admin)> Admin.all.to_sql
=> "SELECT `users`.* FROM `users`"

Further, I defined a throwaway subclass Q < User inside the user.rb file. I logged Q.all.to_sql from its definition, from the definition of Admin, and from a view. In that order, we get:

From Q: Q: SELECT `users`.* FROM `users` WHERE `users`.`type` IN ('Q')
From Admin: Q: SELECT `users`.* FROM `users`
From View: Q: SELECT `users`.* FROM `users` WHERE `users`.`type` IN ('Q')

What could cause, in the first line of the Admin subclass definition in admin.rb, any subclass of User to fail to use its type_condition?

This is causing development tests to fail, and so is of some consequence to my app. What on earth could be causing this difference in behavior? Can anyone think of a more general way around the problem of not having the STI conditions defined on a subclass during its definition only in the development app environment?

like image 962
Andrew Schwartz Avatar asked May 02 '16 22:05

Andrew Schwartz


2 Answers

One difference between production and development is the following line inside of the application configuration:

# config/environments/development.rb
config.eager_load = false

vs.

# config/environments/production.rb
config.eager_load = true

So on your production environment, all your clases are loaded when the app is started. When eager_load is set to false, Rails will try to autoload your User class when you first load the Admin class.

Given that, I'd assume that you have another class or module named User.

FYI: ActiveRecord has a method called finder_needs_type_condition?. It should return true for a class that uses STI:

User.finder_needs_type_condition? # should be false
Admin.finder_needs_type_condition? # should be true
like image 59
Alex Avatar answered Oct 24 '22 10:10

Alex


Currently, any User will have an empty :type column. Could this is be a problem?

Have you tried to make User a super class? E.g.

class User < ActiveRecord::Base
end

class Admin < User; end
class NormalUser < User; end
like image 42
andiba Avatar answered Oct 24 '22 09:10

andiba