title: Django pagination: How do 'paginator' and 'page_obj' get injected into a template's context?
body: <p>I'm trying to understand some source code which is used in a <code>ListView</code> to generate pagination links at the bottom of the page. For example, on a page listing 'experts', it looks like this:</p>
  
  <p><a href="https://i.stack.imgur.com/knT9o.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/knT9o.png" alt="enter image description here"></a></p>
  
  <p>It seems to be implemented somewhat differently from the way described in <a href="https://docs.djangoproject.com/en/2.0/topics/pagination/" rel="nofollow noreferrer">https://docs.djangoproject.com/en/2.0/topics/pagination/</a>. There is an inclusion tag called <code>paginator</code>:</p>
  
  <pre><code>from django import template
  from django.core.paginator import EmptyPage
  
  from dashboard.views.utils import query_dict_from_params_or_cookies
  
  register = template.Library()
  
  
  def query_params(params):
      query = params.copy()
  
      if query.get('page'):
          del query['page']
  
      return '&amp;' + query.urlencode() if query.urlencode() else ''
  
  
  @register.inclusion_tag('templatetags/paginator.html', takes_context=True)
  def paginator(context, adjacent_pages=2):
      request = context['request']
  
      page_obj = context['page_obj']
      page_range = context['paginator'].page_range
  
      page_number = page_obj.number
      last_page_number = len(page_range)
  
      left_idx = max(0, page_number - adjacent_pages - 1)
      right_idx = min(page_number + adjacent_pages, last_page_number)
  
      if left_idx == 2:
          left_idx -= 1
  
      if right_idx == last_page_number - 2:
          right_idx += 1
  
      window = page_range[left_idx:right_idx]
  
      try:
          previous_page_number = page_obj.previous_page_number()
      except EmptyPage:
          previous_page_number = None
  
      try:
          next_page_number = page_obj.next_page_number()
      except EmptyPage:
          next_page_number = None
  
      params = query_dict_from_params_or_cookies(request)
  
      return {
          'has_previous': page_obj.has_previous(),
          'has_next': page_obj.has_next(),
          'previous_page_number': previous_page_number,
          'next_page_number': next_page_number,
          'last_page_number': last_page_number,
          'page_range': window,
          'page_number': page_number,
          'show_first': page_number &gt; adjacent_pages + 1,
          'show_left_gap': page_number &gt; adjacent_pages + 3,
          'show_last': page_number &lt; last_page_number - adjacent_pages,
          'show_right_gap': page_number &lt; last_page_number - adjacent_pages - 2,
          'query_params': query_params(params)
      }
  </code></pre>
  
  <p>The corresponding template, <code>templatetags/paginator.html</code>, looks like this:</p>
  
  <pre><code>&lt;ul class="pagination right"
      {% if has_previous %}data-previous-page="?page={{ previous_page_number }}{{ query_params }}"{% endif %}
      {% if has_next %}data-next-page="?page={{ next_page_number }}{{ query_params }}"{% endif %}&gt;
    {% if has_previous %}
      &lt;li class="waves-effect"&gt;
        &lt;a href="?page={{ previous_page_number }}{{ query_params }}"&gt;
          &lt;i class="material-icons"&gt;chevron_left&lt;/i&gt;
        &lt;/a&gt;
      &lt;/li&gt;
    {% else %}
      &lt;li class="disabled"&gt;
        &lt;a&gt;&lt;i class="material-icons"&gt;chevron_left&lt;/i&gt;&lt;/a&gt;
      &lt;/li&gt;
    {% endif %}
  
    {% if show_first %}
      &lt;li&gt;&lt;a href="?page=1{{ query_params }}"&gt;1&lt;/a&gt;&lt;/li&gt;
    {% endif %}
  
    {% if show_left_gap %}
      &lt;li class="disabled"&gt;&lt;a&gt;...&lt;/a&gt;&lt;/li&gt;
    {% endif %}
  
    {% for i in page_range %}
      {% if page_number == i %}
        &lt;li class="active"&gt;&lt;a&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
      {% else %}
        &lt;li class="waves-effect"&gt;&lt;a href="?page={{ i }}{{ query_params }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
      {% endif %}
    {% endfor %}
  
    {% if show_right_gap %}
      &lt;li class="disabled"&gt;&lt;a&gt;...&lt;/a&gt;&lt;/li&gt;
    {% endif %}
  
    {% if show_last %}
      &lt;li&gt;
        &lt;a href="?page={{ last_page_number }}{{ query_params }}"&gt;{{ last_page_number }}&lt;/a&gt;
      &lt;/li&gt;
    {% endif %}
  
    {% if has_next %}
      &lt;li class="waves-effect"&gt;
        &lt;a href="?page={{ next_page_number }}{{ query_params }}"&gt;
          &lt;i class="material-icons"&gt;chevron_right&lt;/i&gt;
        &lt;/a&gt;
      &lt;/li&gt;
    {% else %}
      &lt;li class="disabled"&gt;
        &lt;a&gt;&lt;i class="material-icons"&gt;chevron_right&lt;/i&gt;&lt;/a&gt;
      &lt;/li&gt;
    {% endif %}
  &lt;/ul&gt;
  </code></pre>
  
  <p>What I don't quite understand is how <code>paginator</code> and <code>page_obj</code> get inserted into the template's context. For example, I did a project-wide search for <code>page_obj</code> and it does not appear that it is being injected in our own code, which would mean that it is being injected by Django's source code. But how is this being done?</p>
  
  <p>Also, it seems to me like some of the logic essentially replicates what is in Django's <code>Paginator</code> class and this code could be simplified/refactored, would anyone agree?</p>
  
  <p><a href="https://i.stack.imgur.com/R0s9f.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/R0s9f.png" alt="enter image description here"></a></p>