Rails kaminari: How to specify total_count for paginate? - ruby-on-rails

COUNT query to find out total_count sometimes very slow.
But if I am caching values of items_amount or such field in model I want to use it for kaminari pagination.
EXAMPLE: I have model
Category (:id, :title, :items_amount)
And then on show view I want to
<%= paginate #items, total_count: #category.items_amount %>
So like paginate_array, but in paginate. It is very useful for perfomance.
Is there a feature like this or how I can add it?
Like will_paginate gem, where we can do it like this
.paginate(page: params[:page], per_page: 72, total_entries: #category.items_amount)

Kaminari just calls the total_count method on #items in order to get the count. By default this does the SELECT count(*) ... query but you could decorate #items to be something that provides your cached count.
If you are just generating page links though then paginate will take a total_pages option. By providing the page count it means that the paginate call doesn't need to get the total count. (I can't find that option explicitly documented but it is in the code and I've tested using it).
paginate #items, total_pages: #cached_page_count
#cache_page_count might have a value like (#category.item_amount / 30.0).ceil in order to get the correct page count with 30.0 being your page size.

I think they answer this really well in their gems readme? https://github.com/kaminari/kaminari
You can do this on controller level using paginates_per to set the limit on each page and max_paginates_per to set the limit of pages?

Related

method "where" does not work when switched from will_paginate to pagy gem in rails

My will_paginate code in controller:
#posts = Post.paginate(page: params[:page], per_page: 100).order('created_at DESC')
My pagy code that replaced it:
#pagy, #posts = pagy_countless(Post.order('created_at DESC'), items:5, link_extra: 'class="" style="color:#222222; margin:3px;"')
My view where the problem is:
<% #posts.where(user_id: current_user.friends).each do |post| %>
The error message:
undefined method `where' for #<Array:0x00007f94329d1f70>
Did you mean? when
What am I doing wrong? The previous will_paginate implementation worked in terms of showing all the posts of user's friends. I'm lost as to what changed in switching to pagy.
UPDATE:
So for the moment, I've solved this by moving the "where..." logic to controller. So:
#pagy, #posts = pagy_countless(Post.where(user_id: current_user.friends).order('created_at DESC'), items:5, link_extra: 'class="" style="color:#222222; margin:3px;"')
And now it works as before. But I'm still confused as to why I had to do this.
Be aware pagy_countless returns an array of items, instead of an ActiveRecord collection. That's why you can't use ActiveRecord methods on #posts (like where).
Follow my link to see that collection is transformed into array using to_a and then it is returned as second argument of pagy_countless: https://github.com/ddnexus/pagy/blob/bd88866ad6001ae1ef6ce63080d36ecff7bc5603/lib/pagy/extras/countless.rb#L29
Two comments:
1. You must use ActiveRecord filters before calling pagy_countless on your collection (eg: where clauses)
2. If you need to filter records after pagination (which does not make sense in most of the cases), you must use Array methods like select of reject as following #posts.select { |record| record.user.in?(current_user.friends) }

Limit number of json results - Rails API

I am creating Rails API and when I do get request to mywebsite.com/posts/all it shows me all the post data. I am trying to do mywebsite.com/posts/all?limit=20 to show 20 results or some other number. What is the way to achieve it with rails?
Limit will not work, each time if u call this method it will return only last 20 records,
Best way to achieve this result using pagination, here you can limit number record passing when each request is hitting
You can use gem will_paginate
In controller
#posts = Post.paginate(:page => params[:page], :per_page => 20)
In the view
<%= will_paginate #posts %>
For info about will_paginate
https://github.com/mislav/will_paginate/blob/master/README.md
If u r using API pass params ass below
GET /v1/posts?page=1&per_page=10
Change value in controller
#posts = Post.paginate(page: params[:page], per_page: params[:per_page])
Easy way for this is
#posts = Post.limit(params[:limit])
But you could also use will_paginate gem

Going Through Users in Database Individually

I have been searching and searching for help on this question, but I have found no luck. I am wanting to have a page that displays each user in my database one at a time, and when I press a 'Next' button, it will go to the next user in the database and display their information. I have been seeing this code everywhere:
<% #users.each do |user| %>
<li>
# Add some code
</li>
<% end %>
but this is displaying every user all at once on my page. I would like to only display a single user at a time. If anyone could direct me to a good source or has a simple explanation, I would be forever grateful!
You just need to paginate the #users records. I prefer using will_paginate gem.
After including it in your gem file, you may use it like:
#users = User.paginate(:page => 1, :per_page => 1)
For basic understanding, the gem basically uses limit and offset in SQL query to attain the record. If you don't want to use the gem, then you may try like:
User.limit(1).offset(params[:offset].present? ? params[:offset] : 0)
And on clicking on next you may pass the incremented value of offset each time as parameter.
have a look at the kaminari gem. You will be able to paginate your results one by one (one result per page, hence the name).

How to use pagination in rails 4.2.5 and also have the ability to reorder the list with ajax?

I'm working on an forum-type app in Rails v 4.2.5. My index page is a list of all the questions being discussed in the application and they are default sorted by the created_at date. I am also using the Kaminari gem to paginate all of the questions (25 per page). I originally had my app set up like this:
Questions Controller:
def index
#questions = Question.order(:created_at).page params[:page]
end
Index View:
# I render a partial that iterates through the questions list to display
# the title of the questions, then I include the paginate code below.
<div class="pagination">
<%= paginate #questions %>
</div>
I eventually decided I wanted users to be able to sort the questions by different criteria (e.g., by total amount of upvotes, by total amount of responses for a question, and by recently asked questions). Right now, you can click a link corresponding to the type of sort you want and it will AJAX the new sorted list (a partial) onto the page. However, when I do this, the pagination does not work and when I click to see the second page of the results, everything becomes unsorted.
Index View with Sort Links:
<div class="sort_selection">
<h3> Sort By: </h3>
<%= link_to "By Upvotes", "/questions/top?sort=votes", class: "question_sort_link" %>
<%= link_to "Answers Provided", "/questions/top?sort=answers", class: "question_sort_link" %>
<%= link_to "Recently Asked", "/questions/top?sort=recent", class: "question_sort_link" %>
</div>
Index Controller:
def top
case params[:sort]
when "votes"
#questions = Question.sort_by_votes #sort_by_votes is a method in my Question model that performs a SQL query
when "answers"
#questions = Question.where.not(answers_count: nil).order(answers_count: :desc).limit(25)
when "recent"
#questions = Question.order(created_at: :desc).limit(25)
end
render partial: 'questions_list', layout: false
end
Javascript AJAX
$(document).on("click", ".question_sort_link", function(event){
event.preventDefault();
$.ajax({
method: "get",
url: $(this).attr("href")
}).done(function(sorted){
$('.questions_show_sorted').replaceWith(sorted);
});
});
I fooled around with the placement of the <%= paginate #questions %> in the view, as well as removed the 25 limit in my controller and added .page params[:page] after all of the queries in the Top route but I still cannot get the pagination to work after I've AJAX'ed a sorted list onto the page. Does anyone have any suggestions?
When you are switching pages the data about sorting is lost, because you are reloading site with different parameters. You can either try to pass this data (the column you are going to sort, and info is it asc or desc) to the new page, and sort it before loading, or paginate it using AJAX (but that means loading everything at the first load). I can't tell about the "pagination does not work problem", because I don't know what you mean.
In general, this thing you are trying to do is rather complicated, and there is no simple solution for that. There is a library for JS called Datatables that (in theory) makes it easier. There is another library called jQ-Bootgrid, and a ruby gem called "Smart listing".
I think you need to provide the pagination links in your ajax response and replace them in your javascript callback.
I assume that you return html rather than json, which will make this a bit awkward. Perhaps you could build up a json response with pagination links and html content
{
next: /list?page=3,
prev: /list?page=1,
content: "<ul>
<li>foo</li>
<li>bar</li>
</ul>"
}

How to obtain per_page number of a Model in views?

The number of items for a page in Kaminari is defined by:
defining per_page member in model, or
defining default_per_page in config.
My question is, how to obtain that number of items in view?
Update
Pardon me for being not clear. What I want is to show is the order of item in each page. I have a table header:
<th>#</th>
And several columns below:
<td><%= (#sales.current_page - 1) * some_ruby_code + index + 1 %></td>
My question is what some_ruby_code should be?
1) If I substitute some_ruby_code with Sale.per_page, it will throw error when I decided to delete per_page.
2) If I substitute some_ruby_code with Kaminari.config.default_per_page, it won't show the proper value when I add per_page in model.
I want to know whether there are a method in Kaminari which detects the existence of per_page, return its value if exists, or Kaminari.config.default_per_page otherwise.
Thank you!
You can use Sale.default_per_page.
See https://github.com/kaminari/kaminari/blob/1-0-stable/kaminari-core/lib/kaminari/models/configuration_methods.rb#L16
You can use ActiveRecord's limit_value method for this, since Kaminari internally uses limit and offset.
User.all.limit(10).limit_value
=> 10
With kaminari is the same:
User.page(3).per(10).limit_value
=> 10
User.page(3).per(10).offset_value
=> 20
I guess, you want to display the number of items visible out of the total,
if so you can use
<%= page_entries_info #items %>
Read this documentation on github:
https://github.com/amatsuda/kaminari
Assuming you actually have the items for that page being rendered in the view, this should work:
#items.length
current_per_page returns of given list of paged records.

Resources