Skip to content

Commit

Permalink
Use correct values when sorting by has_many associations
Browse files Browse the repository at this point in the history
When sorting by has_many associations, Administrate assumes that
the table and column names are `#{attribute}.id`.
An error will occur if the user defines a different association than
the table name or uses a primary key other than :id.

For example, the following code causes an error.

```ruby
# db/migrate/20201016000000_create_users.rb
create_table :users, primary_key: "guid" do |t|
  t.references :company

  t.timestamps
end

# app/models/company.rb
class Company < ApplicationRecord
  has_many :employee, class_name: "User"
end

Administrate::Order.new("employee", "asc").apply(Company.all).take
#   Company Load (0.6ms)  SELECT "companies".* FROM "companies" LEFT OUTER JOIN "users" ON "users"."company_id" = "companies"."id" GROUP BY "companies"."id" ORDER BY COUNT(employee.id) asc LIMIT ?  [["LIMIT", 1]]
# Traceback (most recent call last):
#         1: from (irb):14
# ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: employee.id)
```

This commit fixes `Administrate::Order` to use the correct table and column names.
  • Loading branch information
sinsoku committed Oct 15, 2020
1 parent 0bf2064 commit 988ec18
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 4 deletions.
3 changes: 2 additions & 1 deletion lib/administrate/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ def order_by_association(relation)
end

def order_by_count(relation)
klass = reflect_association(relation).klass
relation.
left_joins(attribute.to_sym).
group(:id).
reorder("COUNT(#{attribute}.id) #{direction}")
reorder("COUNT(#{klass.table_name}.#{klass.primary_key}) #{direction}")
end

def order_by_id(relation)
Expand Down
10 changes: 7 additions & 3 deletions spec/lib/administrate/order_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@
context "when relation has_many association" do
it "orders the column by count" do
order = Administrate::Order.new(:name)
relation = relation_with_association(:has_many)
relation = relation_with_association(
:has_many,
klass: double(table_name: "users", primary_key: "guid")
)
allow(relation).to receive(:reorder).and_return(relation)
allow(relation).to receive(:left_joins).and_return(relation)
allow(relation).to receive(:group).and_return(relation)
Expand All @@ -76,7 +79,7 @@

expect(relation).to have_received(:left_joins).with(:name)
expect(relation).to have_received(:group).with(:id)
expect(relation).to have_received(:reorder).with("COUNT(name.id) asc")
expect(relation).to have_received(:reorder).with("COUNT(users.guid) asc")
expect(ordered).to eq(relation)
end
end
Expand Down Expand Up @@ -190,13 +193,14 @@ def relation_with_column(column)
)
end

def relation_with_association(association, foreign_key: "#{association}_id")
def relation_with_association(association, foreign_key: "#{association}_id", klass: nil)
double(
klass: double(
reflect_on_association: double(
"#{association}_reflection",
macro: association,
foreign_key: foreign_key,
klass: klass
),
),
)
Expand Down

0 comments on commit 988ec18

Please sign in to comment.