Permalink
Browse files

Working ListView

  • Loading branch information...
1 parent 91de4ae commit 997784c0877a328cad259fe9dcc1b0c30eae0a97 @bfirsh committed Jul 10, 2010
View
78 class_based_views/list.py
@@ -1,8 +1,8 @@
+from class_based_views.base import View
from django.core.paginator import Paginator, InvalidPage
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.utils.encoding import smart_str
-from class_based_views.base import View
class ListView(View):
"""
@@ -11,20 +11,15 @@ class ListView(View):
queryset will be handled correctly.
"""
- paginate_by = None
- allow_empty = True
- template_object_name = None
- queryset = None
- items = None
-
- def GET(self, request, *args, **kwargs):
- page = kwargs.get('page', None)
- paginator, page, items = self.get_items(request, page)
- template = self.get_template(request, items)
- context = self.get_context(request, items, paginator, page)
- mimetype = self.get_mimetype(request, items)
- response = self.get_response(request, items, template, context, mimetype=mimetype)
- return response
+ def __init__(self, **kwargs):
+ self._load_config_values(kwargs,
+ paginate_by = None,
+ allow_empty = True,
+ template_resource_name = None,
+ queryset = None,
+ items = None,
+ )
+ super(ListView, self).__init__(**kwargs)
def get_items(self, request, page):
"""
@@ -36,24 +31,26 @@ def get_items(self, request, page):
elif hasattr(self, 'items') and self.items is not None:
items = self.items
else:
- raise ImproperlyConfigured("'%s' must define 'queryset' or 'items'"
+ raise ImproperlyConfigured("'%s' must define 'queryset' or 'items'" \
% self.__class__.__name__)
-
+ # FIXME: Does this suck? I don't like how request data is being
+ # accessed in two different ways everywhere
+ self.items = items
return self.paginate_items(request, items, page)
def get_paginate_by(self, request, items):
"""
Get the number of items to paginate by, or ``None`` for no pagination.
"""
return self.paginate_by
-
+
def get_allow_empty(self, request):
"""
Returns ``True`` if the view should display empty lists, and ``False``
if a 404 should be raised instead.
"""
return self.allow_empty
-
+
def paginate_items(self, request, items, page):
"""
Paginate the list of items, if needed.
@@ -64,7 +61,7 @@ def paginate_items(self, request, items, page):
if not allow_empty and len(items) == 0:
raise Http404("Empty list and '%s.allow_empty' is False." % self.__class__.__name__)
return (None, None, items)
-
+
paginator = Paginator(items, paginate_by, allow_empty_first_page=allow_empty)
page = page or request.GET.get('page', 1)
try:
@@ -79,49 +76,48 @@ def paginate_items(self, request, items, page):
return (paginator, page, page.object_list)
except InvalidPage:
raise Http404('Invalid page (%s)' % page_number)
-
- def get_template_names(self, request, items, suffix='list'):
+
+ def get_template_names(self, suffix='list'):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if get_template is overridden.
- """
- names = super(ListView, self).get_template_names(request, items)
-
+ """
+ names = super(ListView, self).get_template_names()
+
# If the list is a queryset, we'll invent a template name based on the
- # app and model name. This name gets put at the end of the template
+ # app and model name. This name gets put at the end of the template
# name list so that user-supplied names override the automatically-
# generated ones.
- if hasattr(items, 'model'):
- opts = items.model._meta
+ if hasattr(self.items, 'model'):
+ opts = self.items.model._meta
names.append("%s/%s_%s.html" % (opts.app_label, opts.object_name.lower(), suffix))
-
+
return names
- def get_context(self, request, items, paginator, page, context=None):
+ def get_resource(self, request, page=None, *args, **kwargs):
"""
Get the context for this view.
"""
- if not context:
- context = {}
- context.update({
+ paginator, page, items = self.get_items(request, page)
+ context = {
'paginator': paginator,
'object_list': items,
'page_obj': page,
'is_paginated': paginator is not None
- })
- context = super(ListView, self).get_context(request, items, context)
-
- template_obj_name = self.get_template_object_name(request, items)
+ }
+
+ template_obj_name = self.get_template_resource_name(request, items)
if template_obj_name:
context[template_obj_name] = items
+
return context
-
- def get_template_object_name(self, request, items):
+
+ def get_template_resource_name(self, request, items):
"""
Get the name of the item to be used in the context.
"""
- if self.template_object_name:
- return "%s_list" % self.template_object_name
+ if self.template_resource_name:
+ return "%s_list" % self.template_resource_name
elif hasattr(items, 'model'):
return smart_str(items.model._meta.verbose_name_plural)
else:
View
1 class_based_views/tests/tests/__init__.py
@@ -1,2 +1,3 @@
from class_based_views.tests.tests.base import ViewTest, DecoratorViewTest
from class_based_views.tests.tests.detail import DetailViewTest
+from class_based_views.tests.tests.list import ListViewTests
View
99 class_based_views/tests/tests/list.py
@@ -0,0 +1,99 @@
+from class_based_views.tests.models import Author
+from django.core.exceptions import ImproperlyConfigured
+from django.test import TestCase
+
+class ListViewTests(TestCase):
+ fixtures = ['generic-views-test-data.json']
+ urls = 'class_based_views.tests.urls'
+
+ def test_items(self):
+ res = self.client.get('/list/dict/')
+ self.assertEqual(res.status_code, 200)
+ self.assertTemplateUsed(res, 'tests/list.html')
+ self.assertEqual(res.context['object_list'][0]['first'], 'John')
+ self.assertEqual(res.context['paginator'], None)
+ self.assertEqual(res.context['page_obj'], None)
+ self.assertEqual(res.context['is_paginated'], False)
+
+ def test_queryset(self):
+ res = self.client.get('/list/authors/')
+ self.assertEqual(res.status_code, 200)
+ self.assertTemplateUsed(res, 'tests/list.html')
+ self.assertEqual(list(res.context['object_list']), list(Author.objects.all()))
+ self.assertEqual(list(res.context['authors']), list(Author.objects.all()))
+ self.assertEqual(res.context['paginator'], None)
+ self.assertEqual(res.context['page_obj'], None)
+ self.assertEqual(res.context['is_paginated'], False)
+
+ def test_paginated_queryset(self):
+ self._make_authors(100)
+ res = self.client.get('/list/authors/paginated/')
+ self.assertEqual(res.status_code, 200)
+ self.assertTemplateUsed(res, 'tests/list.html')
+ self.assertEqual(len(res.context['authors']), 30)
+ self.assertNotEqual(res.context['paginator'], None)
+ self.assertNotEqual(res.context['page_obj'], None)
+ self.assertEqual(res.context['is_paginated'], True)
+ self.assertEqual(res.context['page_obj'].number, 1)
+ self.assertEqual(res.context['paginator'].num_pages, 4)
+ self.assertEqual(res.context['authors'][0].name, 'Author 00')
+ self.assertEqual(list(res.context['authors'])[-1].name, 'Author 29')
+
+ def test_paginated_get_page_by_query_string(self):
+ self._make_authors(100)
+ res = self.client.get('/list/authors/paginated/', {'page': '2'})
+ self.assertEqual(res.status_code, 200)
+ self.assertTemplateUsed(res, 'tests/list.html')
+ self.assertEqual(len(res.context['authors']), 30)
+ self.assertEqual(res.context['authors'][0].name, 'Author 30')
+ self.assertEqual(res.context['page_obj'].number, 2)
+
+ def test_paginated_get_last_page_by_query_string(self):
+ self._make_authors(100)
+ res = self.client.get('/list/authors/paginated/', {'page': 'last'})
+ self.assertEqual(res.status_code, 200)
+ self.assertEqual(len(res.context['authors']), 10)
+ self.assertEqual(res.context['authors'][0].name, 'Author 90')
+ self.assertEqual(res.context['page_obj'].number, 4)
+
+ def test_paginated_get_page_by_urlvar(self):
+ self._make_authors(100)
+ res = self.client.get('/list/authors/paginated/3/')
+ self.assertEqual(res.status_code, 200)
+ self.assertTemplateUsed(res, 'tests/list.html')
+ self.assertEqual(len(res.context['authors']), 30)
+ self.assertEqual(res.context['authors'][0].name, 'Author 60')
+ self.assertEqual(res.context['page_obj'].number, 3)
+
+ def test_paginated_page_out_of_range(self):
+ self._make_authors(100)
+ res = self.client.get('/list/authors/paginated/42/')
+ self.assertEqual(res.status_code, 404)
+
+ def test_paginated_invalid_page(self):
+ self._make_authors(100)
+ res = self.client.get('/list/authors/paginated/?page=frog')
+ self.assertEqual(res.status_code, 404)
+
+ def test_allow_empty_false(self):
+ res = self.client.get('/list/authors/notempty/')
+ self.assertEqual(res.status_code, 200)
+ Author.objects.all().delete()
+ res = self.client.get('/list/authors/notempty/')
+ self.assertEqual(res.status_code, 404)
+
+ def test_template_object_name(self):
+ res = self.client.get('/list/authors/template_object_name/')
+ self.assertEqual(res.status_code, 200)
+ self.assertEqual(list(res.context['object_list']), list(Author.objects.all()))
+ self.assertEqual(list(res.context['author_list']), list(Author.objects.all()))
+ self.assert_('authors' not in res.context)
+
+ def test_missing_items(self):
+ self.assertRaises(ImproperlyConfigured, self.client.get, '/list/authors/invalid/')
+
+ def _make_authors(self, n):
+ Author.objects.all().delete()
+ for i in range(n):
+ Author.objects.create(name='Author %02i' % i, slug='a%s' % i)
+
View
24 class_based_views/tests/urls.py
@@ -6,11 +6,11 @@
(r'^about/login-required/$', views.DecoratedAboutView()),
# DetailView
- (r'^detail/obj/$', views.ObjectDetail()),
- url(r'^detail/author/(?P<pk>\d+)/$', views.AuthorDetail(), name="author_detail"),
+ (r'^detail/obj/$', views.ObjectDetail()),
+ url(r'^detail/author/(?P<pk>\d+)/$', views.AuthorDetail(), name="author_detail"),
(r'^detail/author/byslug/(?P<slug>[\w-]+)/$', views.AuthorDetail()),
- (r'^detail/author/invalid/url/$', views.AuthorDetail()),
- (r'^detail/author/invalid/qs/$', views.AuthorDetail(queryset=None)),
+ (r'^detail/author/invalid/url/$', views.AuthorDetail()),
+ (r'^detail/author/invalid/qs/$', views.AuthorDetail(queryset=None)),
# # EditView
# (r'^edit/authors/create/$', views.AuthorCreate()),
@@ -22,14 +22,14 @@
# (r'^dates/books/$', views.BookArchive()),
# (r'^dates/books/invalid/$', views.BookArchive(queryset=None)),
#
- # # ListView
- # (r'^list/dict/$', views.DictList()),
- # url(r'^list/authors/$', views.AuthorList(), name="authors_list"),
- # (r'^list/authors/paginated/$', views.AuthorList(paginate_by=30)),
- # (r'^list/authors/paginated/(?P<page>\d+)/$', views.AuthorList(paginate_by=30)),
- # (r'^list/authors/notempty/$', views.AuthorList(allow_empty=False)),
- # (r'^list/authors/template_object_name/$', views.AuthorList(template_object_name='author')),
- # (r'^list/authors/invalid/$', views.AuthorList(queryset=None)),
+ # ListView
+ (r'^list/dict/$', views.DictList()),
+ url(r'^list/authors/$', views.AuthorList(), name="authors_list"),
+ (r'^list/authors/paginated/$', views.AuthorList(paginate_by=30)),
+ (r'^list/authors/paginated/(?P<page>\d+)/$', views.AuthorList(paginate_by=30)),
+ (r'^list/authors/notempty/$', views.AuthorList(allow_empty=False)),
+ (r'^list/authors/template_object_name/$', views.AuthorList(template_resource_name='author')),
+ (r'^list/authors/invalid/$', views.AuthorList(queryset=None)),
#
# # YearView
# # Mixing keyword and possitional captures below is intentional; the views
View
22 class_based_views/tests/views.py
@@ -22,18 +22,18 @@ class DecoratedAboutView(class_based_views.View):
decorators = [login_required,]
+class DictList(class_based_views.ListView):
+ """A ListView that doesn't use a model."""
+ items = [
+ {'first': 'John', 'last': 'Lennon'},
+ {'last': 'Yoko', 'last': 'Ono'}
+ ]
+ template_name = 'tests/list.html'
-# class DictList(class_based_views.ListView):
-# """A ListView that doesn't use a model."""
-# items = [
-# {'first': 'John', 'last': 'Lennon'},
-# {'last': 'Yoko', 'last': 'Ono'}
-# ]
-# template_name = 'tests/list.html'
-#
-# class AuthorList(class_based_views.ListView):
-# queryset = Author.objects.all()
-# template_name = 'tests/list.html'
+
+class AuthorList(class_based_views.ListView):
+ queryset = Author.objects.all()
+ template_name = 'tests/list.html'

0 comments on commit 997784c

Please sign in to comment.