djangoのListViewで検索結果をページングするときのあれこれ

適当に作ったサンプル

from django.views.generic import ListView
from items.forms import ItemSearchForm
from items.models import Item


class ItemListView(ListView):
    model = Item
    paginate_by = 10

    def __init__(self, **kwargs):
        super(ItemListView, self).__init__(**kwargs)
        self.form = None

    def get_queryset(self):
        qs = super(ItemListView, self).get_queryset()
        self.form = ItemSearchForm(self.request.GET or None)
        if self.form.is_valid() and self.form.cleaned_data.get('name'):
            qs = qs.filter(name__contains=self.form.cleaned_data.get('name'))
        return qs
<h2>ItemList</h2>

<form>
    {{ form }}
    <input type="submit" value="search">
</form>

<ul>
    {% for obj in page_obj %}
        <li>{{ obj.name }}</li>
    {% endfor %}
</ul>
<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>

これだと絞り込んだ結果をページングしようとすると、queryに検索条件が引き継がれてないため、2ページは全件の2ページ目になってしまう。

1ページ目

2ページ目


この辺をうまくやってくれるライブラリをどっかでみたような記憶があるんだけど思い出せない。

とはいえ、request.GET.urlencode()をcontextに渡しておけばそれでよさそう。

from django.views.generic import ListView
from items.forms import ItemSearchForm
from items.models import Item


class ItemListView(ListView):
    model = Item
    paginate_by = 10

    def __init__(self, **kwargs):
        super(ItemListView, self).__init__(**kwargs)
        self.form = None

    def get_queryset(self):
        qs = super(ItemListView, self).get_queryset()
        self.form = ItemSearchForm(self.request.GET or None)
        if self.form.is_valid() and self.form.cleaned_data.get('name'):
            qs = qs.filter(name__contains=self.form.cleaned_data.get('name'))
        return qs

    def get_context_data(self, **kwargs):
        ctx = super(ItemListView, self).get_context_data(**kwargs)
        ctx.update(dict(form=self.form,
                        query_string=self.request.GET.urlencode()))
        return ctx
<h2>ItemList</h2>

<form>
    {{ form }}
    <input type="submit" value="search">
</form>

<ul>
    {% for obj in page_obj %}
        <li>{{ obj.name }}</li>
    {% endfor %}
</ul>
<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?{{ query_string }}&page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
            <a href="?{{ query_string }}&page={{ page_obj.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>