Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

has_and_belongs_to_many relationships will not store join table #38

Open
bessey opened this issue Apr 13, 2016 · 3 comments
Open

has_and_belongs_to_many relationships will not store join table #38

bessey opened this issue Apr 13, 2016 · 3 comments

Comments

@bessey
Copy link

bessey commented Apr 13, 2016

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

@bessey
Copy link
Author

bessey commented Apr 13, 2016

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 Roles to load from the SQL statement proceeding it.

Well, I don't see a solution to this problem. 😅

@bessey
Copy link
Author

bessey commented Apr 13, 2016

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"]

@nettofarah
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants