I was wondering if someone could explain how to use will_paginate on an array of objects?
For example, on my site I have an opinion section where users can rate the opinions. Here's a method I wrote to gather the users who have rated the opinion:
def agree_list
list = OpinionRating.find_all_by_opinion_id(params[:id])
@agree_list = []
list.each do |r|
user = Profile.find(r.profi开发者_StackOverflow中文版le_id)
@agree_list << user
end
end
Thank you
will_paginate 3.0 is designed to take advantage of the new ActiveRecord::Relation
in Rails 3, so it defines paginate
only on relations by default. It can still work with an array, but you have to tell rails to require that part.
In a file in your config/initializers
(I used will_paginate_array_fix.rb
), add this
require 'will_paginate/array'
Then you can use on arrays
my_array.paginate(:page => x, :per_page => y)
You could use Array#from
to simulate pagination, but the real problem here is that you shouldn't be using Array
at all.
This is what ActiveRecord Associations are made for. You should read that guide carefully, there is a lot of useful stuff you will need to know if you're developing Rails applications.
Let me show you a better way of doing the same thing:
class Profile < ActiveRecord::Base
has_many :opinion_ratings
has_many :opinions, :through => :opinion_ratings
end
class Opinion < ActiveRecord::Base
has_many :opinion_ratings
end
class OpinionRating < ActiveRecord::Base
belongs_to :opinion
belongs_to :profile
end
It's important that your database schema is following the proper naming conventions or all this will break. Make sure you're creating your tables with Database Migrations instead of doing it by hand.
These associations will create helpers on your models to make searching much easier. Instead of iterating a list of OpinionRatings and collecting the users manually, you can make Rails do this for you with the use of named_scope
or scope
depending on whether you're using Rails 2.3 or 3.0. Since you didn't specify, I'll give both examples. Add this to your OpinionRating class:
2.3
named_scope :for, lambda {|id|
{
:joins => :opinion,
:conditions => {
:opinion => { :id => id }
}
}
}
named_scope :agreed, :conditions => { :agree => true }
named_scope :with_profiles, :includes => :profile
3.0
scope :agreed, where(:agree => true)
def self.for(id)
joins(:opinion).where(:opinion => { :id => id })
end
In either case you can call for(id)
on the OpinionRatings
model and pass it an id:
2.3
@ratings = OpinionRating.agreed.for(params[:id]).with_profiles
@profiles = @ratings.collect(&:profile)
3.0
@ratings = OpinionRating.agreed.for(params[:id]).includes(:profile)
@profiles = @ratings.collect(&:profile)
The upshot of all this is that you can now easily paginate:
@ratings = @ratings.paginate(:page => params[:page])
Update for Rails 4.x: more or less the same:
scope :agreed, ->{ where agreed: true }
def self.for(id)
joins(:opinion).where(opinion: { id: id })
end
Although for newer Rails my preference is kaminari for pagination:
@ratings = @ratings.page(params[:page])
The gem will_paginate
will paginate both ActiveRecord queries and arrays.
list = OpinionRating.where(:opinion_id => params[:id]).includes(:profile).paginate(:page => params[:page])
@agree_list = list.map(&:profile)
If you don't want to use the config file or are having trouble with it, you can also just ensure you return an ActiveRecord::Relation instead of an array. For instance, change the agree_list to be a list of user ids instead, then do an IN on those ids to return a Relation.
def agree_list
list = OpinionRating.find_all_by_opinion_id(params[:id])
@agree_id_list = []
list.each do |r|
user = Profile.find(r.profile_id)
@agree_id_list << user.id
end
@agree_list = User.where(:id => @agree_id_list)
end
This is inefficient from a database perspective, but it's an option for anybody having issues with the will_paginate config file.
I took advantage of rails associations, and came up with a new method:
def agree_list
o = Opinion.find(params[:id])
@agree_list = o.opinion_ratings(:conditions => {:agree => true}, :order => 'created_at DESC').paginate :page => params[:page]
rescue ActiveRecord::RecordNotFound
redirect_to(profile_opinion_path(session[:user]))
end
In my view I looked up the profile like so:
<% @agree_list.each do |rating| %>
<% user = Profile.find(rating.profile_id) %>
<% end %>
Please post up if there's a better way to do this. I tried to use the named_scope helper in the OpinionRating model with no luck. Here's an example of what I tried, but doesn't work:
named_scope :with_profile, lambda {|id| { :joins => [:profile], :conditions => ['profile_id = ?', id] } }
That seemed like the same as using the find method though.
Thanks for all the help.
I am using rails 3 ruby 1.9.2. Also, I am just starting app, so no css or styles included.
Install will_paginate:
gem install will_paginate
Add to Gemfile and run bundle.
Controller
class DashboardController < ApplicationController
include StructHelper
def show
@myData =structHelperGet.paginate(:page => params[:page])
end
end
module StructHelper queries a service, not a database. structHelperGet() returns an array of records.
Not sure if a more sophisticated solution would be to fake a model, or to grab the data every so often and recreate a sqllite table once in a while and have a real model to query. Just creating my first rails app ever.
View
<div id="Data">
<%= will_paginate @myData%>
<table>
<thead>
<tr>
<th>col 1</th>
<th>Col 2</th>
<th>Col 3</th>
<th>Col 4</th>
</tr>
</thead>
</tbody>
<% @myData.each do |app| %>
<tr>
<td><%=app[:col1]%> </td>
<td><%=app[:col2]%> </td>
<td><%=app[:col3]%> </td>
<td><%=app[:col4]%> </td>
</tr>
<% end %>
</tbody>
</table>
<%= will_paginate @myData%>
</div>
This will give you pagnation of the default 30 rows per page.
If you have not read http://railstutorial.org yet, start reading it now.
You can implement pagination even without any gem.I saw this How do I paginate an Array?. Simple implementation in kaminari gems doc. Please see the below example which i got from kaminari gems doc
arr = (1..100).to_a
page, per_page = 1, 10
arr[((page - 1) * per_page)...(page * per_page)] #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
page, per_page = 2, 10
arr[((page - 1) * per_page)...(page * per_page)] #=> [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
精彩评论