Resolving Column Names Ambiguity When Using Cross Table Filters In ActiveAdmin

Gagandeep Singh · July 24, 2019

Recently, I was stuck solving a very unique issue while adding a new filter to an existing ActiveAdmin page. Consider two models:

class User
columns
  id: Integer
  name: String
  email: String
  balance: Float

Here, balance is a field representing balance for a legacy wallet. Consider another model:

class AnotherWallet
columns
  id: Integer
  user_id: Integer (belongs_to :user)
  phone_number: String
  balance: Float

I had an ActiveAdmin page for AnotherWallet with a filter on user's email and some scopes on balance column.

ActiveAdmin.register AnotherWallet do
  menu parent: 'Wallets'

  filter :phone_number
  filter :user_email

  scope :active # where(active: true)
  scope :zero_balance # where(balance: 0)
end

The page worked fine before I added a filter on user_email. On filtering by user_email, I got the following error

PG::AmbiguousColumn: ERROR:  column reference "balance" is ambiguous
LINE 1: ... ("users"."email" ILIKE '%example%') AND (balance <=...
                                                             ^
SELECT COUNT(DISTINCT "another_wallets"."id") FROM "another_wallets"
  LEFT OUTER JOIN
"users" ON "users"."id" = "another_wallets"."user_id"
  WHERE
"users"."email" ILIKE '%example%'
  AND
balance = 0

The query was fired by the zero_balance scope to count how many wallets have zero balance. The reason for this error was self-explanatory - Postgres performed a join between tables which had same column name (balance), and it had no idea how to differentiate between both.

At SQL level, the fix is simple; We need to add the table name before specifying the column. But because ActiveAdmin provides a DSL for creating everything, I thought there would be an option for this as well.

I spent hours debugging this, but the fix was very simple and was required at the model level. Rather than initializing scopes like this:

class AnotherWallet < ApplicationRecord
  ...
  scope :zero_balance, { where(balance: 0 ) }
end

I had to mention the complete table name in scope

class AnotherWallet < ApplicationRecord
  ...
  scope :zero_balance, -> { where('another_wallets.balance = 0') }
end

The resultant query was:

SELECT COUNT(DISTINCT "another_wallets"."id") FROM "another_wallets"
  LEFT OUTER JOIN
"users" ON "users"."id" = "another_wallets"."user_id"
  WHERE
"users"."email" ILIKE '%example%'
  AND
"another_wallets".balance = 0

So, that’s all for now! Let us know if you have any other suggestions for the same.


Twitter, Facebook