Search code examples
pythondjangodjango-pagination

Slicing paginator.page_range inside a template


I have page_obj in a template which was returned from a ListView view. Now, I wanted to create links to several pages before and after the current page. Therefore, I wanted to slice page_obj.paginator.page_range this way: page_obj.paginator.page_range[page_obj.number-3:page_obj.number+4]. This works in django shell but for some reason when I did it a template, there is a Template Syntax Error, Could not parse the remainder: '[page_obj.number-3:page_obj.number+4]' from 'page_obj.paginator.page_range[page_obj.number-3:page_obj.number+4]'. Is there a workaround for this case?

P.S. I know I can do it using the whole page_obj.paginator.page_range and then using if statements to check if a page is in the required range, but I thought it's a bit inefficient.


Solution

  • As stated in my comment Django Template Language does not include normal python syntax. The reason for this is Django aims to separate the logic and design of the website. If there is need to perform somewhat complicated logic you either need to use template tags or filters.

    For your need either an inclusion tag would work or a simple filter that would take the page_range and return a sliced version of it. A template filter here would not be very useful considering we can only pass one argument to it, meaning it would not be very customizable. Let's assume that your pagination would look very similar or perhaps you would pass the template you use to the tag.

    Firstly you need to create a templatetags sub-package in your app and then in that you would add files (e.g. pagination_tags.py) which would contain your tags. The layout would be something like:

    your_app/
        __init__.py
        models.py
        templatetags/
            __init__.py
            pagination_tags.py
        views.py
    

    Now in your file pagination_tags.py you want to write your tags. As a reference you may read the howto on Custom template tags and filters in the documentation.

    Firstly we declare register which is an instance of template.Library. After which we would write our template tags / filters. We will use an inclusion_tag:

    from django import template
    
    register = template.Library()
    
    @register.inclusion_tag('pagination_tag.html')
    def show_pagination(page_obj, **kwargs):
        left = kwargs.get('left', 3)
        right = kwargs.get('right', 4)
        pages_iter = page_obj.paginator.page_range[page_obj.number - left:page_obj.number + right]
        template = kwargs.get('template', 'default_pagination_template.html')
        return {**kwargs, 'page_obj': page_obj, 'pages_iter': pages_iter, 'template': template}
    

    Now we will have a simple template named pagination_tag.html that will simply extend the template name either passed as a keyword argument or default_pagination_template.html:

    {% extends template %}
    

    Now in default_pagination_template.html or any other template we can use all the variables in the dictionary that our function show_pagination returns:

    {% for page_num in pages_iter %}
        Display page links here, etc.
    {% endfor %}
    

    You can modify this implementation as per your needs. I will also leave the design and implementation of default_pagination_template.html upto you.

    Now in your template where you want to use this, first we will load these tags. Then we will use them:

    {% load pagination_tags %}
    
    ...
    {% show_pagination page_obj left=5 right=6 template="some_custom_template.html" %}