We recently upgraded rails to 5.1 following steps in the Rails guide, but see that the following is not working. I have the following model definitions
class User < ActiveRecord::Base
has_many :members, :dependent => :destroy
has_many :user_accounts, :dependent => :destroy
end
class Member < ActiveRecord::Base
belongs_to :user
has_many :user_accounts, :through => :user
end
class UserAccount < ActiveRecord::Base
belongs_to :user
end
When I try to execute, for example
user = User.find 109
member = user.members[0]
member.user_accounts
this generates the following query
SELECT `user_accounts`.* FROM `user_accounts` INNER JOIN `users` ON `user_accounts`.`user_id` = `users`.`id` WHERE `user_accounts`.`users` = NULL
users is getting checked with NULL
it should be users.id which is 109.
One important thing to mention is that the same Member
model has other relationship using through
with other models which work. for example
member.stores
member.credit_cards
but member.user_accounts
does not work.
Any help on how to address this would be great, Thanks.
I feel like I have to put this here:
:through
Specifies an association through which to perform the query. This can be any other type of association...
https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many.
I've set up models, just like you show, working fine:
>> Member.first.user.user_accounts
=> #<ActiveRecord::Associations::CollectionProxy [#<UserAccount id: 1, user_id: 1>]>
# through: user
>> Member.first.user_accounts
Member Load (0.8ms) SELECT `members`.* FROM `members` ORDER BY `members`.`id` ASC LIMIT 1
UserAccount Load (0.9ms) SELECT `user_accounts`.* FROM `user_accounts` INNER JOIN `users` ON `user_accounts`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 LIMIT 11
=> #<ActiveRecord::Associations::CollectionProxy [#<UserAccount id: 1, user_id: 1>]>
Here's how I broke it:
class AddUsersToUserAccounts < ActiveRecord::Migration[5.1]
def change
add_column :user_accounts, :users, :integer
end
end
>> Member.first.user_accounts
Member Load (0.8ms) SELECT `members`.* FROM `members` ORDER BY `members`.`id` ASC LIMIT 1
UserAccount Load (0.7ms)
SELECT `user_accounts`.* FROM `user_accounts`
INNER JOIN `users` ON `user_accounts`.`user_id` = `users`.`id`
WHERE `user_accounts`.`users` = NULL LIMIT 11
# ^
# using `users` column instead
=> #<ActiveRecord::Associations::CollectionProxy []>
>> UserAccount.column_names
=> ["id", "user_id", "users"]
>> Member.first.user_accounts.arel.ast.cores[0].wheres[0].children[0].to_sql
=> "`user_accounts`.`users` = ?"
When users.id = 1
where clause is being built, arel takes a hash {"users"=>{"id"=>1}}
and spits out some sql. If there is a users
column present, it stumbles on this part and treats "users"
key as a column instead of a table:
when value.is_a?(Hash) && !table.has_column?(column_name)
# ^^^^^^^^^ this one ^^^^^^^^^^^^
When I rollback and remove users
column, it goes into the correct branch and builds a working sql:
>> UserAccount.column_names
=> ["id", "user_id"]
>> Member.first.user_accounts.arel.ast.cores[0].wheres[0].children[0].to_sql
=> "`users`.`id` = ?"