Explorar el Código

Fix handling of InvalidPage exceptions in CatalogueView (#3505)

master
Alexander Gaevsky hace 3 años
padre
commit
c87a4d88ca
No account linked to committer's email address

+ 5
- 3
src/oscar/apps/catalogue/search_handlers.py Ver fichero

@@ -2,10 +2,11 @@ from django.conf import settings
2 2
 from django.utils.module_loading import import_string
3 3
 from django.views.generic.list import MultipleObjectMixin
4 4
 
5
-from oscar.core.loading import get_class, get_model
5
+from oscar.core.loading import get_class, get_classes, get_model
6 6
 
7 7
 BrowseCategoryForm = get_class('search.forms', 'BrowseCategoryForm')
8
-SearchHandler = get_class('search.search_handlers', 'SearchHandler')
8
+SearchResultsPaginationMixin, SearchHandler = get_classes(
9
+    'search.search_handlers', ('SearchHandler', 'SearchResultsPaginationMixin'))
9 10
 is_solr_supported = get_class('search.features', 'is_solr_supported')
10 11
 is_elasticsearch_supported = get_class('search.features', 'is_elasticsearch_supported')
11 12
 Product = get_model('catalogue', 'Product')
@@ -77,7 +78,7 @@ class ESProductSearchHandler(SearchHandler):
77 78
         return sqs
78 79
 
79 80
 
80
-class SimpleProductSearchHandler(MultipleObjectMixin):
81
+class SimpleProductSearchHandler(SearchResultsPaginationMixin, MultipleObjectMixin):
81 82
     """
82 83
     A basic implementation of the full-featured SearchHandler that has no
83 84
     faceting support, but doesn't require a Haystack backend. It only
@@ -89,6 +90,7 @@ class SimpleProductSearchHandler(MultipleObjectMixin):
89 90
     paginate_by = settings.OSCAR_PRODUCTS_PER_PAGE
90 91
 
91 92
     def __init__(self, request_data, full_path, categories=None):
93
+        self.request_data = request_data
92 94
         self.categories = categories
93 95
         self.kwargs = {'page': request_data.get('page') or 1}
94 96
         self.object_list = self.get_queryset()

+ 4
- 2
src/oscar/apps/catalogue/views.py Ver fichero

@@ -130,11 +130,12 @@ class CatalogueView(TemplateView):
130 130
         try:
131 131
             self.search_handler = self.get_search_handler(
132 132
                 self.request.GET, request.get_full_path(), [])
133
+            response = super().get(request, *args, **kwargs)
133 134
         except InvalidPage:
134 135
             # Redirect to page one.
135 136
             messages.error(request, _('The given page number was invalid.'))
136 137
             return redirect('catalogue:index')
137
-        return super().get(request, *args, **kwargs)
138
+        return response
138 139
 
139 140
     def get_search_handler(self, *args, **kwargs):
140 141
         return get_product_search_handler_class()(*args, **kwargs)
@@ -172,11 +173,12 @@ class ProductCategoryView(TemplateView):
172 173
         try:
173 174
             self.search_handler = self.get_search_handler(
174 175
                 request.GET, request.get_full_path(), self.get_categories())
176
+            response = super().get(request, *args, **kwargs)
175 177
         except InvalidPage:
176 178
             messages.error(request, _('The given page number was invalid.'))
177 179
             return redirect(self.category.get_absolute_url())
178 180
 
179
-        return super().get(request, *args, **kwargs)
181
+        return response
180 182
 
181 183
     def is_viewable(self, category, request):
182 184
         return category.is_public or request.user.is_staff

+ 34
- 35
src/oscar/apps/search/search_handlers.py Ver fichero

@@ -1,5 +1,4 @@
1 1
 from django.core.paginator import InvalidPage, Paginator
2
-from django.utils.translation import gettext_lazy as _
3 2
 from haystack import connections
4 3
 
5 4
 from oscar.core.loading import get_class
@@ -9,7 +8,39 @@ from . import facets
9 8
 FacetMunger = get_class('search.facets', 'FacetMunger')
10 9
 
11 10
 
12
-class SearchHandler(object):
11
+class SearchResultsPaginationMixin:
12
+    paginate_by = None
13
+    paginator_class = Paginator
14
+    page_kwarg = 'page'
15
+
16
+    def paginate_queryset(self, queryset, page_size):
17
+        """
18
+        Paginate the search results. This is a simplified version of
19
+        Django's MultipleObjectMixin.paginate_queryset
20
+        """
21
+        paginator = self.get_paginator(queryset, page_size)
22
+        page_kwarg = self.page_kwarg
23
+        page_number = self.request_data.get(page_kwarg, 1)
24
+        try:
25
+            page_number = int(page_number)
26
+        except ValueError:
27
+            if page_number == 'last':
28
+                page_number = paginator.num_pages
29
+            else:
30
+                raise InvalidPage
31
+        # This can also raise an InvalidPage exception.
32
+        page = paginator.page(page_number)
33
+        return paginator, page, page.object_list, page.has_other_pages()
34
+
35
+    def get_paginator(self, queryset, per_page=None):
36
+        """
37
+        Return a paginator. Override this to set settings like orphans,
38
+        allow_empty, etc.
39
+        """
40
+        return self.paginator_class(queryset, per_page)
41
+
42
+
43
+class SearchHandler(SearchResultsPaginationMixin):
13 44
     """
14 45
     A class that is concerned with performing a search and paginating the
15 46
     results. The search is triggered upon initialisation (mainly to have a
@@ -36,9 +67,6 @@ class SearchHandler(object):
36 67
 
37 68
     form_class = None
38 69
     model_whitelist = None
39
-    paginate_by = None
40
-    paginator_class = Paginator
41
-    page_kwarg = 'page'
42 70
 
43 71
     def __init__(self, request_data, full_path):
44 72
         self.full_path = full_path
@@ -51,8 +79,7 @@ class SearchHandler(object):
51 79
         self.results = self.get_search_results(self.search_form)
52 80
         # If below raises an UnicodeDecodeError, you're running pysolr < 3.2
53 81
         # with Solr 4.
54
-        self.paginator, self.page = self.paginate_queryset(
55
-            self.results, request_data)
82
+        self.paginator, self.page = self.paginate_queryset(self.results, self.paginate_by)[0:2]
56 83
 
57 84
     # Search related methods
58 85
 
@@ -85,34 +112,6 @@ class SearchHandler(object):
85 112
             sqs = sqs.models(*self.model_whitelist)
86 113
         return sqs
87 114
 
88
-    # Pagination related methods
89
-
90
-    def paginate_queryset(self, queryset, request_data):
91
-        """
92
-        Paginate the search results. This is a simplified version of
93
-        Django's MultipleObjectMixin.paginate_queryset
94
-        """
95
-        paginator = self.get_paginator(queryset)
96
-        page_kwarg = self.page_kwarg
97
-        page = request_data.get(page_kwarg, 1)
98
-        try:
99
-            page_number = int(page)
100
-        except ValueError:
101
-            if page == 'last':
102
-                page_number = paginator.num_pages
103
-            else:
104
-                raise InvalidPage(_(
105
-                    "Page is not 'last', nor can it be converted to an int."))
106
-        # This can also raise an InvalidPage exception.
107
-        return paginator, paginator.page(page_number)
108
-
109
-    def get_paginator(self, queryset):
110
-        """
111
-        Return a paginator. Override this to set settings like orphans,
112
-        allow_empty, etc.
113
-        """
114
-        return self.paginator_class(queryset, self.paginate_by)
115
-
116 115
     # Accessing the search results and meta data
117 116
 
118 117
     def bulk_fetch_results(self, paginated_results):

+ 7
- 0
tests/functional/catalogue/test_catalogue.py Ver fichero

@@ -103,6 +103,13 @@ class TestProductListView(WebTestCase):
103 103
         products_on_page = list(page.context['products'].all())
104 104
         self.assertEqual(products_on_page, [])
105 105
 
106
+    def test_invalid_page_redirects_to_index(self):
107
+        create_product()
108
+        products_list_url = reverse('catalogue:index')
109
+        response = self.app.get('%s?page=200' % products_list_url)
110
+        self.assertEqual(response.status_code, 302)
111
+        self.assertRedirectsTo(response, 'catalogue:index')
112
+
106 113
 
107 114
 class TestProductCategoryView(WebTestCase):
108 115
 

Loading…
Cancelar
Guardar