will_paginate with named_scopes
Asked Answered
O

4

16

I'm using will_paginate for pagination, which has been working well so far, except for this one thing.

If I try to paginate a scope, for instance

class User < ActiveRecord::Base

    named_scope :scope, lambda { etc }

end

User.scope.paginate({:page => params[:page], :per_page => 10})

That will tell me paginate is an undefined method. I'd rather not have to use a second solution for only this scope, is there something I can do here?

Organdy answered 7/5, 2010 at 22:45 Comment(0)
P
9

Lowgain, klew's version should work out of the box. In your version you should write:

User.scope.paginate :page => params[:page], :per_page => 10

I prefer another approach to pagination. It allows to make controller more clean and encapsulates pagination at model level, for e.g.:

class Property < ActiveRecord::Base
  named_scope :all_properties, lambda {{ :order => "name asc" }}

  def self.admin_properties(page = 1)
    self.all_properties.paginate(:page => page, :per_page => Settings.admin.per_page)
  end
end

And in a controller pretty clear code:

class Admin::PropertiesController < Admin::AdminController
  def index
    @properties = Property.admin_properties(params[:page])
  end
end

p.s: Settings.admin.per_page - this is Searchlogic settings.

Pskov answered 9/5, 2010 at 8:38 Comment(2)
Hmm, this is interesting! Is there any kind of noticeable performance drawback to doing it this way, as it is paginating every property?Organdy
How can it paginate every property? :) Not every, but all. Named scope all_properties is a kind of class method (not instance!). So when we call self.all_properties, we receive all properties from this named scope. Then we paginate them. Self means class Property itself - not object of class Property.Pskov
C
2

I have a named_scope defined like this:

 named_scope :look_for, lambda { |search| bla;bla;bla }

I call it:

 Person.look_for(params[:search]).paginate :page => params[:page]

And it works. Maybe you need some parameter to your scope?

Castro answered 7/5, 2010 at 23:9 Comment(2)
hmm, what kind of parameter would you expect? the scope does appear to work outside of being paginatedOrgandy
If your scope takes parameter, then you should give one. If not, then don't ;). My example is with parameter.Castro
O
2

Kind of a weird solution, but

User.scope.find(:all).paginate :page => params[:page], :per_page => 10

works?

Organdy answered 7/5, 2010 at 23:58 Comment(0)
N
1

Lowgain, it doesn't sound like you are, but just to make sure -- you aren't actually doing tests with a named_scope named scope right? Because scope is an existing method and using that as your scope name causes an error (and an infinite loop).

EDIT:

Does your named_scope happen to include a :limit clause? I just started having a similar problem. I have a model Response which belongs_to User, with a named scope something like this:

named_scope :top, lambda { |limit| {
            :limit => limit,
            :order => 'total_score DESC' }}

And I am seeing results in the console such as this:

?> u = User.find 1
?> u.responses.length
=> 9
?> u.responses.paginate(:page => 1, :per_page => 5).length
=> 5
?> u.responses.top(3).length
=> 3
?> u.responses.top(3).paginate(:page => 1, :per_page => 5).length
=> 5

Yikes! How can my top 3 paginate to produce more than 3 rows? Per your example I tried your find(:all) trick with similar results:

?> u.responses.top(3).find(:all).paginate(:page => 1, :per_page => 5).length
=> 3

This looks like a bug in named_scope, because I can take will_paginate out of the picture and get similar mayhem to occur:

?> u.responses.top(3).length
=> 3
?> u.responses.top(3).size
=> 9                       <-- .size produces wrong answer
?> r = u.responses.top(3)
?> r.size
=> 3                       <-- correct when result assigned to var

So far, this only appears to happen when I am using MySQL. I think I read another post on StackOverflow where someone had a similar problem using .size with AR results and MySQL, and the solution was to always use .length on their AR results. I have tried modifying will_paginate to replace all instances of .size with .length, but alas it was not that simple, but I suspect that this or a similar issue is somehow affecting will_paginate.

For the time being, I am using your find(:all) trick to hack around this.

Neper answered 15/5, 2010 at 15:58 Comment(1)
Just realized I blogged about this same thing 2 months ago, forgot about it, then "rediscovered" it today. I hate becoming senile!Neper

© 2022 - 2024 — McMap. All rights reserved.