---
slug: "複雑なクエリで検索したDjangoモデルインスタンスをPaginatorでページングする"
title: "Paging Django Model Instances Queried with Complex Filters Using Paginator"
description: "Page through Django model instances retrieved via a complex query (raw SQL or composed Q objects) using Paginator, with a working implementation pattern."
url: "https://www.ytyng.com/en/blog/複雑なクエリで検索したDjangoモデルインスタンスをPaginatorでページングする"
publish_date: "2015-10-15T10:10:34Z"
created: "2015-10-15T10:10:34Z"
updated: "2026-05-11T13:05:50.875Z"
categories: ["Django"]
keywords: ""
featured_image_url: "https://media.ytyng.com/resize/20230812/29a0edf0c96b4f4890196c7eb0ff1a88.png.webp?width=768"
has_video: false
has_music: false
video_urls: []
music_urls: []
lang: "en"
---

# Paging Django Model Instances Queried with Complex Filters Using Paginator

<div class="document">

<pre class="literal-block">class Content(models.Model):
    content_name = models.CharField(...)
    group_id = models.PositiveIntegerField(...)
    volume_number = models.PositiveIntegerField(...)
    ...
</pre>

<p>Assuming we have a typical Django model class, we want to search for its instances using a complex SQL query in a single shot. The results should be displayed on a web page, but since many rows are expected, we want to display a paginator.</p>
<p>For instance, in the above model, we might want to list only the instances with the maximum volume_number within the same group_id.</p>
<p>We will use the general django.core.paginator.Paginator for pagination.</p>

<div class="section" id="paginator">
<h3>Review of Basic Paginator Usage</h3>

<pre class="literal-block">contents = Content.objects.filter(...)
paginator = Paginator(contents, 100)
</pre>

<p>By putting a queryset into the first argument, you can paginate it. A list would also work.</p>
<p>Looking at the Paginator code, you can see that it works if slicing and .count() are implemented. .count can also be __len__.</p>
<p>Therefore, you can wrap the list of instances extracted using complex SQL (which cannot be made into a queryset) in a custom class instance that implements slicing and .count(). Slicing has a "step" feature, but it's cumbersome so we won't implement the step.
(Step: hoge_sequence[0:100:2] ← the third number in this slice)</p>
</div>

<div class="section" id="id1">
<h3>Implementation Code</h3>

<p>Model Class</p>
<pre class="literal-block">class Content(models.Model):
    content_name = models.CharField(...)
    group_id = models.PositiveIntegerField(...)
    volume_number = models.PositiveIntegerField(...)
    ...

    @classmethod
    def last_volumes(cls, limit=100, offset=0):
        """
        Method for searching with raw SQL if absolutely necessary
        Uses objects.raw() to convert to model instances.
        The SQL is a bit peculiar, but don't mind it.
        """
        sql = """SELECT t1.*
FROM (
  SELECT *
  FROM content_content
  ORDER BY volume_number DESC
) t1
GROUP BY group_id
LIMIT %s OFFSET %s
"""
        return cls.objects.raw(sql, params=[limit, offset])
</pre>

<p>Class for Paginator</p>
<pre class="literal-block">class LastContents(object):
    """
    Class to be used with the Paginator.
    Implements slicing and count()
    """

    def __getitem__(self, key):
        if isinstance(key, slice):
            # In case of slicing (e.g., instance[0:100])
            # Ignore the step!
            limit = key.stop - key.start
            return Content.last_volumes(
                limit=limit, offset=key.start)
       # In case of getting a single element (e.g., instance[50])
       # Not used this time, but
       return Content.last_volumes(limit=1, offset=key)[0]

    def count(self):
        # Implement a method to get the total count
        return Content.objects.filter(volume_number=1).count()
</pre>

<p>View Function</p>
<pre class="literal-block"># Put the instance of LastContents we just created!
paginator = Paginator(LastContents(), 100)

# You can paginate.
page = paginator.page(page_number)

page.object_list ...
</pre>
</div>
</div>
