ransackable_scopes doesn't seem to work though an association

Created on 27 Aug 2014  路  11Comments  路  Source: activerecord-hackery/ransack

As an example I forked the ransack_demo app and added a scope to the Comment model, 'created_after'. I can now use ransack to search the Comment model using this scope, but I can't search it via the User model:

Comment.search(:created_after=>'2014-01-01').result.to_sql
=> "SELECT \"comments\".* FROM \"comments\" WHERE (created_at>'2014-01-01')"

vs:

User.search(:comments_created_after=>'2014-01-01').result.to_sql
=> "SELECT \"users\".* FROM \"users\"

But normal searches across the association work fine:
User.search(:comments_body_cont=>'hi').result.to_sql
=> "SELECT \"users\".* FROM \"users\" LEFT OUTER JOIN \"comments\" ON \"comments\".\"user_id\" = \"users\".\"id\" WHERE (\"comments\".\"body\" ILIKE '%hi%')"

The view pukes with:
undefined method `comments_created_since' for Ransack::Search>:Ransack::Search

My modified demo is at: https://github.com/cotcomsol/ransack_demo

Most helpful comment

Aha, I only thought this was working because we have this functionality in our app, but we've worked around it by defining our own ransackers and a formatter that changes "attributes" to "scopes"... all dirty hacks before I worked on the ransackable_scopes extraction.

So I was confused. ransackable_scopes only supports the base model you're searching on. If you want to search scopes on associated models, it gets more complex. I'll see about extracting what we have into a more general solution.

You can currently work around it by defining the scope on the parent model:

User.scope :with_comments_since, ->(date) { 
  joins(:comments).merge(Comment.created_after(date))
}

All 11 comments

Hi @cotcomsol, thanks for the input. Scopes in Ransack are a recent addition since June, and I'm not sure they work through associations yet. To be honest, I didn't need scopes (using ransackers instead) and so far don't use them, so someone who does use them could hopefully chime in about it.

Hmm. I think this should be working, at least I know that we use it successfully... I'll take a closer look when I have a minute.

I also have a patch almost ready to fix default scopes on associations, which is a closely related issue, but also its own beast.

Thanks @avit :smiley:

@cotcomsol your bundle is locked to ransack 1.2.2 which is older than the ransackable scopes feature:

https://github.com/cotcomsol/ransack_demo/blob/a55f7cb1f1ce2017ce654538a29e693dd1a6a6df/Gemfile.lock#L92

Guessing that's likely your problem... You can also use this option to see if your conditions aren't recognized:

Ransack.configure do |config|
  config.ignore_unknown_conditions = false
end

Please try updating and let us know.

I actually updated the demo to 1.3.0 before I did my testing but forgot to commit the updated Gemfile.lock. I just did that now. It definitely doesn't work for me even with 1.3.0.

  • Is it intentional that your scope name changes from created_after to created_since?
  • I don't see any join clauses in your queries above. Could you provide the exact query to help people reproduce the issue?
  • Why are you seeing LEFT OUTER JOIN instead of INNER JOIN?
  • Have you tried with a more recent version of Rails?
  • Did you try raising on unknown conditions as @avit suggested? If so, what happened?

The created_after/crated_since was a typo on my part. I did most of my testing on the console and didn't really worry about through the web. I did fix it though and it behaves the same.

I'm not doing any special joins or queries. Just the commands from the console example is essentially all I am doing to duplicate the issue. I don't know why it is doing a left outer join.

I did try upgrading to 3.2.19 and had the same results.

If I use the ignore_unknown_conditions=false, the web interface behaves the same. The console results are different though:

Comment.search(:created_after=>'2014-01-01').result.to_sql
=> "SELECT \"comments\".* FROM \"comments\" WHERE (created_at>'2014-01-01')"

vs

User.search(:comments_created_after=>'2014-01-01').result.to_sql
ArgumentError: Invalid search term comments_created_after
from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/ransack-1.3.0/lib/ransack/search.rb:40:in block in build' from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/ransack-1.3.0/lib/ransack/search.rb:32:ineach'
from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/ransack-1.3.0/lib/ransack/search.rb:32:in build' from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/ransack-1.3.0/lib/ransack/search.rb:24:ininitialize'
from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/ransack-1.3.0/lib/ransack/adapters/active_record/base.rb:16:in new' from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/ransack-1.3.0/lib/ransack/adapters/active_record/base.rb:16:inransack'
from (irb):2
from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-3.2.17/lib/rails/commands/console.rb:47:in start' from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-3.2.17/lib/rails/commands/console.rb:8:instart'
from /home/brock/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/railties-3.2.17/lib/rails/commands.rb:41:in <top (required)>' from script/rails:6:inrequire'
from script/rails:6:in `

'

which seems to confirm that Ransack is not seeing that scope as an available condition when accessed via an associated model.

I appreciate everyone's feedback and looking into this issue.

Aha, I only thought this was working because we have this functionality in our app, but we've worked around it by defining our own ransackers and a formatter that changes "attributes" to "scopes"... all dirty hacks before I worked on the ransackable_scopes extraction.

So I was confused. ransackable_scopes only supports the base model you're searching on. If you want to search scopes on associated models, it gets more complex. I'll see about extracting what we have into a more general solution.

You can currently work around it by defining the scope on the parent model:

User.scope :with_comments_since, ->(date) { 
  joins(:comments).merge(Comment.created_after(date))
}

I don't know why it is doing a left outer join.

Because some conditions require it, like IS NULL. (Part of our internal hacks includes using subqueries for these things to make sure there's no crossed wires with multiple joins.)

OK that's a good idea, I got my particular case working by moving the scope up to the parent model. Thanks for the suggestion!

Great to see this clarified and a solution found. Thanks again @avit. Would you like to add your suggestion to the Scopes section of the README? Something along the lines of saying that searching through scopes on the associated model isn't yet possible and for now you'll need to define the association scope on the parent model. I've been intending to add more documentation about the new scopes because they are not well-documented yet, and this would be useful for people to know.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MatsumotoHiroko picture MatsumotoHiroko  路  4Comments

ivanovaleksey picture ivanovaleksey  路  3Comments

grillorafael picture grillorafael  路  5Comments

JFimex picture JFimex  路  3Comments

timoschilling picture timoschilling  路  5Comments