Repository: encode/django-rest-framework

Issue:
Paginators always repeat the query even if the count is 0
## Checklist
- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [ ] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.) - This can be done with a third party library, but I feel that it's a sensible improvement.
- [x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.) - I haven't, since I'm not sure this is considered a bug/desired feature.
## Steps to reproduce
- Add a paginated view for an empty model (Only applies to LimitOffsetPagination)
- Make a GET request.
## Expected behavior
- If the count query returns 0, the actual query shouldn't be re-run
## Actual behavior
- The query is re-run.

This isn't a problem in the case I described, but in some cases the query can get quite complex and slow. Even if the view isn't wrapped in a transaction and it is possible to get rows on the second run, the results will be inconsistent, but the maybe expensive query is re-run.



Discussion:
Do you have an analysis in regard to where the queryset is re-evaluated ?

It depends on the paginator, but, for LimitOffsetPagination it's [rest_framework/pagination.py:307](https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/pagination.py#L307) (first one is line 303->53). For CursorPagination, this isn't an issue, since it doesn't do the count. For PageNumberPagination, this is done by [DjangoPaginator](https://github.com/django/django/blob/master/django/core/paginator.py#L61) (first one is 59->78).

While Django behaves the same, that's a separate issue and I will probably put in a ticket there as well. My main concern is whether this is a behaviour that seems sensible (it does to me).

> For PageNumberPagination, this is done by DjangoPaginator (first one is 59->78).

I've actually went and tested that, and it's not true. Django has an extra bit which [caps](https://github.com/django/django/blob/master/django/core/paginator.py#L59) the upper limit to the count and in qs.query.set_limits, if low and high are equal, it returns an empty QS, without going to the database.

So, I guess that answers my question whether this is intended behaviour or not, I'll make a PR tonight when I get home to unify the behaviour.


Resolving patch:
diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py
--- a/rest_framework/pagination.py
+++ b/rest_framework/pagination.py
@@ -312,6 +312,9 @@ def paginate_queryset(self, queryset, request, view=None):
         self.request = request
         if self.count > self.limit and self.template is not None:
             self.display_page_controls = True
+
+        if self.count == 0 or self.offset > self.count:
+            return []
         return list(queryset[self.offset:self.offset + self.limit])
 
     def get_paginated_response(self, data):


Test patch: