Paginator for inline models in django admin

Question:

I have this simple django model consisting of an sensor and values for the specific sensor.
The number of values per Pyranometer is high (>30k). Is it somehow possible to paginate PyranometerValues by a specific day or generell apply a paginator to the admin inline view?

class Pyranometer(models.Model):
     name = models.CharField(max_length=75)                                                                             

class PyranometerValues(models.Model):                                                                                 
    timestamp = models.DateTimeField()
    value = models.DecimalField(max_digits=10,decimal_places=6)                                                        
    sensor = models.ForeignKey('Pyranometer')      
Asked By: Ulrich Dangel

||

Answers:

Have you checked raw_id_fields attribute? I think you might find it useful.

Answered By: Juan

As django-admin is mainly a matter of templates (only needed to redefine templates to i18n some django-admin-tools parts), I have an idea.

The are pagination modules for Django, like linaro-django-pagination or endless-pagination, which provide template tags to paginate anything given it is iterable.

If you could find the template in charge of displaying inline models, you could copy it in your project, then try adding a {% load pagination_tags %} to it and paginate the inlines.

I have not tested it nor thought it very thoroughly but except for validation, I don’t see how it could fail. Just test and tell us.

Answered By: Steve K

Well, maybe dynamically generated filters would help:

https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter

Also, the admin has a nice GET-type query as in:

localhost:8000/admin/pyranometervalues/?value=10.0

You could specify the date as:

admin/pyranometervalues/?timestamp_year=2011&timestamp_month=10&timestamp__day=13

and so on… Unfortunately I don’t know a shorter way to make this query in admin. Any ideas? 🙂

EDIT: this is only for narrowing your query, doesn’t have anything to do with pagination 😉

Answered By: seeg

If anyone requires this, I found this nice (though described as “quite hacky”) implementation of a pagination TabularInline subclass in this comment of a django-suit issue.

For Django 1.6 it requires a template change and subclassing this PaginationInline class:

from django.contrib import admin
from django.contrib.admin.views.main import ChangeList
from django.core.paginator import EmptyPage, InvalidPage, Paginator

class InlineChangeList(object):
    can_show_all = True
    multi_page = True
    get_query_string = ChangeList.__dict__['get_query_string']

    def __init__(self, request, page_num, paginator):
        self.show_all = 'all' in request.GET
        self.page_num = page_num
        self.paginator = paginator
        self.result_count = paginator.count
        self.params = dict(request.GET.items())


class PaginationInline(admin.TabularInline):
    template = 'admin/edit_inline/tabular_paginated.html'
    per_page = 20

    def get_formset(self, request, obj=None, **kwargs):
        formset_class = super(PaginationInline, self).get_formset(
            request, obj, **kwargs)
        class PaginationFormSet(formset_class):
            def __init__(self, *args, **kwargs):
                super(PaginationFormSet, self).__init__(*args, **kwargs)

                qs = self.queryset
                paginator = Paginator(qs, self.per_page)
                try:
                    page_num = int(request.GET.get('p', '0'))
                except ValueError:
                    page_num = 0

                try:
                    page = paginator.page(page_num + 1)
                except (EmptyPage, InvalidPage):
                    page = paginator.page(paginator.num_pages)

                self.cl = InlineChangeList(request, page_num, paginator)
                self.paginator = paginator

                if self.cl.show_all:
                    self._queryset = qs
                else:
                    self._queryset = page.object_list

        PaginationFormSet.per_page = self.per_page
        return PaginationFormSet
Answered By: tutuDajuju

For any version of Django, follow these steps:

  1. Visit this file : python3.7/site-packages/django/contrib/admin/templates/admin/edit_inline/tabular.html make a copy of it in your app/templates/admin/edit_inline/anyname.html

between </table> and </fieldset> tag add in anyname.html:

   <style>
    .dark {
      /*background-color: #417690;*/
      background-color: #FFFFFF;
      border: none;
      color: #666;
      padding: 5px 10px;
      text-align: center;
      text-decoration: none;
      display: inline-block;
      font-size: 12px;
      margin: 4px 2px;
      cursor: pointer;
    }
    .light {
      background-color: #008CBA;
      border: none;
      color: white;
      padding: 5px 10px;
      text-align: center;
      text-decoration: none;
      display: inline-block;
      font-size: 12px;
      margin: 4px 2px;
      cursor: pointer;
    }

  </style>
   <div>
   {% with inline_admin_formset.formset.page as page_obj %}
    <p class="paginator">
      {% if page_obj.previous_page_number > 1 %}
        <a href="?page={{ page_obj.previous_page_number|add:'-1' }}">{% trans 'previous' %}</a>
      {% endif %}

      {% if page_obj.number|add:"-5" > 0 %}
        <a href="?page=0">1</a>
      {% endif %}

      {% if page_obj.number|add:"-5" > 1 %}
        <span>&hellip;</span>
      {% endif %}

      {% for page_num in page_obj.paginator.page_range %}
        {% if page_obj.number == page_num %}
          <span class="dark">{{ page_num|add:"-1" }}</span>
        {% else %}
          {% if page_num > page_obj.number|add:"-5" and page_num < page_obj.number|add:"5" %}
            <a class="light" style="color:white" href="?page={{ page_num|add:'-1' }}">{{ page_num|add:"-1" }}</a>
          {% endif %}
        {% endif %}
      {% endfor %}

      {% if page_obj.number|add:"5" < page_obj.paginator.num_pages %}
        <span>&hellip;</span>
      {% endif %}

      {% if page_obj.number|add:"4" < page_obj.paginator.num_pages %}
        <a href="?page={{ page_obj.paginator.num_pages }}">{{ page_obj.paginator.num_pages }}</a>
      {% endif %}

      {% if page_obj.next_page_number < page_obj.paginator.num_pages|add:'1' %}
        <a href="?page={{ page_obj.next_page_number|add:'-1' }}">{% trans 'next' %}</a>
      {% endif %}
      <span class='dark'>{{ page_obj.paginator.count }} Queries</span>
    </p>
  {% endwith %}
  </div> 

2.Go to your admin.py file:

from django.contrib.admin.views.main import ChangeList
from django.core.paginator import EmptyPage, InvalidPage, Paginator

class InlineChangeList(object):
    can_show_all = True
    multi_page = True
    get_query_string = ChangeList.__dict__['get_query_string']

    def __init__(self, request, page_num, paginator):
        self.show_all = 'all' in request.GET
        self.page_num = page_num
        self.paginator = paginator
        self.result_count = paginator.count
        self.params = dict(request.GET.items())


class MyInline(admin.TabularInline):
    per_page = 10
    template = 'admin/edit_inline/anyname.html'
    model = Mymodel
    extra = 0
    can_delete = False

    def get_formset(self, request, obj=None, **kwargs):
        formset_class = super(MyInline, self).get_formset(
            request, obj, **kwargs)
        class PaginationFormSet(formset_class):
            def __init__(self, *args, **kwargs):
                super(PaginationFormSet, self).__init__(*args, **kwargs)

                qs = self.queryset
                paginator = Paginator(qs, self.per_page)
                try:
                    page_num = int(request.GET.get('page', ['0'])[0])
                except ValueError:
                    page_num = 0

                try:
                    page = paginator.page(page_num + 1)
                except (EmptyPage, InvalidPage):
                    page = paginator.page(paginator.num_pages)

                self.page = page
                self.cl = InlineChangeList(request, page_num, paginator)
                self.paginator = paginator

                if self.cl.show_all:
                    self._queryset = qs
                else:
                    self._queryset = page.object_list

        PaginationFormSet.per_page = self.per_page
        return PaginationFormSet
Answered By: DARK_C0D3R
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.