33

Is there a way to clear old selects in a .select("table.col1, ...") statement?

Background:

I have a scope that requests accessible items for a given user id (simplified)

    scope :accessible, lambda { |user_id|
  joins(:users).select("items.*")
    .where("items_users.user_id = ?) OR items.created_by = ?", user_id, user_id)
}

Then for example in the index action i only need the item id and title, so i would do this:

@items = Item.accessible(@auth.id).select("polls.id, polls.title")

However, this will select the columns "items., items.id, items.title". I'd like to avoid removing the select from the scope, since then i'd have to add a select("items.") everywhere else. Am I right to assume that there is no way to do this, and i either live with fetching too many fields or have to use multiple scopes?

mkon
  • 1,063
  • 9
  • 13

2 Answers2

68

Fortunately you're wrong :D, you can use the #except method to remove some parts of the query made by the relation, so if you want to remove the SELECT part just do :

@items = Item.accessible(@auth.id).except(:select).select("polls.id, polls.title")
Holin
  • 1,191
  • 10
  • 10
  • 6
    This is super helpful to know about. It seems to me that whenever you apply a `#select` clause that contains an AS alias (like `User.select("'a' AS abacus")...`, you always need to `except(:select)` before calling `#count`. Do you know of any case where this isn't true, and if not, any idea why `#count` doesn't automatically apply `except(:select)` for you? I'm working with some thorny geolocation filtering situations that are making this into a constant issue for me. – Topher Hunt Mar 15 '15 at 23:00
11

reselect (Rails 6+)

Rails 6 introduced a new method called reselect, which does exactly what you need, it replaces previously set select statement.

So now, your query can be written even shorter:

@items = Item.accessible(@auth.id).reselect("polls.id, polls.title")
Marian13
  • 7,740
  • 2
  • 47
  • 51