├── .coveragerc ├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── django_cool_paginator ├── __init__.py ├── exceptions │ ├── __init__.py │ └── tag_exceptions.py ├── templates │ └── __paginator.html └── templatetags │ ├── __init__.py │ ├── cool_paginate.py │ └── paginator_tags.py ├── requirements.txt ├── runtests.py ├── setup.py └── tests ├── __init__.py ├── test-requirements.txt ├── test_settings.py └── tests.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source= 3 | django_cool_paginator/ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | .idea/ 107 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "3.6" 5 | - "3.5" 6 | - "3.4" 7 | 8 | env: 9 | - DJANGO_VERSION=2.0.0 10 | - DJANGO_VERSION=1.11.0 11 | - DJANGO_VERSION=1.10.0 12 | 13 | install: 14 | - pip install Django==$DJANGO_VERSION --quiet 15 | - pip install -r tests/test-requirements.txt 16 | 17 | before_script: 18 | - flake8 --ignore=E501 19 | 20 | script: 21 | - coverage run runtests.py 22 | - python setup.py build 23 | 24 | after_success: 25 | - coveralls 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jabrail Lezgintsev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | recursive-include django_cool_paginator/templates * 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-cool-pagination 2 | 3 | [![Build Status](https://travis-ci.org/joe513/django-cool-pagination.svg?branch=master)](https://travis-ci.org/joe513/django-cool-pagination) 4 | [![Coverage Status](https://coveralls.io/repos/github/joe513/django-cool-pagination/badge.svg?branch=master)](https://coveralls.io/github/joe513/django-cool-pagination?branch=master) 5 | [![PyPI version](https://badge.fury.io/py/django-cool-pagination.svg)](https://badge.fury.io/py/django-cool-pagination) 6 | 7 | *django-cool-pagination* is simple pagination app that saves your time. 8 | 9 |

10 | 11 | ![Usage Example](https://media.giphy.com/media/325f9CboTX86Eqa6he/giphy.gif) 12 |

13 | 14 | ## Prerequisites 15 | Currently it supports Bootstrap 4.x only. So that you have to add [Bootstrap 4](https://getbootstrap.com/docs/4.1/getting-started/download) to your project.
16 | 17 | ## Features 18 | 19 | - _Dynamic query string creation_ 20 | - _Length auto control_ 21 | - _Fully customizable_ (aspiring) 22 | 23 | ## Installation 24 | ### Installing 25 | #### pip 26 | pip install django-cool-pagination 27 | #### setup.py 28 | git clone https://github.com/joe513/django-cool-pagination.git 29 | cd django-cool-pagination 30 | python setup.py install 31 | ### Setting up 32 | #### Add to `INSTALLED_APPS` 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 41 | 'django_cool_paginator' 42 | #### Make sure `request` is in `context_processors` 43 | 'context_processors': [ 44 | 'django.template.context_processors.debug', 45 | 'django.template.context_processors.request', 46 | 'django.contrib.auth.context_processors.auth', 47 | 'django.contrib.messages.context_processors.messages', 48 | 49 | ## Using 50 | 51 | ### View 52 | #### FBV (Function based view) 53 | 54 | def listing(request): 55 | contact_list = Contact.objects.all() 56 | paginator = Paginator(contact_list, 25) 57 | 58 | page = request.GET.get('page') 59 | page_obj = paginator.get_page(page) 60 | return render(request, 'list.html', {'page_obj': page_obj}) 61 | 62 | #### CBV (Class based view) 63 | 64 | class Listing(ListView): 65 | model = Item 66 | paginate_by = 5 67 | 68 | ### Template 69 | {% load cool_paginate %} 70 | 71 | {% for contact in page_obj %} 72 | ... 73 | {% endfor %} 74 | 75 | {% cool_paginate page_obj=ENTER HERE YOUR PAGE OBJECT! %} 76 | 77 | > **Note:** 78 | You don't have to specify `page` if its name is `page_obj` as default. 79 | 80 | ## Customization 81 | You can customize it so that it works as you want. Customize it by defining settings either in setting.py or 82 | inside of `{% cool_paginate %} ` 83 | 84 | #### setting.py 85 | 86 | `COOL_PAGINATOR_NEXT_NAME` - Name for "next" button in pagination bar.
87 | `COOL_PAGINATOR_PREVIOUS_NAME` - Name for "previous" button in pagination bar
88 | `COOL_PAGINATOR_SIZE` - Size of pagination bar (choose: "LARGE" or "SMALL")
89 | `COOL_PAGINATOR_ELASTIC` - What page width is elastic mode enabled from? 90 | 91 | #### {% cool_paginate page_obj next_name previous_name size elastic %} 92 | `page_obj` - Type here your page object.
93 | `next_name` - Name for "next" button in pagination bar.
94 | `previous_name` - Name for "previous" button in pagination bar
95 | `size` - Size of pagination bar (choose: "LARGE" or "SMALL")
96 | `elastic` - What page width is elastic mode enabled from? 97 | 98 | > **Note:** 99 | > `{% cool_paginate %}` has a priority, _django-cool-pagination_ will firstly look at this, after at setting.py 100 | 101 | ## License 102 | This project is licensed under the MIT License - see the LICENSE file for details 103 |
104 | 105 | 106 | -------------------------------------------------------------------------------- /django_cool_paginator/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Jabrail Lezgintsev' 2 | __credits__ = ['Jabrail Lezgintsev'] 3 | __license__ = 'MIT' 4 | __version__ = '0.3.0' 5 | __maintainer__ = 'Jabrail Lezgintsev' 6 | __email__ = 'lezgintsev13@yandex.ru' 7 | __status__ = 'Beta' 8 | -------------------------------------------------------------------------------- /django_cool_paginator/exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | from .tag_exceptions import PageNotSpecified, RequestNotExists # NOQA 2 | -------------------------------------------------------------------------------- /django_cool_paginator/exceptions/tag_exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | These exceptions relate to django-cool-pagination. 3 | 4 | Items: 5 | Exceptions: 6 | - PageNotSpecified(*args, **kwargs) 7 | - RequestNotExists(*args, **kwargs) 8 | 9 | Description: 10 | Exception Description: 11 | 12 | PageNotSpecified: 13 | Raises in case if page wasn't specified at all 14 | 15 | RequestNotExists: 16 | Raises in case if user has forgotten to include request context processor 17 | 18 | """ 19 | 20 | 21 | class PageNotSpecified(Exception): 22 | """Raised if page wasn't specified at all""" 23 | pass 24 | 25 | 26 | class RequestNotExists(Exception): 27 | """Raised if user has forgotten to include request context processor""" 28 | pass 29 | -------------------------------------------------------------------------------- /django_cool_paginator/templates/__paginator.html: -------------------------------------------------------------------------------- 1 | {% load paginator_tags %} 2 | 3 | 52 | -------------------------------------------------------------------------------- /django_cool_paginator/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joe513/django-cool-pagination/9a803a0fb1b9b141d5b4147e77d8e985484c9b1c/django_cool_paginator/templatetags/__init__.py -------------------------------------------------------------------------------- /django_cool_paginator/templatetags/cool_paginate.py: -------------------------------------------------------------------------------- 1 | """ 2 | Main module for pagination purpose 3 | 4 | This module is designed as main. To load it to your template just write: 5 | 6 | {% load cool_paginate %} 7 | 8 | Items: 9 | Functions: 10 | 11 | - cool_paginate(context, **kwargs) 12 | 13 | Description: 14 | Function description: 15 | 16 | cool_paginate: 17 | Main function for pagination process. 18 | 19 | """ 20 | 21 | from django import template 22 | from django.conf import settings 23 | 24 | from django_cool_paginator.exceptions import PageNotSpecified, RequestNotExists 25 | 26 | # TODO Discuss which name is better for the module cool_paginate or cool_pagination 27 | 28 | register = template.Library() 29 | 30 | 31 | @register.inclusion_tag('__paginator.html', takes_context=True) 32 | def cool_paginate(context, **kwargs) -> dict: 33 | """Main function for pagination process.""" 34 | 35 | names = ( 36 | 'size', 37 | 'next_name', 38 | 'previous_name', 39 | 'elastic', 40 | 'page_obj', 41 | ) 42 | 43 | return_dict = {name: value for name, value in zip(names, map(kwargs.get, names))} 44 | 45 | if context.get('request'): 46 | return_dict['request'] = context['request'] 47 | else: 48 | raise RequestNotExists( 49 | 'Unable to find request in your template context,' 50 | 'please make sure that you have the request context processor enabled' 51 | ) 52 | 53 | if not return_dict.get('page_obj'): 54 | if context.get('page_obj'): 55 | return_dict['page_obj'] = context['page_obj'] 56 | else: 57 | raise PageNotSpecified( 58 | 'You customized paginator standard name, ' 59 | "but haven't specified it in {% cool_paginate %} tag." 60 | ) 61 | 62 | if not return_dict.get('elastic'): 63 | return_dict['elastic'] = getattr(settings, 'COOL_PAGINATOR_ELASTIC', 10) 64 | 65 | return return_dict 66 | -------------------------------------------------------------------------------- /django_cool_paginator/templatetags/paginator_tags.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tags for pagination template which is in templates directory 3 | 4 | These are internal pagination custom tag functions that are needed by paginator template (__paginator.html) 5 | which is in templates directory. User doesn't have to interact with them. They are designed as internal. 6 | 7 | Most of them depend on project settings. 8 | 9 | Items: 10 | Vars: 11 | - COOL_PAGINATOR_NEXT_NAME 12 | - COOL_PAGINATOR_PREVIOUS_NAME 13 | - COOL_PAGINATOR_SIZE 14 | 15 | Functions: 16 | - url_replace(context, field, value) 17 | - ellipsis_or_number(context, paginator, current_page) 18 | - size(chosen_size=None) 19 | - next_name(name=None) 20 | - previous_name(name=None) 21 | 22 | Description: 23 | Function description: 24 | 25 | url_replace: 26 | To avoid GET params loosing 27 | ellipsis_or_number: 28 | To avoid display a long page table 29 | size: 30 | Points to pagination table size. 31 | 32 | """ 33 | 34 | from django import template 35 | from django.conf import settings 36 | 37 | register = template.Library() 38 | 39 | 40 | # Pagination settings 41 | 42 | COOL_PAGINATOR_NEXT_NAME = getattr(settings, 'COOL_PAGINATOR_NEXT_NAME', 'Next') 43 | COOL_PAGINATOR_PREVIOUS_NAME = getattr(settings, 'COOL_PAGINATOR_PREVIOUS_NAME', 'Previous') 44 | COOL_PAGINATOR_SIZE = getattr(settings, 'COOL_PAGINATOR_SIZE', None) 45 | 46 | # Tags... 47 | 48 | 49 | # next_name tag 50 | next_name = register.simple_tag(name='next_name', func=lambda name=None: name or COOL_PAGINATOR_NEXT_NAME) 51 | 52 | # previous_name tag 53 | previous_name = register.simple_tag(name='previous_name', func=lambda name=None: name or COOL_PAGINATOR_PREVIOUS_NAME) 54 | 55 | 56 | @register.simple_tag(takes_context=True) 57 | def url_replace(context, field, value): 58 | """ 59 | To avoid GET params losing 60 | 61 | :param context: context_obj 62 | :param field: str 63 | :param value: str 64 | :return: dict-like object 65 | """ 66 | 67 | query_string = context['request'].GET.copy() 68 | query_string[field] = value 69 | 70 | return query_string.urlencode() 71 | 72 | 73 | # TODO Make this function maximum customizable 74 | @register.simple_tag(takes_context=True) 75 | def ellipsis_or_number(context, paginator, current_page): 76 | """ 77 | To avoid display a long pagination bar 78 | 79 | :param context: template context 80 | :param paginator: paginator_obj 81 | :param current_page: int 82 | :return: str or None 83 | """ 84 | 85 | # Checks is it first page 86 | chosen_page = int(context['request'].GET['page'] or 1) if 'page' in context['request'].GET else 1 87 | 88 | if current_page in (chosen_page + 1, chosen_page + 2, chosen_page - 1, chosen_page - 2, 89 | paginator.num_pages, paginator.num_pages - 1, 1, 2, chosen_page): 90 | return current_page 91 | 92 | if current_page in (chosen_page + 3, chosen_page - 3): 93 | return '...' 94 | 95 | 96 | @register.simple_tag 97 | def size(chosen_size=None) -> str: 98 | """ 99 | Points to pagination bar size. 100 | 101 | :param chosen_size: int 102 | :return: str 103 | """ 104 | 105 | return { 106 | 'LARGE': 'pagination-lg', 107 | 'SMALL': 'pagination-sm', 108 | }.get(chosen_size or COOL_PAGINATOR_SIZE, '') 109 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django 2 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | 6 | import django 7 | 8 | from django.conf import settings 9 | from django.test.utils import get_runner 10 | 11 | 12 | def run_tests(): 13 | os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings' 14 | django.setup() 15 | test_runner = get_runner(settings)() 16 | failures = test_runner.run_tests(["tests"]) 17 | sys.exit(bool(failures)) 18 | 19 | 20 | if __name__ == "__main__": 21 | run_tests() 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import find_packages, setup 4 | 5 | with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme: 6 | README = readme.read() 7 | 8 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 9 | 10 | setup( 11 | name='django-cool-pagination', 12 | version='0.3.0', 13 | packages=find_packages(), 14 | include_package_data=True, 15 | license='MIT', 16 | description='Simple pagination app that saves your time.', 17 | long_description_content_type='text/markdown', 18 | long_description=README, 19 | url='https://github.com/joe513/django-cool-pagination', 20 | author='Jabrail Lezgintsev', 21 | author_email='lezgintsev13@yandex.ru', 22 | keywords='python3 django pagination bootstrap4', 23 | project_urls={ 24 | 'Source': 'https://github.com/joe513/django-cool-pagination' 25 | }, 26 | python_requires='>=3', 27 | classifiers=[ 28 | 'Environment :: Web Environment', 29 | 'Framework :: Django', 30 | 'Framework :: Django :: 2.0', 31 | 'Intended Audience :: Developers', 32 | 'License :: OSI Approved :: MIT License', 33 | 'Operating System :: OS Independent', 34 | 'Programming Language :: Python', 35 | 'Programming Language :: Python :: 3.6', 36 | 'Programming Language :: Python :: 3.5', 37 | 'Programming Language :: Python :: 3.4', 38 | 'Topic :: Internet :: WWW/HTTP', 39 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joe513/django-cool-pagination/9a803a0fb1b9b141d5b4147e77d8e985484c9b1c/tests/__init__.py -------------------------------------------------------------------------------- /tests/test-requirements.txt: -------------------------------------------------------------------------------- 1 | coverage 2 | coveralls 3 | flake8 4 | -------------------------------------------------------------------------------- /tests/test_settings.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 4 | 5 | 6 | SECRET_KEY = 'fake-key' 7 | 8 | INSTALLED_APPS = [ 9 | 'django_cool_paginator', 10 | 11 | ] 12 | 13 | TEMPLATES = [ 14 | { 15 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 16 | 'APP_DIRS': True, 17 | } 18 | ] 19 | 20 | # For testing purposes. 21 | 22 | COOL_PAGINATOR_SIZE = 'SMALL' 23 | COOL_PAGINATOR_NEXT_NAME = 'Go' 24 | COOL_PAGINATOR_PREVIOUS_NAME = 'Previous' 25 | -------------------------------------------------------------------------------- /tests/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import SimpleTestCase 2 | from django.http import HttpRequest 3 | from django.template import Template, Context 4 | from django.core.paginator import Paginator 5 | 6 | from django_cool_paginator.templatetags import paginator_tags 7 | from django_cool_paginator.exceptions import PageNotSpecified, RequestNotExists 8 | 9 | 10 | class BaseTest(SimpleTestCase): 11 | 12 | def setUp(self): 13 | paginator = Paginator([... for _ in range(30)], 5) 14 | self.page = paginator.page(4) 15 | self.request = HttpRequest() 16 | 17 | self.base_context = { 18 | 'request': self.request, 19 | 'page_obj': paginator.page(4), 20 | } 21 | 22 | self.size_conf = { 23 | 'LARGE': 'pagination-lg', 24 | 'SMALL': 'pagination-sm' 25 | } 26 | 27 | 28 | class PaginatorTagTest(BaseTest): 29 | """These tests are designed for paginator_tags.py internal module which is in templatetags""" 30 | 31 | load = '{% load paginator_tags %}' 32 | 33 | def test_ellipsis_or_number(self): 34 | template_string = Template(self.load + '{% ellipsis_or_number page_obj.paginator page_numb %}') 35 | 36 | self.request.GET['page'] = 5 37 | 38 | context = self.base_context.copy() 39 | context['page_numb'] = 5 40 | 41 | template = template_string.render(Context(context)) 42 | self.assertEqual(int(template), 5) 43 | 44 | context['page_numb'] = 8 45 | 46 | template = template_string.render(Context(context)) 47 | self.assertEqual(template, '...') 48 | 49 | self.request.GET['page'] = 1 50 | context['page_numb'] = 7 51 | 52 | template = template_string.render(Context(context)) 53 | self.assertEqual(template, str(None)) 54 | 55 | def test_url_replace(self): 56 | template_string = Template(self.load + "{% url_replace 'page' number %}") 57 | 58 | context = self.base_context.copy() 59 | context['number'] = 2 60 | 61 | template = template_string.render(Context(context)) 62 | self.assertEquals(template, 'page=2') 63 | 64 | def test_size(self): 65 | template_string = Template(self.load + '{% size size %}') 66 | 67 | context = {'size': 'LARGE'} 68 | 69 | template = template_string.render(Context(context)) 70 | self.assertEqual(template, self.size_conf[context['size']]) 71 | 72 | # By default 73 | template = template_string.render(Context()) 74 | self.assertEqual(template, self.size_conf[paginator_tags.COOL_PAGINATOR_SIZE]) 75 | 76 | def test_previous_name(self): 77 | template_string = Template(self.load + "{% previous_name name=previous_name %}") 78 | 79 | previous_name = 'Bobby' 80 | 81 | template = template_string.render(Context({'previous_name': previous_name})) 82 | self.assertEqual(template, previous_name) 83 | 84 | # By default 85 | template = template_string.render(Context()) 86 | self.assertEqual(template, paginator_tags.COOL_PAGINATOR_PREVIOUS_NAME) 87 | 88 | def test_next_name(self): 89 | template_string = Template(self.load + "{% next_name name=next_name %}") 90 | 91 | next_name = 'Jack' 92 | 93 | template = template_string.render(Context({'next_name': next_name})) 94 | self.assertEqual(template, next_name) 95 | 96 | # By default 97 | template = template_string.render(Context({})) 98 | self.assertEqual(template, paginator_tags.COOL_PAGINATOR_NEXT_NAME) 99 | 100 | 101 | class CoolPaginateTest(BaseTest): 102 | """These tests are designed for cool_paginate function which is in cool_paginate.py module""" 103 | 104 | load = '{% load cool_paginate %}' 105 | 106 | def test_page(self): 107 | template_string = Template(self.load + '{% cool_paginate %}') 108 | self.assertTrue(template_string.render(Context(self.base_context.copy()))) 109 | 110 | def test_exception(self): 111 | template_string = Template(self.load + '{% cool_paginate %}') 112 | 113 | with self.assertRaisesMessage(RequestNotExists, 114 | 'Unable to find request in your template context,' 115 | 'please make sure that you have the request context processor enabled' 116 | ): 117 | template_string.render(Context()) 118 | 119 | with self.assertRaisesMessage(PageNotSpecified, 120 | 'You customized paginator standard name, ' 121 | "but haven't specified it in {% cool_paginate %} tag." 122 | ): 123 | template_string.render(Context({'request': self.request})) 124 | 125 | def test_size(self): 126 | 127 | context = self.base_context.copy() 128 | context['size'] = 'LARGE' 129 | 130 | template_string = Template(self.load + '{% cool_paginate size=size %}') 131 | template = template_string.render(Context(context)) 132 | 133 | self.assertIn(self.size_conf[context['size']], template) 134 | 135 | # By default 136 | context.pop('size') 137 | template = template_string.render(Context(context)) 138 | 139 | self.assertIn(self.size_conf[paginator_tags.COOL_PAGINATOR_SIZE], template) 140 | 141 | def test_next_name(self): 142 | 143 | context = self.base_context.copy() 144 | context['next_name'] = 'Go to' 145 | 146 | template_string = Template(self.load + '{% cool_paginate next_name=next_name %}') 147 | template = template_string.render(Context(context)) 148 | 149 | self.assertIn(context['next_name'], template) 150 | 151 | # By default 152 | context.pop('next_name') 153 | template = template_string.render(Context(context)) 154 | self.assertIn(paginator_tags.COOL_PAGINATOR_NEXT_NAME, template) 155 | 156 | def test_previous_name(self): 157 | 158 | context = self.base_context.copy() 159 | context['previous_name'] = 'Back to' 160 | 161 | template_string = Template(self.load + '{% cool_paginate previous_name=previous_name %}') 162 | template = template_string.render(Context(context)) 163 | 164 | self.assertIn(context['previous_name'], template) 165 | 166 | # By default 167 | context.pop('previous_name') 168 | template = template_string.render(Context(context)) 169 | self.assertIn(paginator_tags.COOL_PAGINATOR_PREVIOUS_NAME, template) 170 | 171 | def test_elastic(self): 172 | 173 | context = self.base_context.copy() 174 | context['elastic'] = 5 175 | 176 | standard_template = Template(self.load + '{% cool_paginate elastic=elastic%}').render(Context(context)).strip() 177 | 178 | context['elastic'] = 10 179 | template = Template(self.load + '{% cool_paginate elastic=elastic%}').render(Context(context)).strip() 180 | 181 | self.assertHTMLNotEqual(template, standard_template) 182 | --------------------------------------------------------------------------------