has_and_belongs_to_many relationships will not store join table
Opened this issue · 3 comments
Title perhaps doesn't explain the problem well, code does:
class Account
has_and_belongs_to_many :roles
end
class Role
has_and_belongs_to_many :accounts
end
account = Account.create name: "Matt"
role = Role.create name: "admin"
account.roles << role
Polo.explore(Account, [1,2,3], [:roles])
This will result in the INSERT SQL for account
and role
being created, but not for the join table. The "solution" appears to be to use has_many through:
instead, and always have an AR model with an ID column as the join table.
I don't have a solution, so I feel the README should at least clarify that HABTM doesn't work right now, and to use has_many through:
instead.
Encountered on Rails 3.2
Some prying about revealed why. I threw a pry in here:
From: /Users/matt/work/polo/lib/polo/collector.rb @ line 39 Polo::Collector#collector:
37: def collector
38: lambda do |name, start, finish, id, payload|
=> 39: binding.pry
40: return unless payload[:name] =~ /^(.*) Load$/
41: begin
42: class_name = $1.constantize
43: sql = payload[:sql]
44: collect_sql(class_name, sql)
45: rescue ActiveRecord::StatementInvalid, NameError
46: # invalid table name (common when prefetching schemas)
47: end
48: end
49: end
And, ignoring the irrelevant good payloads for Account, heres the interesting iterations:
# [1] directory(#<Polo::Collector>) » payload
=> {
:sql => "SHOW TABLES LIKE 'accounts_roles'",
:name => "SCHEMA",
:connection_id => 70096124074740,
:binds => [],
:line => 7,
:filename => "/Users/matt/work/dir.caring.com/bin/rake",
:method => "<top (required)>"
}
# [1] directory(#<Polo::Collector>) » payload
=> {
:sql => "SHOW FULL FIELDS FROM `accounts_roles`",
:name => "SCHEMA",
:connection_id => 70096124074740,
:binds => [],
:line => 7,
:filename => "/Users/matt/work/dir.caring.com/bin/rake",
:method => "<top (required)>"
}
# [1] directory(#<Polo::Collector>) » payload
=> {
:sql => "SELECT `roles`.*, `t0`.`account_id` AS ar_association_key_name FROM `roles` INNER JOIN `accounts_roles` `t0` ON `roles`.`id` = `t0`.`role_id` WHERE `t0`.`account_id` IN (2, 5, 6, ... this is a very long list ... ) AND (`roles`.`deleted_at` IS NULL)",
:name => "SQL",
:connection_id => 70096124074740,
:binds => [],
:line => 7,
:filename => "/Users/matt/work/dir.caring.com/bin/rake",
:method => "<top (required)>"
}
# [1] directory(#<Polo::Collector>) » payload
=> {
:sql => "SELECT `roles`.* FROM `roles` WHERE `roles`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25) AND (`roles`.`deleted_at` IS NULL)",
:name => "Role Load",
:connection_id => 70096124074740,
:binds => [],
:line => 7,
:filename => "/Users/matt/work/dir.caring.com/bin/rake",
:method => "<top (required)>"
}
The Role Load
statement has already worked out which Role
s to load from the SQL
statement proceeding it.
Well, I don't see a solution to this problem. 😅
Lastly I'll add the way we have "solved" this problem is to redefine the associations when we scrub to be has_many through:
, and make a dummy join model. Its far from ideal but it works.
# lib/tasks/db.rake
namespace :db do
task :scrub_setup do
# Redefine the HABTM for roles so we can use polo.
class AccountsRole < ActiveRecord::Base
belongs_to :account
belongs_to :role
end
class Account < ActiveRecord::Base
has_many :accounts_roles
has_many :roles, through: :accounts_roles
end
class Role < ActiveRecord::Base
has_many :accounts_roles
has_many :accounts, through: :accounts_roles
end
end
end
Rake::Task["db:scrub"].enhance ["db:scrub_setup"]
hey, @bessey. Great investigative work!
I'll see if I can dig into this a little bit too.. Maybe we can come up with some creative solution for this.