Repository: Nitrate/Nitrate

Issue:
Drop django-pagination and use django.core.paginator
django-pagination is too old and looks it is not being maintained now. Drop it and back to use django's builtin Pagination in order to upgrade to Django >=1.9.

Blocks #110 


Discussion:

Resolving patch:
diff --git a/tcms/search/__init__.py b/tcms/search/__init__.py
--- a/tcms/search/__init__.py
+++ b/tcms/search/__init__.py
@@ -11,6 +11,8 @@
 from django.shortcuts import render_to_response
 from django.template import RequestContext
 from django.views.decorators.http import require_GET
+from django.core.paginator import Paginator
+from django.core.paginator import PageNotAnInteger
 
 from tcms.core.helpers.cache import cached_entities
 from tcms.core.utils.raw_sql import RawSQL
@@ -125,7 +127,8 @@ def sum_orm_queries(plans, cases, runs, target):
         return cases
 
 
-def render_results(request, results, time_cost, queries, tmpl='search/results.html'):
+def render_results(request, results, time_cost, queries,
+                   tmpl='search/results.html'):
     """Using a SQL "in" query and PKs as the arguments"""
     klasses = {
         'plan': {'class': TestPlan, 'result_key': 'test_plans'},
@@ -133,18 +136,38 @@ def render_results(request, results, time_cost, queries, tmpl='search/results.ht
         'run': {'class': TestRun, 'result_key': 'test_runs'}
     }
     asc = bool(request.GET.get('asc', None))
+    navigate_url = remove_from_request_path(request, 'page')
     query_url = remove_from_request_path(request, 'order_by')
     if asc:
         query_url = remove_from_request_path(query_url, 'asc')
     else:
         query_url = '%s&asc=True' % query_url
+
+    paginator = Paginator(results, 20)
+    page = request.GET.get('page')
+    try:
+        this_page = paginator.page(page)
+    except PageNotAnInteger:
+        this_page = paginator.page(1)
+
+    page_start = paginator.per_page * (this_page.number - 1) + 1
+    page_end = page_start + min(len(this_page.object_list),
+                                paginator.per_page) - 1
     context_data = {
-        klasses[request.GET['target']]['result_key']: results,
+        'search_target': klasses[request.GET['target']]['result_key'],
         'time_cost': time_cost,
         'queries': queries,
         'query_url': query_url,
+        # For navigation
+        'navigate_url': navigate_url,
+        'this_page': this_page,
+        'page_start': page_start,
+        'page_end': page_end,
     }
-    return render_to_response(tmpl, context_data, context_instance=RequestContext(request))
+    return render_to_response(
+        tmpl,
+        context_data,
+        context_instance=RequestContext(request))
 
 
 def remove_from_request_path(request, name):
diff --git a/tcms/settings/common.py b/tcms/settings/common.py
--- a/tcms/settings/common.py
+++ b/tcms/settings/common.py
@@ -129,7 +129,6 @@
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
-    'pagination.middleware.PaginationMiddleware',
 )
 
 ROOT_URLCONF = 'tcms.urls'
@@ -159,7 +158,6 @@
 
     'djcelery',
     'kombu.transport.django',
-    'pagination',
     'tinymce',
 
     'tcms.core',
diff --git a/tcms/testruns/views.py b/tcms/testruns/views.py
--- a/tcms/testruns/views.py
+++ b/tcms/testruns/views.py
@@ -379,26 +379,32 @@ def load_runs_of_one_plan(request, plan_id,
         run_filters = dict(('run__{0}'.format(key), value)
                            for key, value in form.cleaned_data.iteritems())
 
-        qs = TestCaseRun.objects \
-            .filter(case_run_status=TestCaseRunStatus.id_failed(),
-                    **run_filters) \
-            .values('run', 'case_run_status') \
-            .annotate(count=Count('pk')).order_by('run', 'case_run_status')
-        failure_subtotal = magic_convert(qs,
-                                         key_name='run', value_name='count')
-
-        qs = TestCaseRun.objects \
-            .filter(case_run_status=TestCaseRunStatus.id_passed(),
-                    **run_filters) \
-            .values('run', 'case_run_status') \
-            .annotate(count=Count('pk')).order_by('run', 'case_run_status')
-        success_subtotal = magic_convert(qs,
-                                         key_name='run', value_name='count')
-
-        qs = TestCaseRun.objects.filter(**run_filters).values('run') \
-            .annotate(count=Count('case')).order_by('run')
-        cases_subtotal = magic_convert(qs,
-                                       key_name='run', value_name='count')
+        qs = TestCaseRun.objects.filter(
+            case_run_status=TestCaseRunStatus.id_failed(),
+            **run_filters
+        ).values(
+            'run', 'case_run_status'
+        ).annotate(
+            count=Count('pk')
+        ).order_by('run', 'case_run_status')
+        failure_subtotal = magic_convert(qs, key_name='run', value_name='count')
+
+        qs = TestCaseRun.objects.filter(
+            case_run_status=TestCaseRunStatus.id_passed(),
+            **run_filters
+        ).values(
+            'run', 'case_run_status'
+        ).annotate(
+            count=Count('pk')
+        ).order_by('run', 'case_run_status')
+        success_subtotal = magic_convert(qs, key_name='run', value_name='count')
+
+        qs = TestCaseRun.objects.filter(
+            **run_filters
+        ).values('run').annotate(
+            count=Count('case')
+        ).order_by('run')
+        cases_subtotal = magic_convert(qs, key_name='run', value_name='count')
 
         for run in searched_runs:
             run_id = run.pk
@@ -482,25 +488,35 @@ def ajax_search(request, template_name='run/common/json_runs.txt'):
 
         # Get associated statistics data
         run_ids = [run.pk for run in searched_runs]
-        qs = TestCaseRun.objects \
-            .filter(case_run_status=TestCaseRunStatus.id_failed(), run__in=run_ids) \
-            .values('run', 'case_run_status') \
-            .annotate(count=Count('pk')) \
-            .order_by('run', 'case_run_status')
+        qs = TestCaseRun.objects.filter(
+            case_run_status=TestCaseRunStatus.id_failed(),
+            run__in=run_ids
+        ).values(
+            'run', 'case_run_status'
+        ).annotate(
+            count=Count('pk')
+        ).order_by('run', 'case_run_status')
         failure_subtotal = magic_convert(qs, key_name='run', value_name='count')
 
         completed_status_ids = TestCaseRunStatus._get_completed_status_ids()
-        qs = TestCaseRun.objects \
-            .filter(case_run_status__in=completed_status_ids, run__in=run_ids) \
-            .values('run', 'case_run_status') \
-            .annotate(count=Count('pk')) \
-            .order_by('run', 'case_run_status')
+        qs = TestCaseRun.objects.filter(
+            case_run_status__in=completed_status_ids,
+            run__in=run_ids
+        ).values(
+            'run', 'case_run_status'
+        ).annotate(
+            count=Count('pk')
+        ).order_by('run', 'case_run_status')
         completed_subtotal = dict((
             (run_id, sum((item['count'] for item in stats_rows)))
             for run_id, stats_rows
             in itertools.groupby(qs.iterator(), key=lambda row: row['run'])))
 
-        qs = TestCaseRun.objects.filter(run__in=run_ids).values('run').annotate(cases_count=Count('case'))
+        qs = TestCaseRun.objects.filter(
+            run__in=run_ids
+        ).values('run').annotate(
+            cases_count=Count('case')
+        )
         cases_subtotal = magic_convert(qs, key_name='run', value_name='cases_count')
 
         for run in searched_runs:


Test patch: