Search code examples
pythondjangodjango-pagination

Django Pagination - re group and show group of times per page?


Im currently regrouping some data and then displaying that data in columns whilst also using pagination. However, due to the ordering of the origin data, there are instances where column 1 has 1 entry in but actually has 5 or 6 total entries, but they would be on different pages, is it possible to regroup the data so that the number of items per page is taken by the number of grouped items?

I thought about sorting the data before posting it to the page but then that will only show the one column of types per page (there could be 1 or n types/columns that id like to have on one page)

note: the sample data below is not actual data,

The below example hopefully will make things clearer

orgin data:

  ID       Type      Item
----------------------------
  9        Fruit     Apple    
  15       Meat      Beef    
  18       Fruit     Lemon    
  99       Fruit     Orange   
  9        Fruit     Grape    
  77       Meat      Chicken    

Paginated and grouped data current output

 Meat          Fruit
-------       -------
 Beef          Apple
               Lemon

page 1 of 2 (3 items per page)

 Meat          Fruit
-------       -------
 Chicken       Orange
               Grape

page 2 of 2 (3 items per page)

Desired Output

 Meat          Fruit
-------       -------
 Beef          Apple
Chicken        Lemon
               Orange

page 1 of 2 (3 items per group per page)

 Meat          Fruit
-------       -------
               Grape

page 2 of 2 (3 items per group per page)

This is my current template:

{% include 'search/page_nav.html' %}
{% with results.object_list as results_list %}
    {% regroup results_list|dictsort:"model_name" by model_name as grouped_objects %}
    {% for ct in grouped_objects %}
        <div class="col-4">
            <h3 class="text-capitalize">{{ ct.grouper }}'s</h3>
            {% for result in ct.list %}
            <div class="col-12 results">
                <div class="pt-4 border-bottom">
                    <a class="page-url h4 text-primary" href="{{ result.object.get_absolute_url }}">{{ result.object.get_search_title }}</a>
                    <p class="page-description mt-1 text-muted"> {{ result.object.get_search_text|safe }}</p>
                </div>
            </div>
            {% endfor %}
        </div>
    {% empty %}
        <p class="lead">Although I am a guru, I am lacking sentience. One must ask a specific question to recieve a specific answer.</p>
    {% endfor %}
{% endwith %}
{% include 'search/page_nav.html' %}

and the pagination template:

{% if results.has_previous or results.has_next %}
    <nav class="col-12" aria-label="Page navigation">
        <ul class="pagination mb-2">
            <li class="page-item {% if not results.has_previous %} disabled {% endif %}"><a class="page-link" href="{% if results.has_previous %}?q={{ query }}&amp;page={{ results.previous_page_number }}{% endif %}">Previous</a>
            {% if results.number|add:'-5' > 1 %}
                <li class="page-item active}"><a class="page-link" href="?q={{ query }}&amp;page={{ results.number|add:'-5' }}">&hellip;</a></li>
            {% endif %}
            {% for i in results.paginator.page_range %}
                {% if results.number == i %}
                <li class="page-item active"><a class="page-link" href="?q={{ query }}&amp;page={{ i }}">{{ i }}</a></li>
                {% elif i > results.number|add:'-6' and i < results.number|add:'6' %}
                <li class="page-item"><a class="page-link" href="?q={{ query }}&amp;page={{ i }}">{{ i }}</a></li>
                {% endif %}
            {% endfor %}
            {% if results.paginator.num_pages > results.number|add:'5' %}
                <li class="page-item active}"><a class="page-link" href="?q={{ query }}&amp;page={{ results.number|add:'5' }}">&hellip;</a></li>
            {% endif %}                                    
            <li class="page-item {% if not results.has_next %} disabled {% endif %}"><a class="page-link" href="{% if results.has_next %}?q={{ query }}&amp;page={{ results.next_page_number }}{% endif %}">Next</a>
            <li class="page-item" style="padding: 0.3rem 0.75rem">(Page {{ results.number }} of {{ results.paginator.num_pages }})</li>
        </ul>
    </nav>
{% endif %}

Edit:

the view is a haystack query code below:

from haystack.query import SearchQuerySet
from haystack.inputs import AutoQuery, Exact, Clean
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
sqs = SearchQuerySet().filter(content=AutoQuery(query))
# create the pagination
paginator = Paginator(sqs, 10)
page = request.GET.get('page')
try:
    results = paginator.page(page)
except PageNotAnInteger:
    results = paginator.page(1)
except EmptyPage:
    results = paginator.page(paginator.num_pages)

SQS object returned:

>>> sqs
<SearchQuerySet: query=<haystack.backends.elasticsearch5_backend.Elasticsearch5SearchQuery object at 0x7ff0e549f5f8>, using=None>
>>> vars(sqs[0])
{'app_label': 'config', 'model_name': 'devicecircuitsubnets', 'pk': '6788', 'score': 8.327476, '_object': None, '_model': None, '_verbose_name': None, '_additional_fields': ['id', 'text'], '_point_of_origin': None, '_distance': None, 'stored_fields': None, 'log': <haystack.utils.log.LoggingFacade object at 0x7ff0e525e7f0>, 'id': 'config.devicecircuitsubnets.6788', 'text': 'WAN-EDGE\nBarb\n\n\n\n\n102.155.156.2\nRouted Link\n\n'}
>>>
>>> sqs.count()
44

so the first regroup would be done with model_name and score, which should create x amount of model_groups with x amount of results in each of them, and these results should be paginated... hopefully this all makes sense?

EDIT:

here's the full output of a search and then the object list after pagination

>>> from haystack.query import SearchQuerySet
>>> from haystack.inputs import AutoQuery, Exact, Clean
>>> from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
>>> query ='edge'
>>> sqs = SearchQuerySet().filter(content=AutoQuery(query))
>>> paginator = Paginator(sqs, 10)
>>> page = 1
>>> results = paginator.page(page)
>>> results
<Page 1 of 5>
>>> var(results)
>>> vars(results)
{'object_list': [<SearchResult: config.devicecircuitsubnets (pk='6788')>, <SearchResult: config.devicecircuitsubnets (pk='6992')>, <SearchResult: config.devicecircuitsubnets (pk='7276')>, <SearchResult: config.device (pk='5')>, <SearchResult: config.devicecircuitsubnets (pk='694')>, <SearchResult: config.devicecircuitsubnets (pk='702')>, <SearchResult: config.devicecircuitsubnets (pk='7405')>, <SearchResult: config.devicecircuitsubnets (pk='695')>, <SearchResult: config.devicecircuitsubnets (pk='700')>, <SearchResult: config.devicecircuitsubnets (pk='1804')>], 'number': 1, 'paginator': <django.core.paginator.Paginator object at 0x7f30517037f0>}
>>> results.object_list
[<SearchResult: config.devicecircuitsubnets (pk='6788')>, <SearchResult: config.devicecircuitsubnets (pk='6992')>, <SearchResult: config.devicecircuitsubnets (pk='7276')>, <SearchResult: config.device (pk='5')>, <SearchResult: config.devicecircuitsubnets (pk='694')>, <SearchResult: config.devicecircuitsubnets (pk='702')>, <SearchResult: config.devicecircuitsubnets (pk='7405')>, <SearchResult: config.devicecircuitsubnets (pk='695')>, <SearchResult: config.devicecircuitsubnets (pk='700')>, <SearchResult: config.devicecircuitsubnets (pk='1804')>]
>>> vars(results.object_list[0])
{'app_label': 'config', 'model_name': 'devicecircuitsubnets', 'pk': '6788', 'score': 8.327476, '_object': None, '_model': None, '_verbose_name': None, '_additional_fields': ['id', 'text'], '_point_of_origin': None, '_distance': None, 'stored_fields': None, 'log': <haystack.utils.log.LoggingFacade object at 0x7f3050c83ba8>, 'id': 'config.devicecircuitsubnets.6788', 'text': 'WAN-EDGE\nBarb\n\n\n\n\n10.1.1.1\nRouted Link\n\n'}
>>>

Thanks


Solution

  • I ended up just posting the sqs query to the Django template sorted and then regrouping and using query to paginate each column.

    sample below:

    <div class="col-lg-12">
        <div class="row">
        {% if query %}
            {% regroup results|dictsort:"model_name" by model_name as grouped_objects %}
            {% for ct in grouped_objects %}
                <div class="col-6 col-lg-3">
                    <h3 class="text-capitalize">{{ ct.grouper|friendly_search_name }}</h3>
                    <ul id="pager_{{ ct.grouper }}" class="pagination-sm mt-1"></ul>
                    <div id="pager_content_{{ ct.grouper }}">
                        {% for result in ct.list %}
                        <div class="col-12 results item">
                            <div class="pt-4 border-bottom">
                                <a class="page-url h4 text-primary" href="{{ result.object.get_absolute_url }}">{{ result.object.get_search_title }}</a>
                                <p class="page-description mt-1 text-muted"> {{ result.object.get_search_text|safe }}</p>
                            </div>
                        </div>
                        {% endfor %}
                    </div>
                </div>
            {% empty %}
                <p class="lead">Although I am a guru, I am lacking sentience. One must ask a specific question to recieve a specific answer.</p>
            {% endfor %}
        {% else %}
            {# Show some example queries to run, maybe query syntax, something else? #}
        {% endif %}
        </div>
    </div>
    

    jquery:

    {% block extrajs %}
    <script src="{% static 'scripts/twbs-pagingation/jquery.twbsPagination.js' %}" type="text/javascript"></script>
    <script type="text/javascript">
    window.jQuery(function(){
        var items_per_page = 4;
        function hide_all(child) {
            child.removeClass('active');   
        }
        function show_page(child, num, items_per_page) {
            hide_all(child);
            child.slice((num-1)*items_per_page, num*items_per_page).addClass('active');
        }
    {% regroup results|dictsort:"model_name" by model_name as grouped_objects %}
    {% for ct in grouped_objects %}   
        var $content_{{ ct.grouper }} = $('#pager_content_{{ ct.grouper }}');
        var $child_{{ ct.grouper }} = $content_{{ ct.grouper }}.children();
        $('#pager_{{ ct.grouper }}').twbsPagination({
            totalPages: Math.ceil($child_{{ ct.grouper }}.length/items_per_page),
            visiblePages: 5,
            onPageClick: function (event, page) {
                show_page($child_{{ ct.grouper }}, page, items_per_page);
            }
        });
    {% endfor %}
    });
    </script>
    {% endblock extrajs %}
    {% block extrastyle %}
    <style type="text/css">
    .item {
        display: none;
    }
    .item.active {
        display: block;   
    }
    </style>
    {% endblock extrastyle %}