#35637: Paginator executes additional SQL query when using QuerySet -------------------------------------+------------------------------------- Reporter: amir-mahdih86 | Type: | Uncategorized Status: new | Component: Database | layer (models, ORM) Version: 5.0 | Severity: Normal Keywords: Paginator, | Triage Stage: Queryset, Performance, SQL | Unreviewed Queries | Has patch: 1 | Needs documentation: 0 Needs tests: 0 | Patch needs improvement: 0 Easy pickings: 0 | UI/UX: 0 -------------------------------------+------------------------------------- When using Django's `Paginator` class with a `QuerySet`, it executes an additional SQL count query to determine the total count of items. This does not happen when a `list` is passed to the `Paginator`.
**Steps to Reproduce:** 1. Create a view that uses `Paginator` with a `QuerySet` object. 2. Compare the SQL queries executed with those executed when a `list` is used instead. **Expected Behavior:** The `Paginator` shouldn't execute an additional query to count a QuerySet's items while it can use `len()` to do that. **Actual Behavior:** An additional query is executed when a `QuerySet` is passed while it's possible to convert `QuerySet` to a `list` before passing it to `Paginator` with no difference in returned object but 1 less database query execution. **Example Code:** {{{ # This code is simplified to be smaller # This bellow view executes 3 SQL queries class PostList(APIView): def get(self, request): posts = Post.objects.filter(is_published=True) paginator = Paginator(posts, 10) posts = paginator.page(1) serializer = PostSerializer(posts, many=True) return Response(serializer.data) # But this one executes 2 SQL queries class PostList(APIView): def get(self, request): posts = list(Post.objects.filter(is_published=True)) paginator = Paginator(posts, 10) posts = paginator.page(1) serializer = PostSerializer(posts, many=True) return Response(serializer.data) }}} **My Solution:** A simple solution is to modify the `__init__` method of the `Paginator` class to convert `object_list` to a `list` object. This is just a simple solution and it may be necessary to enhance the problem in a deeper layer. Here is the modified version of that method: {{{ def __init__( self, object_list, per_page, orphans=0, allow_empty_first_page=True, error_messages=None, ): self.object_list = list(object_list) self._check_object_list_is_ordered() self.per_page = int(per_page) self.orphans = int(orphans) self.allow_empty_first_page = allow_empty_first_page self.error_messages = ( self.default_error_messages if error_messages is None else self.default_error_messages | error_messages ) }}} -- Ticket URL: <https://code.djangoproject.com/ticket/35637> Django <https://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-updates+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/01070190f381df72-16d1f284-e429-4402-9535-fc859ef4ffa1-000000%40eu-central-1.amazonses.com.