From 3ca3d2206c31ed251474d66b6775fa0903c09eae Mon Sep 17 00:00:00 2001 From: Preston Timmons Date: Sat, 6 Apr 2013 13:43:31 -0500 Subject: Modified generic_views for unittest2 discovery. --- tests/generic_views/base.py | 413 ------------------------ tests/generic_views/dates.py | 638 ------------------------------------- tests/generic_views/detail.py | 101 ------ tests/generic_views/edit.py | 317 ------------------ tests/generic_views/forms.py | 18 -- tests/generic_views/list.py | 217 ------------- tests/generic_views/test_base.py | 413 ++++++++++++++++++++++++ tests/generic_views/test_dates.py | 638 +++++++++++++++++++++++++++++++++++++ tests/generic_views/test_detail.py | 101 ++++++ tests/generic_views/test_edit.py | 317 ++++++++++++++++++ tests/generic_views/test_forms.py | 18 ++ tests/generic_views/test_list.py | 217 +++++++++++++ tests/generic_views/tests.py | 10 +- tests/generic_views/views.py | 2 +- 14 files changed, 1710 insertions(+), 1710 deletions(-) delete mode 100644 tests/generic_views/base.py delete mode 100644 tests/generic_views/dates.py delete mode 100644 tests/generic_views/detail.py delete mode 100644 tests/generic_views/edit.py delete mode 100644 tests/generic_views/forms.py delete mode 100644 tests/generic_views/list.py create mode 100644 tests/generic_views/test_base.py create mode 100644 tests/generic_views/test_dates.py create mode 100644 tests/generic_views/test_detail.py create mode 100644 tests/generic_views/test_edit.py create mode 100644 tests/generic_views/test_forms.py create mode 100644 tests/generic_views/test_list.py diff --git a/tests/generic_views/base.py b/tests/generic_views/base.py deleted file mode 100644 index 0e84e17132..0000000000 --- a/tests/generic_views/base.py +++ /dev/null @@ -1,413 +0,0 @@ -from __future__ import absolute_import - -import time - -from django.core.exceptions import ImproperlyConfigured -from django.http import HttpResponse -from django.test import TestCase, RequestFactory -from django.utils import unittest -from django.views.generic import View, TemplateView, RedirectView - -from . import views - -class SimpleView(View): - """ - A simple view with a docstring. - """ - def get(self, request): - return HttpResponse('This is a simple view') - - -class SimplePostView(SimpleView): - post = SimpleView.get - - -class PostOnlyView(View): - def post(self, request): - return HttpResponse('This view only accepts POST') - - -class CustomizableView(SimpleView): - parameter = {} - - -def decorator(view): - view.is_decorated = True - return view - - -class DecoratedDispatchView(SimpleView): - - @decorator - def dispatch(self, request, *args, **kwargs): - return super(DecoratedDispatchView, self).dispatch(request, *args, **kwargs) - - -class AboutTemplateView(TemplateView): - def get(self, request): - return self.render_to_response({}) - - def get_template_names(self): - return ['generic_views/about.html'] - - -class AboutTemplateAttributeView(TemplateView): - template_name = 'generic_views/about.html' - - def get(self, request): - return self.render_to_response(context={}) - - -class InstanceView(View): - - def get(self, request): - return self - - -class ViewTest(unittest.TestCase): - rf = RequestFactory() - - def _assert_simple(self, response): - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, b'This is a simple view') - - def test_no_init_kwargs(self): - """ - Test that a view can't be accidentally instantiated before deployment - """ - try: - view = SimpleView(key='value').as_view() - self.fail('Should not be able to instantiate a view') - except AttributeError: - pass - - def test_no_init_args(self): - """ - Test that a view can't be accidentally instantiated before deployment - """ - try: - view = SimpleView.as_view('value') - self.fail('Should not be able to use non-keyword arguments instantiating a view') - except TypeError: - pass - - def test_pathological_http_method(self): - """ - The edge case of a http request that spoofs an existing method name is caught. - """ - self.assertEqual(SimpleView.as_view()( - self.rf.get('/', REQUEST_METHOD='DISPATCH') - ).status_code, 405) - - def test_get_only(self): - """ - Test a view which only allows GET doesn't allow other methods. - """ - self._assert_simple(SimpleView.as_view()(self.rf.get('/'))) - self.assertEqual(SimpleView.as_view()(self.rf.post('/')).status_code, 405) - self.assertEqual(SimpleView.as_view()( - self.rf.get('/', REQUEST_METHOD='FAKE') - ).status_code, 405) - - def test_get_and_head(self): - """ - Test a view which supplies a GET method also responds correctly to HEAD. - """ - self._assert_simple(SimpleView.as_view()(self.rf.get('/'))) - response = SimpleView.as_view()(self.rf.head('/')) - self.assertEqual(response.status_code, 200) - - def test_head_no_get(self): - """ - Test a view which supplies no GET method responds to HEAD with HTTP 405. - """ - response = PostOnlyView.as_view()(self.rf.head('/')) - self.assertEqual(response.status_code, 405) - - def test_get_and_post(self): - """ - Test a view which only allows both GET and POST. - """ - self._assert_simple(SimplePostView.as_view()(self.rf.get('/'))) - self._assert_simple(SimplePostView.as_view()(self.rf.post('/'))) - self.assertEqual(SimplePostView.as_view()( - self.rf.get('/', REQUEST_METHOD='FAKE') - ).status_code, 405) - - def test_invalid_keyword_argument(self): - """ - Test that view arguments must be predefined on the class and can't - be named like a HTTP method. - """ - # Check each of the allowed method names - for method in SimpleView.http_method_names: - kwargs = dict(((method, "value"),)) - self.assertRaises(TypeError, SimpleView.as_view, **kwargs) - - # Check the case view argument is ok if predefined on the class... - CustomizableView.as_view(parameter="value") - # ...but raises errors otherwise. - self.assertRaises(TypeError, CustomizableView.as_view, foobar="value") - - def test_calling_more_than_once(self): - """ - Test a view can only be called once. - """ - request = self.rf.get('/') - view = InstanceView.as_view() - self.assertNotEqual(view(request), view(request)) - - def test_class_attributes(self): - """ - Test that the callable returned from as_view() has proper - docstring, name and module. - """ - self.assertEqual(SimpleView.__doc__, SimpleView.as_view().__doc__) - self.assertEqual(SimpleView.__name__, SimpleView.as_view().__name__) - self.assertEqual(SimpleView.__module__, SimpleView.as_view().__module__) - - def test_dispatch_decoration(self): - """ - Test that attributes set by decorators on the dispatch method - are also present on the closure. - """ - self.assertTrue(DecoratedDispatchView.as_view().is_decorated) - - def test_options(self): - """ - Test that views respond to HTTP OPTIONS requests with an Allow header - appropriate for the methods implemented by the view class. - """ - request = self.rf.options('/') - view = SimpleView.as_view() - response = view(request) - self.assertEqual(200, response.status_code) - self.assertTrue(response['Allow']) - - def test_options_for_get_view(self): - """ - Test that a view implementing GET allows GET and HEAD. - """ - request = self.rf.options('/') - view = SimpleView.as_view() - response = view(request) - self._assert_allows(response, 'GET', 'HEAD') - - def test_options_for_get_and_post_view(self): - """ - Test that a view implementing GET and POST allows GET, HEAD, and POST. - """ - request = self.rf.options('/') - view = SimplePostView.as_view() - response = view(request) - self._assert_allows(response, 'GET', 'HEAD', 'POST') - - def test_options_for_post_view(self): - """ - Test that a view implementing POST allows POST. - """ - request = self.rf.options('/') - view = PostOnlyView.as_view() - response = view(request) - self._assert_allows(response, 'POST') - - def _assert_allows(self, response, *expected_methods): - "Assert allowed HTTP methods reported in the Allow response header" - response_allows = set(response['Allow'].split(', ')) - self.assertEqual(set(expected_methods + ('OPTIONS',)), response_allows) - - def test_args_kwargs_request_on_self(self): - """ - Test a view only has args, kwargs & request once `as_view` - has been called. - """ - bare_view = InstanceView() - view = InstanceView.as_view()(self.rf.get('/')) - for attribute in ('args', 'kwargs', 'request'): - self.assertNotIn(attribute, dir(bare_view)) - self.assertIn(attribute, dir(view)) - - -class TemplateViewTest(TestCase): - urls = 'generic_views.urls' - - rf = RequestFactory() - - def _assert_about(self, response): - response.render() - self.assertEqual(response.status_code, 200) - self.assertContains(response, '

About

') - - def test_get(self): - """ - Test a view that simply renders a template on GET - """ - self._assert_about(AboutTemplateView.as_view()(self.rf.get('/about/'))) - - def test_head(self): - """ - Test a TemplateView responds correctly to HEAD - """ - response = AboutTemplateView.as_view()(self.rf.head('/about/')) - self.assertEqual(response.status_code, 200) - - def test_get_template_attribute(self): - """ - Test a view that renders a template on GET with the template name as - an attribute on the class. - """ - self._assert_about(AboutTemplateAttributeView.as_view()(self.rf.get('/about/'))) - - def test_get_generic_template(self): - """ - Test a completely generic view that renders a template on GET - with the template name as an argument at instantiation. - """ - self._assert_about(TemplateView.as_view(template_name='generic_views/about.html')(self.rf.get('/about/'))) - - def test_template_name_required(self): - """ - A template view must provide a template name - """ - self.assertRaises(ImproperlyConfigured, self.client.get, '/template/no_template/') - - def test_template_params(self): - """ - A generic template view passes kwargs as context. - """ - response = self.client.get('/template/simple/bar/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['foo'], 'bar') - self.assertTrue(isinstance(response.context['view'], View)) - - def test_extra_template_params(self): - """ - A template view can be customized to return extra context. - """ - response = self.client.get('/template/custom/bar/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['foo'], 'bar') - self.assertEqual(response.context['key'], 'value') - self.assertTrue(isinstance(response.context['view'], View)) - - def test_cached_views(self): - """ - A template view can be cached - """ - response = self.client.get('/template/cached/bar/') - self.assertEqual(response.status_code, 200) - - time.sleep(1.0) - - response2 = self.client.get('/template/cached/bar/') - self.assertEqual(response2.status_code, 200) - - self.assertEqual(response.content, response2.content) - - time.sleep(2.0) - - # Let the cache expire and test again - response2 = self.client.get('/template/cached/bar/') - self.assertEqual(response2.status_code, 200) - - self.assertNotEqual(response.content, response2.content) - - def test_content_type(self): - response = self.client.get('/template/content_type/') - self.assertEqual(response['Content-Type'], 'text/plain') - - -class RedirectViewTest(unittest.TestCase): - rf = RequestFactory() - - def test_no_url(self): - "Without any configuration, returns HTTP 410 GONE" - response = RedirectView.as_view()(self.rf.get('/foo/')) - self.assertEqual(response.status_code, 410) - - def test_permanent_redirect(self): - "Default is a permanent redirect" - response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/') - - def test_temporary_redirect(self): - "Permanent redirects are an option" - response = RedirectView.as_view(url='/bar/', permanent=False)(self.rf.get('/foo/')) - self.assertEqual(response.status_code, 302) - self.assertEqual(response.url, '/bar/') - - def test_include_args(self): - "GET arguments can be included in the redirected URL" - response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/') - - response = RedirectView.as_view(url='/bar/', query_string=True)(self.rf.get('/foo/?pork=spam')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/?pork=spam') - - def test_include_urlencoded_args(self): - "GET arguments can be URL-encoded when included in the redirected URL" - response = RedirectView.as_view(url='/bar/', query_string=True)( - self.rf.get('/foo/?unicode=%E2%9C%93')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/?unicode=%E2%9C%93') - - def test_parameter_substitution(self): - "Redirection URLs can be parameterized" - response = RedirectView.as_view(url='/bar/%(object_id)d/')(self.rf.get('/foo/42/'), object_id=42) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/42/') - - def test_redirect_POST(self): - "Default is a permanent redirect" - response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/') - - def test_redirect_HEAD(self): - "Default is a permanent redirect" - response = RedirectView.as_view(url='/bar/')(self.rf.head('/foo/')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/') - - def test_redirect_OPTIONS(self): - "Default is a permanent redirect" - response = RedirectView.as_view(url='/bar/')(self.rf.options('/foo/')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/') - - def test_redirect_PUT(self): - "Default is a permanent redirect" - response = RedirectView.as_view(url='/bar/')(self.rf.put('/foo/')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/') - - def test_redirect_DELETE(self): - "Default is a permanent redirect" - response = RedirectView.as_view(url='/bar/')(self.rf.delete('/foo/')) - self.assertEqual(response.status_code, 301) - self.assertEqual(response.url, '/bar/') - - def test_redirect_when_meta_contains_no_query_string(self): - "regression for #16705" - # we can't use self.rf.get because it always sets QUERY_STRING - response = RedirectView.as_view(url='/bar/')(self.rf.request(PATH_INFO='/foo/')) - self.assertEqual(response.status_code, 301) - - -class GetContextDataTest(unittest.TestCase): - - def test_get_context_data_super(self): - test_view = views.CustomContextView() - context = test_view.get_context_data(kwarg_test='kwarg_value') - - # the test_name key is inserted by the test classes parent - self.assertTrue('test_name' in context) - self.assertEqual(context['kwarg_test'], 'kwarg_value') - self.assertEqual(context['custom_key'], 'custom_value') - - # test that kwarg overrides values assigned higher up - context = test_view.get_context_data(test_name='test_value') - self.assertEqual(context['test_name'], 'test_value') diff --git a/tests/generic_views/dates.py b/tests/generic_views/dates.py deleted file mode 100644 index 263326322e..0000000000 --- a/tests/generic_views/dates.py +++ /dev/null @@ -1,638 +0,0 @@ -from __future__ import absolute_import - -import time -import datetime - -from django.core.exceptions import ImproperlyConfigured -from django.test import TestCase, skipUnlessDBFeature -from django.test.utils import override_settings -from django.utils import timezone -from django.utils.unittest import skipUnless - -from .models import Book, BookSigning - -TZ_SUPPORT = hasattr(time, 'tzset') - -# On OSes that don't provide tzset (Windows), we can't set the timezone -# in which the program runs. As a consequence, we must skip tests that -# don't enforce a specific timezone (with timezone.override or equivalent), -# or attempt to interpret naive datetimes in the default timezone. - -requires_tz_support = skipUnless(TZ_SUPPORT, - "This test relies on the ability to run a program in an arbitrary " - "time zone, but your operating system isn't able to do that.") - - -def _make_books(n, base_date): - for i in range(n): - b = Book.objects.create( - name='Book %d' % i, - slug='book-%d' % i, - pages=100+i, - pubdate=base_date - datetime.timedelta(days=i)) - -class ArchiveIndexViewTests(TestCase): - fixtures = ['generic-views-test-data.json'] - urls = 'generic_views.urls' - - - def test_archive_view(self): - res = self.client.get('/dates/books/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) - self.assertEqual(list(res.context['latest']), list(Book.objects.all())) - self.assertTemplateUsed(res, 'generic_views/book_archive.html') - - def test_archive_view_context_object_name(self): - res = self.client.get('/dates/books/context_object_name/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) - self.assertEqual(list(res.context['thingies']), list(Book.objects.all())) - self.assertFalse('latest' in res.context) - self.assertTemplateUsed(res, 'generic_views/book_archive.html') - - def test_empty_archive_view(self): - Book.objects.all().delete() - res = self.client.get('/dates/books/') - self.assertEqual(res.status_code, 404) - - def test_allow_empty_archive_view(self): - Book.objects.all().delete() - res = self.client.get('/dates/books/allow_empty/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), []) - self.assertTemplateUsed(res, 'generic_views/book_archive.html') - - def test_archive_view_template(self): - res = self.client.get('/dates/books/template_name/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) - self.assertEqual(list(res.context['latest']), list(Book.objects.all())) - self.assertTemplateUsed(res, 'generic_views/list.html') - - def test_archive_view_template_suffix(self): - res = self.client.get('/dates/books/template_name_suffix/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) - self.assertEqual(list(res.context['latest']), list(Book.objects.all())) - self.assertTemplateUsed(res, 'generic_views/book_detail.html') - - def test_archive_view_invalid(self): - self.assertRaises(ImproperlyConfigured, self.client.get, '/dates/books/invalid/') - - def test_archive_view_by_month(self): - res = self.client.get('/dates/books/by_month/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'month', 'DESC'))) - - def test_paginated_archive_view(self): - _make_books(20, base_date=datetime.date.today()) - res = self.client.get('/dates/books/paginated/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) - self.assertEqual(list(res.context['latest']), list(Book.objects.all()[0:10])) - self.assertTemplateUsed(res, 'generic_views/book_archive.html') - - res = self.client.get('/dates/books/paginated/?page=2') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['page_obj'].number, 2) - self.assertEqual(list(res.context['latest']), list(Book.objects.all()[10:20])) - - def test_paginated_archive_view_does_not_load_entire_table(self): - # Regression test for #18087 - _make_books(20, base_date=datetime.date.today()) - # 1 query for years list + 1 query for books - with self.assertNumQueries(2): - self.client.get('/dates/books/') - # same as above + 1 query to test if books exist + 1 query to count them - with self.assertNumQueries(4): - self.client.get('/dates/books/paginated/') - - def test_no_duplicate_query(self): - # Regression test for #18354 - with self.assertNumQueries(2): - self.client.get('/dates/books/reverse/') - - def test_datetime_archive_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) - res = self.client.get('/dates/booksignings/') - self.assertEqual(res.status_code, 200) - - @requires_tz_support - @skipUnlessDBFeature('has_zoneinfo_database') - @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') - def test_aware_datetime_archive_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) - res = self.client.get('/dates/booksignings/') - self.assertEqual(res.status_code, 200) - - def test_date_list_order(self): - """date_list should be sorted descending in index""" - _make_books(5, base_date=datetime.date(2011, 12, 25)) - res = self.client.get('/dates/books/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), list(reversed(sorted(res.context['date_list'])))) - - -class YearArchiveViewTests(TestCase): - fixtures = ['generic-views-test-data.json'] - urls = 'generic_views.urls' - - def test_year_view(self): - res = self.client.get('/dates/books/2008/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), [datetime.date(2008, 10, 1)]) - self.assertEqual(res.context['year'], datetime.date(2008, 1, 1)) - self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') - - # Since allow_empty=False, next/prev years must be valid (#7164) - self.assertEqual(res.context['next_year'], None) - self.assertEqual(res.context['previous_year'], datetime.date(2006, 1, 1)) - - def test_year_view_make_object_list(self): - res = self.client.get('/dates/books/2006/make_object_list/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), [datetime.date(2006, 5, 1)]) - self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2006))) - self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006))) - self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') - - def test_year_view_empty(self): - res = self.client.get('/dates/books/1999/') - self.assertEqual(res.status_code, 404) - res = self.client.get('/dates/books/1999/allow_empty/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), []) - self.assertEqual(list(res.context['book_list']), []) - - # Since allow_empty=True, next/prev are allowed to be empty years (#7164) - self.assertEqual(res.context['next_year'], datetime.date(2000, 1, 1)) - self.assertEqual(res.context['previous_year'], datetime.date(1998, 1, 1)) - - def test_year_view_allow_future(self): - # Create a new book in the future - year = datetime.date.today().year + 1 - b = Book.objects.create(name="The New New Testement", pages=600, pubdate=datetime.date(year, 1, 1)) - res = self.client.get('/dates/books/%s/' % year) - self.assertEqual(res.status_code, 404) - - res = self.client.get('/dates/books/%s/allow_empty/' % year) - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), []) - - res = self.client.get('/dates/books/%s/allow_future/' % year) - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), [datetime.date(year, 1, 1)]) - - def test_year_view_paginated(self): - res = self.client.get('/dates/books/2006/paginated/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2006))) - self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006))) - self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') - - def test_year_view_invalid_pattern(self): - res = self.client.get('/dates/books/no_year/') - self.assertEqual(res.status_code, 404) - - def test_no_duplicate_query(self): - # Regression test for #18354 - with self.assertNumQueries(4): - self.client.get('/dates/books/2008/reverse/') - - def test_datetime_year_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) - res = self.client.get('/dates/booksignings/2008/') - self.assertEqual(res.status_code, 200) - - @skipUnlessDBFeature('has_zoneinfo_database') - @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') - def test_aware_datetime_year_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) - res = self.client.get('/dates/booksignings/2008/') - self.assertEqual(res.status_code, 200) - - def test_date_list_order(self): - """date_list should be sorted ascending in year view""" - _make_books(10, base_date=datetime.date(2011, 12, 25)) - res = self.client.get('/dates/books/2011/') - self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list']))) - - -class MonthArchiveViewTests(TestCase): - fixtures = ['generic-views-test-data.json'] - urls = 'generic_views.urls' - - def test_month_view(self): - res = self.client.get('/dates/books/2008/oct/') - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/book_archive_month.html') - self.assertEqual(list(res.context['date_list']), [datetime.date(2008, 10, 1)]) - self.assertEqual(list(res.context['book_list']), - list(Book.objects.filter(pubdate=datetime.date(2008, 10, 1)))) - self.assertEqual(res.context['month'], datetime.date(2008, 10, 1)) - - # Since allow_empty=False, next/prev months must be valid (#7164) - self.assertEqual(res.context['next_month'], None) - self.assertEqual(res.context['previous_month'], datetime.date(2006, 5, 1)) - - def test_month_view_allow_empty(self): - # allow_empty = False, empty month - res = self.client.get('/dates/books/2000/jan/') - self.assertEqual(res.status_code, 404) - - # allow_empty = True, empty month - res = self.client.get('/dates/books/2000/jan/allow_empty/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['date_list']), []) - self.assertEqual(list(res.context['book_list']), []) - self.assertEqual(res.context['month'], datetime.date(2000, 1, 1)) - - # Since allow_empty=True, next/prev are allowed to be empty months (#7164) - self.assertEqual(res.context['next_month'], datetime.date(2000, 2, 1)) - self.assertEqual(res.context['previous_month'], datetime.date(1999, 12, 1)) - - # allow_empty but not allow_future: next_month should be empty (#7164) - url = datetime.date.today().strftime('/dates/books/%Y/%b/allow_empty/').lower() - res = self.client.get(url) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['next_month'], None) - - def test_month_view_allow_future(self): - future = (datetime.date.today() + datetime.timedelta(days=60)).replace(day=1) - urlbit = future.strftime('%Y/%b').lower() - b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) - - # allow_future = False, future month - res = self.client.get('/dates/books/%s/' % urlbit) - self.assertEqual(res.status_code, 404) - - # allow_future = True, valid future month - res = self.client.get('/dates/books/%s/allow_future/' % urlbit) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['date_list'][0], b.pubdate) - self.assertEqual(list(res.context['book_list']), [b]) - self.assertEqual(res.context['month'], future) - - # Since allow_future = True but not allow_empty, next/prev are not - # allowed to be empty months (#7164) - self.assertEqual(res.context['next_month'], None) - self.assertEqual(res.context['previous_month'], datetime.date(2008, 10, 1)) - - # allow_future, but not allow_empty, with a current month. So next - # should be in the future (yup, #7164, again) - res = self.client.get('/dates/books/2008/oct/allow_future/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['next_month'], future) - self.assertEqual(res.context['previous_month'], datetime.date(2006, 5, 1)) - - def test_month_view_paginated(self): - res = self.client.get('/dates/books/2008/oct/paginated/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2008, pubdate__month=10))) - self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2008, pubdate__month=10))) - self.assertTemplateUsed(res, 'generic_views/book_archive_month.html') - - def test_custom_month_format(self): - res = self.client.get('/dates/books/2008/10/') - self.assertEqual(res.status_code, 200) - - def test_month_view_invalid_pattern(self): - res = self.client.get('/dates/books/2007/no_month/') - self.assertEqual(res.status_code, 404) - - def test_previous_month_without_content(self): - "Content can exist on any day of the previous month. Refs #14711" - self.pubdate_list = [ - datetime.date(2010, month, day) - for month,day in ((9,1), (10,2), (11,3)) - ] - for pubdate in self.pubdate_list: - name = str(pubdate) - Book.objects.create(name=name, slug=name, pages=100, pubdate=pubdate) - - res = self.client.get('/dates/books/2010/nov/allow_empty/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['previous_month'], datetime.date(2010,10,1)) - # The following test demonstrates the bug - res = self.client.get('/dates/books/2010/nov/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['previous_month'], datetime.date(2010,10,1)) - # The bug does not occur here because a Book with pubdate of Sep 1 exists - res = self.client.get('/dates/books/2010/oct/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['previous_month'], datetime.date(2010,9,1)) - - def test_datetime_month_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0)) - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) - BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0)) - res = self.client.get('/dates/booksignings/2008/apr/') - self.assertEqual(res.status_code, 200) - - @skipUnlessDBFeature('has_zoneinfo_database') - @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') - def test_aware_datetime_month_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0, tzinfo=timezone.utc)) - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) - BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0, tzinfo=timezone.utc)) - res = self.client.get('/dates/booksignings/2008/apr/') - self.assertEqual(res.status_code, 200) - - def test_date_list_order(self): - """date_list should be sorted ascending in month view""" - _make_books(10, base_date=datetime.date(2011, 12, 25)) - res = self.client.get('/dates/books/2011/dec/') - self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list']))) - - -class WeekArchiveViewTests(TestCase): - fixtures = ['generic-views-test-data.json'] - urls = 'generic_views.urls' - - def test_week_view(self): - res = self.client.get('/dates/books/2008/week/39/') - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/book_archive_week.html') - self.assertEqual(res.context['book_list'][0], Book.objects.get(pubdate=datetime.date(2008, 10, 1))) - self.assertEqual(res.context['week'], datetime.date(2008, 9, 28)) - - # Since allow_empty=False, next/prev weeks must be valid - self.assertEqual(res.context['next_week'], None) - self.assertEqual(res.context['previous_week'], datetime.date(2006, 4, 30)) - - def test_week_view_allow_empty(self): - # allow_empty = False, empty week - res = self.client.get('/dates/books/2008/week/12/') - self.assertEqual(res.status_code, 404) - - # allow_empty = True, empty month - res = self.client.get('/dates/books/2008/week/12/allow_empty/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), []) - self.assertEqual(res.context['week'], datetime.date(2008, 3, 23)) - - # Since allow_empty=True, next/prev are allowed to be empty weeks - self.assertEqual(res.context['next_week'], datetime.date(2008, 3, 30)) - self.assertEqual(res.context['previous_week'], datetime.date(2008, 3, 16)) - - # allow_empty but not allow_future: next_week should be empty - url = datetime.date.today().strftime('/dates/books/%Y/week/%U/allow_empty/').lower() - res = self.client.get(url) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['next_week'], None) - - def test_week_view_allow_future(self): - # January 7th always falls in week 1, given Python's definition of week numbers - future = datetime.date(datetime.date.today().year + 1, 1, 7) - future_sunday = future - datetime.timedelta(days=(future.weekday() + 1) % 7) - b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) - - res = self.client.get('/dates/books/%s/week/1/' % future.year) - self.assertEqual(res.status_code, 404) - - res = self.client.get('/dates/books/%s/week/1/allow_future/' % future.year) - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), [b]) - self.assertEqual(res.context['week'], future_sunday) - - # Since allow_future = True but not allow_empty, next/prev are not - # allowed to be empty weeks - self.assertEqual(res.context['next_week'], None) - self.assertEqual(res.context['previous_week'], datetime.date(2008, 9, 28)) - - # allow_future, but not allow_empty, with a current week. So next - # should be in the future - res = self.client.get('/dates/books/2008/week/39/allow_future/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['next_week'], future_sunday) - self.assertEqual(res.context['previous_week'], datetime.date(2006, 4, 30)) - - def test_week_view_paginated(self): - week_start = datetime.date(2008, 9, 28) - week_end = week_start + datetime.timedelta(days=7) - res = self.client.get('/dates/books/2008/week/39/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__gte=week_start, pubdate__lt=week_end))) - self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__gte=week_start, pubdate__lt=week_end))) - self.assertTemplateUsed(res, 'generic_views/book_archive_week.html') - - def test_week_view_invalid_pattern(self): - res = self.client.get('/dates/books/2007/week/no_week/') - self.assertEqual(res.status_code, 404) - - def test_week_start_Monday(self): - # Regression for #14752 - res = self.client.get('/dates/books/2008/week/39/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['week'], datetime.date(2008, 9, 28)) - - res = self.client.get('/dates/books/2008/week/39/monday/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['week'], datetime.date(2008, 9, 29)) - - def test_datetime_week_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) - res = self.client.get('/dates/booksignings/2008/week/13/') - self.assertEqual(res.status_code, 200) - - @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') - def test_aware_datetime_week_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) - res = self.client.get('/dates/booksignings/2008/week/13/') - self.assertEqual(res.status_code, 200) - - -class DayArchiveViewTests(TestCase): - fixtures = ['generic-views-test-data.json'] - urls = 'generic_views.urls' - - def test_day_view(self): - res = self.client.get('/dates/books/2008/oct/01/') - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/book_archive_day.html') - self.assertEqual(list(res.context['book_list']), - list(Book.objects.filter(pubdate=datetime.date(2008, 10, 1)))) - self.assertEqual(res.context['day'], datetime.date(2008, 10, 1)) - - # Since allow_empty=False, next/prev days must be valid. - self.assertEqual(res.context['next_day'], None) - self.assertEqual(res.context['previous_day'], datetime.date(2006, 5, 1)) - - def test_day_view_allow_empty(self): - # allow_empty = False, empty month - res = self.client.get('/dates/books/2000/jan/1/') - self.assertEqual(res.status_code, 404) - - # allow_empty = True, empty month - res = self.client.get('/dates/books/2000/jan/1/allow_empty/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), []) - self.assertEqual(res.context['day'], datetime.date(2000, 1, 1)) - - # Since it's allow empty, next/prev are allowed to be empty months (#7164) - self.assertEqual(res.context['next_day'], datetime.date(2000, 1, 2)) - self.assertEqual(res.context['previous_day'], datetime.date(1999, 12, 31)) - - # allow_empty but not allow_future: next_month should be empty (#7164) - url = datetime.date.today().strftime('/dates/books/%Y/%b/%d/allow_empty/').lower() - res = self.client.get(url) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['next_day'], None) - - def test_day_view_allow_future(self): - future = (datetime.date.today() + datetime.timedelta(days=60)) - urlbit = future.strftime('%Y/%b/%d').lower() - b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) - - # allow_future = False, future month - res = self.client.get('/dates/books/%s/' % urlbit) - self.assertEqual(res.status_code, 404) - - # allow_future = True, valid future month - res = self.client.get('/dates/books/%s/allow_future/' % urlbit) - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), [b]) - self.assertEqual(res.context['day'], future) - - # allow_future but not allow_empty, next/prev must be valid - self.assertEqual(res.context['next_day'], None) - self.assertEqual(res.context['previous_day'], datetime.date(2008, 10, 1)) - - # allow_future, but not allow_empty, with a current month. - res = self.client.get('/dates/books/2008/oct/01/allow_future/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['next_day'], future) - self.assertEqual(res.context['previous_day'], datetime.date(2006, 5, 1)) - - # allow_future for yesterday, next_day is today (#17192) - today = datetime.date.today() - yesterday = today - datetime.timedelta(days=1) - res = self.client.get('/dates/books/%s/allow_empty_and_future/' - % yesterday.strftime('%Y/%b/%d').lower()) - self.assertEqual(res.context['next_day'], today) - - def test_day_view_paginated(self): - res = self.client.get('/dates/books/2008/oct/1/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2008, pubdate__month=10, pubdate__day=1))) - self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2008, pubdate__month=10, pubdate__day=1))) - self.assertTemplateUsed(res, 'generic_views/book_archive_day.html') - - def test_next_prev_context(self): - res = self.client.get('/dates/books/2008/oct/01/') - self.assertEqual(res.content, b"Archive for Oct. 1, 2008. Previous day is May 1, 2006") - - def test_custom_month_format(self): - res = self.client.get('/dates/books/2008/10/01/') - self.assertEqual(res.status_code, 200) - - def test_day_view_invalid_pattern(self): - res = self.client.get('/dates/books/2007/oct/no_day/') - self.assertEqual(res.status_code, 404) - - def test_today_view(self): - res = self.client.get('/dates/books/today/') - self.assertEqual(res.status_code, 404) - res = self.client.get('/dates/books/today/allow_empty/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['day'], datetime.date.today()) - - def test_datetime_day_view(self): - BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) - res = self.client.get('/dates/booksignings/2008/apr/2/') - self.assertEqual(res.status_code, 200) - - @requires_tz_support - @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') - def test_aware_datetime_day_view(self): - bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) - res = self.client.get('/dates/booksignings/2008/apr/2/') - self.assertEqual(res.status_code, 200) - # 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date) - bs.event_date = datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc) - bs.save() - res = self.client.get('/dates/booksignings/2008/apr/2/') - self.assertEqual(res.status_code, 200) - # 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date) - bs.event_date = datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc) - bs.save() - res = self.client.get('/dates/booksignings/2008/apr/2/') - self.assertEqual(res.status_code, 404) - - -class DateDetailViewTests(TestCase): - fixtures = ['generic-views-test-data.json'] - urls = 'generic_views.urls' - - def test_date_detail_by_pk(self): - res = self.client.get('/dates/books/2008/oct/01/1/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Book.objects.get(pk=1)) - self.assertEqual(res.context['book'], Book.objects.get(pk=1)) - self.assertTemplateUsed(res, 'generic_views/book_detail.html') - - def test_date_detail_by_slug(self): - res = self.client.get('/dates/books/2006/may/01/byslug/dreaming-in-code/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['book'], Book.objects.get(slug='dreaming-in-code')) - - def test_date_detail_custom_month_format(self): - res = self.client.get('/dates/books/2008/10/01/1/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['book'], Book.objects.get(pk=1)) - - def test_date_detail_allow_future(self): - future = (datetime.date.today() + datetime.timedelta(days=60)) - urlbit = future.strftime('%Y/%b/%d').lower() - b = Book.objects.create(name="The New New Testement", slug="new-new", pages=600, pubdate=future) - - res = self.client.get('/dates/books/%s/new-new/' % urlbit) - self.assertEqual(res.status_code, 404) - - res = self.client.get('/dates/books/%s/%s/allow_future/' % (urlbit, b.id)) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['book'], b) - self.assertTemplateUsed(res, 'generic_views/book_detail.html') - - def test_invalid_url(self): - self.assertRaises(AttributeError, self.client.get, "/dates/books/2008/oct/01/nopk/") - - def test_get_object_custom_queryset(self): - """ - Ensure that custom querysets are used when provided to - BaseDateDetailView.get_object() - Refs #16918. - """ - res = self.client.get( - '/dates/books/get_object_custom_queryset/2006/may/01/2/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Book.objects.get(pk=2)) - self.assertEqual(res.context['book'], Book.objects.get(pk=2)) - self.assertTemplateUsed(res, 'generic_views/book_detail.html') - - res = self.client.get( - '/dates/books/get_object_custom_queryset/2008/oct/01/1/') - self.assertEqual(res.status_code, 404) - - def test_datetime_date_detail(self): - bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) - res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) - self.assertEqual(res.status_code, 200) - - @requires_tz_support - @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') - def test_aware_datetime_date_detail(self): - bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) - res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) - self.assertEqual(res.status_code, 200) - # 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date) - bs.event_date = datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc) - bs.save() - res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) - self.assertEqual(res.status_code, 200) - # 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date) - bs.event_date = datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc) - bs.save() - res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) - self.assertEqual(res.status_code, 404) diff --git a/tests/generic_views/detail.py b/tests/generic_views/detail.py deleted file mode 100644 index faee05688e..0000000000 --- a/tests/generic_views/detail.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import absolute_import - -from django.core.exceptions import ImproperlyConfigured -from django.test import TestCase -from django.views.generic.base import View - -from .models import Artist, Author, Page - - -class DetailViewTest(TestCase): - fixtures = ['generic-views-test-data.json'] - urls = 'generic_views.urls' - - def test_simple_object(self): - res = self.client.get('/detail/obj/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], {'foo': 'bar'}) - self.assertTrue(isinstance(res.context['view'], View)) - self.assertTemplateUsed(res, 'generic_views/detail.html') - - def test_detail_by_pk(self): - res = self.client.get('/detail/author/1/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(pk=1)) - self.assertEqual(res.context['author'], Author.objects.get(pk=1)) - self.assertTemplateUsed(res, 'generic_views/author_detail.html') - - def test_detail_by_custom_pk(self): - res = self.client.get('/detail/author/bycustompk/1/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(pk=1)) - self.assertEqual(res.context['author'], Author.objects.get(pk=1)) - self.assertTemplateUsed(res, 'generic_views/author_detail.html') - - def test_detail_by_slug(self): - res = self.client.get('/detail/author/byslug/scott-rosenberg/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(slug='scott-rosenberg')) - self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg')) - self.assertTemplateUsed(res, 'generic_views/author_detail.html') - - def test_detail_by_custom_slug(self): - res = self.client.get('/detail/author/bycustomslug/scott-rosenberg/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(slug='scott-rosenberg')) - self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg')) - self.assertTemplateUsed(res, 'generic_views/author_detail.html') - - def test_verbose_name(self): - res = self.client.get('/detail/artist/1/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Artist.objects.get(pk=1)) - self.assertEqual(res.context['artist'], Artist.objects.get(pk=1)) - self.assertTemplateUsed(res, 'generic_views/artist_detail.html') - - def test_template_name(self): - res = self.client.get('/detail/author/1/template_name/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(pk=1)) - self.assertEqual(res.context['author'], Author.objects.get(pk=1)) - self.assertTemplateUsed(res, 'generic_views/about.html') - - def test_template_name_suffix(self): - res = self.client.get('/detail/author/1/template_name_suffix/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(pk=1)) - self.assertEqual(res.context['author'], Author.objects.get(pk=1)) - self.assertTemplateUsed(res, 'generic_views/author_view.html') - - def test_template_name_field(self): - res = self.client.get('/detail/page/1/field/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Page.objects.get(pk=1)) - self.assertEqual(res.context['page'], Page.objects.get(pk=1)) - self.assertTemplateUsed(res, 'generic_views/page_template.html') - - def test_context_object_name(self): - res = self.client.get('/detail/author/1/context_object_name/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(pk=1)) - self.assertEqual(res.context['thingy'], Author.objects.get(pk=1)) - self.assertFalse('author' in res.context) - self.assertTemplateUsed(res, 'generic_views/author_detail.html') - - def test_duplicated_context_object_name(self): - res = self.client.get('/detail/author/1/dupe_context_object_name/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(pk=1)) - self.assertFalse('author' in res.context) - self.assertTemplateUsed(res, 'generic_views/author_detail.html') - - def test_invalid_url(self): - self.assertRaises(AttributeError, self.client.get, '/detail/author/invalid/url/') - - def test_invalid_queryset(self): - self.assertRaises(ImproperlyConfigured, self.client.get, '/detail/author/invalid/qs/') - - def test_non_model_object_with_meta(self): - res = self.client.get('/detail/nonmodel/1/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'].id, "non_model_1") diff --git a/tests/generic_views/edit.py b/tests/generic_views/edit.py deleted file mode 100644 index c54d632363..0000000000 --- a/tests/generic_views/edit.py +++ /dev/null @@ -1,317 +0,0 @@ -from __future__ import absolute_import - -from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import reverse -from django import forms -from django.test import TestCase -from django.utils.unittest import expectedFailure -from django.views.generic.base import View -from django.views.generic.edit import FormMixin - -from . import views -from .models import Artist, Author - - -class FormMixinTests(TestCase): - def test_initial_data(self): - """ Test instance independence of initial data dict (see #16138) """ - initial_1 = FormMixin().get_initial() - initial_1['foo'] = 'bar' - initial_2 = FormMixin().get_initial() - self.assertNotEqual(initial_1, initial_2) - - -class BasicFormTests(TestCase): - urls = 'generic_views.urls' - - def test_post_data(self): - res = self.client.post('/contact/', {'name': "Me", 'message': "Hello"}) - self.assertRedirects(res, 'http://testserver/list/authors/') - - -class ModelFormMixinTests(TestCase): - def test_get_form(self): - form_class = views.AuthorGetQuerySetFormView().get_form_class() - self.assertEqual(form_class._meta.model, Author) - -class CreateViewTests(TestCase): - urls = 'generic_views.urls' - - def test_create(self): - res = self.client.get('/edit/authors/create/') - self.assertEqual(res.status_code, 200) - self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) - self.assertTrue(isinstance(res.context['view'], View)) - self.assertFalse('object' in res.context) - self.assertFalse('author' in res.context) - self.assertTemplateUsed(res, 'generic_views/author_form.html') - - res = self.client.post('/edit/authors/create/', - {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/list/authors/') - self.assertQuerysetEqual(Author.objects.all(), ['']) - - def test_create_invalid(self): - res = self.client.post('/edit/authors/create/', - {'name': 'A' * 101, 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/author_form.html') - self.assertEqual(len(res.context['form'].errors), 1) - self.assertEqual(Author.objects.count(), 0) - - def test_create_with_object_url(self): - res = self.client.post('/edit/artists/create/', - {'name': 'Rene Magritte'}) - self.assertEqual(res.status_code, 302) - artist = Artist.objects.get(name='Rene Magritte') - self.assertRedirects(res, 'http://testserver/detail/artist/%d/' % artist.pk) - self.assertQuerysetEqual(Artist.objects.all(), ['']) - - def test_create_with_redirect(self): - res = self.client.post('/edit/authors/create/redirect/', - {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/edit/authors/create/') - self.assertQuerysetEqual(Author.objects.all(), ['']) - - def test_create_with_interpolated_redirect(self): - res = self.client.post('/edit/authors/create/interpolate_redirect/', - {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - self.assertQuerysetEqual(Author.objects.all(), ['']) - self.assertEqual(res.status_code, 302) - pk = Author.objects.all()[0].pk - self.assertRedirects(res, 'http://testserver/edit/author/%d/update/' % pk) - - def test_create_with_special_properties(self): - res = self.client.get('/edit/authors/create/special/') - self.assertEqual(res.status_code, 200) - self.assertTrue(isinstance(res.context['form'], views.AuthorForm)) - self.assertFalse('object' in res.context) - self.assertFalse('author' in res.context) - self.assertTemplateUsed(res, 'generic_views/form.html') - - res = self.client.post('/edit/authors/create/special/', - {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 302) - obj = Author.objects.get(slug='randall-munroe') - self.assertRedirects(res, reverse('author_detail', kwargs={'pk': obj.pk})) - self.assertQuerysetEqual(Author.objects.all(), ['']) - - def test_create_without_redirect(self): - try: - res = self.client.post('/edit/authors/create/naive/', - {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') - except ImproperlyConfigured: - pass - - def test_create_restricted(self): - res = self.client.post('/edit/authors/create/restricted/', - {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/') - - -class UpdateViewTests(TestCase): - urls = 'generic_views.urls' - - def test_update_post(self): - a = Author.objects.create( - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.get('/edit/author/%d/update/' % a.pk) - self.assertEqual(res.status_code, 200) - self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) - self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) - self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) - self.assertTemplateUsed(res, 'generic_views/author_form.html') - - # Modification with both POST and PUT (browser compatible) - res = self.client.post('/edit/author/%d/update/' % a.pk, - {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/list/authors/') - self.assertQuerysetEqual(Author.objects.all(), ['']) - - @expectedFailure - def test_update_put(self): - a = Author.objects.create( - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.get('/edit/author/%d/update/' % a.pk) - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/author_form.html') - - res = self.client.put('/edit/author/%d/update/' % a.pk, - {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) - # Here is the expected failure. PUT data are not processed in any special - # way by django. So the request will equal to a POST without data, hence - # the form will be invalid and redisplayed with errors (status code 200). - # See also #12635 - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/list/authors/') - self.assertQuerysetEqual(Author.objects.all(), ['']) - - def test_update_invalid(self): - a = Author.objects.create( - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.post('/edit/author/%d/update/' % a.pk, - {'name': 'A' * 101, 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/author_form.html') - self.assertEqual(len(res.context['form'].errors), 1) - self.assertQuerysetEqual(Author.objects.all(), ['']) - - def test_update_with_object_url(self): - a = Artist.objects.create(name='Rene Magritte') - res = self.client.post('/edit/artists/%d/update/' % a.pk, - {'name': 'Rene Magritte'}) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/detail/artist/%d/' % a.pk) - self.assertQuerysetEqual(Artist.objects.all(), ['']) - - def test_update_with_redirect(self): - a = Author.objects.create( - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.post('/edit/author/%d/update/redirect/' % a.pk, - {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/edit/authors/create/') - self.assertQuerysetEqual(Author.objects.all(), ['']) - - def test_update_with_interpolated_redirect(self): - a = Author.objects.create( - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.post('/edit/author/%d/update/interpolate_redirect/' % a.pk, - {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) - self.assertQuerysetEqual(Author.objects.all(), ['']) - self.assertEqual(res.status_code, 302) - pk = Author.objects.all()[0].pk - self.assertRedirects(res, 'http://testserver/edit/author/%d/update/' % pk) - - def test_update_with_special_properties(self): - a = Author.objects.create( - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.get('/edit/author/%d/update/special/' % a.pk) - self.assertEqual(res.status_code, 200) - self.assertTrue(isinstance(res.context['form'], views.AuthorForm)) - self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) - self.assertEqual(res.context['thingy'], Author.objects.get(pk=a.pk)) - self.assertFalse('author' in res.context) - self.assertTemplateUsed(res, 'generic_views/form.html') - - res = self.client.post('/edit/author/%d/update/special/' % a.pk, - {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/detail/author/%d/' % a.pk) - self.assertQuerysetEqual(Author.objects.all(), ['']) - - def test_update_without_redirect(self): - try: - a = Author.objects.create( - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.post('/edit/author/%d/update/naive/' % a.pk, - {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) - self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') - except ImproperlyConfigured: - pass - - def test_update_get_object(self): - a = Author.objects.create( - pk=1, - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.get('/edit/author/update/') - self.assertEqual(res.status_code, 200) - self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) - self.assertTrue(isinstance(res.context['view'], View)) - self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) - self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) - self.assertTemplateUsed(res, 'generic_views/author_form.html') - - # Modification with both POST and PUT (browser compatible) - res = self.client.post('/edit/author/update/', - {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'}) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/list/authors/') - self.assertQuerysetEqual(Author.objects.all(), ['']) - - -class DeleteViewTests(TestCase): - urls = 'generic_views.urls' - - def test_delete_by_post(self): - a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - res = self.client.get('/edit/author/%d/delete/' % a.pk) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) - self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) - self.assertTemplateUsed(res, 'generic_views/author_confirm_delete.html') - - # Deletion with POST - res = self.client.post('/edit/author/%d/delete/' % a.pk) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/list/authors/') - self.assertQuerysetEqual(Author.objects.all(), []) - - def test_delete_by_delete(self): - # Deletion with browser compatible DELETE method - a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - res = self.client.delete('/edit/author/%d/delete/' % a.pk) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/list/authors/') - self.assertQuerysetEqual(Author.objects.all(), []) - - def test_delete_with_redirect(self): - a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - res = self.client.post('/edit/author/%d/delete/redirect/' % a.pk) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/edit/authors/create/') - self.assertQuerysetEqual(Author.objects.all(), []) - - def test_delete_with_interpolated_redirect(self): - a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - res = self.client.post('/edit/author/%d/delete/interpolate_redirect/' % a.pk) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/edit/authors/create/?deleted=%d' % a.pk) - self.assertQuerysetEqual(Author.objects.all(), []) - - def test_delete_with_special_properties(self): - a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) - res = self.client.get('/edit/author/%d/delete/special/' % a.pk) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) - self.assertEqual(res.context['thingy'], Author.objects.get(pk=a.pk)) - self.assertFalse('author' in res.context) - self.assertTemplateUsed(res, 'generic_views/confirm_delete.html') - - res = self.client.post('/edit/author/%d/delete/special/' % a.pk) - self.assertEqual(res.status_code, 302) - self.assertRedirects(res, 'http://testserver/list/authors/') - self.assertQuerysetEqual(Author.objects.all(), []) - - def test_delete_without_redirect(self): - try: - a = Author.objects.create( - name='Randall Munroe', - slug='randall-munroe', - ) - res = self.client.post('/edit/author/%d/delete/naive/' % a.pk) - self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') - except ImproperlyConfigured: - pass - diff --git a/tests/generic_views/forms.py b/tests/generic_views/forms.py deleted file mode 100644 index e036ad8afc..0000000000 --- a/tests/generic_views/forms.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import absolute_import - -from django import forms - -from .models import Author - - -class AuthorForm(forms.ModelForm): - name = forms.CharField() - slug = forms.SlugField() - - class Meta: - model = Author - - -class ContactForm(forms.Form): - name = forms.CharField() - message = forms.CharField(widget=forms.Textarea) diff --git a/tests/generic_views/list.py b/tests/generic_views/list.py deleted file mode 100644 index cc4d2f5966..0000000000 --- a/tests/generic_views/list.py +++ /dev/null @@ -1,217 +0,0 @@ -from __future__ import absolute_import - -from django.core.exceptions import ImproperlyConfigured -from django.test import TestCase -from django.test.utils import override_settings -from django.views.generic.base import View -from django.utils.encoding import force_str - -from .models import Author, Artist - - -class ListViewTests(TestCase): - fixtures = ['generic-views-test-data.json'] - urls = 'generic_views.urls' - - def test_items(self): - res = self.client.get('/list/dict/') - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/list.html') - self.assertEqual(res.context['object_list'][0]['first'], 'John') - - def test_queryset(self): - res = self.client.get('/list/authors/') - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/author_list.html') - self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) - self.assertTrue(isinstance(res.context['view'], View)) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertIsNone(res.context['paginator']) - self.assertIsNone(res.context['page_obj']) - self.assertFalse(res.context['is_paginated']) - - 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, 'generic_views/author_list.html') - self.assertEqual(len(res.context['object_list']), 30) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertTrue(res.context['is_paginated']) - self.assertEqual(res.context['page_obj'].number, 1) - self.assertEqual(res.context['paginator'].num_pages, 4) - self.assertEqual(res.context['author_list'][0].name, 'Author 00') - self.assertEqual(list(res.context['author_list'])[-1].name, 'Author 29') - - def test_paginated_queryset_shortdata(self): - # Test that short datasets ALSO result in a paginated view. - res = self.client.get('/list/authors/paginated/') - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/author_list.html') - self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertEqual(res.context['page_obj'].number, 1) - self.assertEqual(res.context['paginator'].num_pages, 1) - self.assertFalse(res.context['is_paginated']) - - 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, 'generic_views/author_list.html') - self.assertEqual(len(res.context['object_list']), 30) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertEqual(res.context['author_list'][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['object_list']), 10) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertEqual(res.context['author_list'][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, 'generic_views/author_list.html') - self.assertEqual(len(res.context['object_list']), 30) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertEqual(res.context['author_list'][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_paginated_custom_paginator_class(self): - self._make_authors(7) - res = self.client.get('/list/authors/paginated/custom_class/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['paginator'].num_pages, 1) - # Custom pagination allows for 2 orphans on a page size of 5 - self.assertEqual(len(res.context['object_list']), 7) - - def test_paginated_custom_page_kwarg(self): - self._make_authors(100) - res = self.client.get('/list/authors/paginated/custom_page_kwarg/', {'pagina': '2'}) - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/author_list.html') - self.assertEqual(len(res.context['object_list']), 30) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertEqual(res.context['author_list'][0].name, 'Author 30') - self.assertEqual(res.context['page_obj'].number, 2) - - def test_paginated_custom_paginator_constructor(self): - self._make_authors(7) - res = self.client.get('/list/authors/paginated/custom_constructor/') - self.assertEqual(res.status_code, 200) - # Custom pagination allows for 2 orphans on a page size of 5 - self.assertEqual(len(res.context['object_list']), 7) - - def test_paginated_orphaned_queryset(self): - self._make_authors(92) - res = self.client.get('/list/authors/paginated-orphaned/') - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['page_obj'].number, 1) - res = self.client.get( - '/list/authors/paginated-orphaned/', {'page': 'last'}) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['page_obj'].number, 3) - res = self.client.get( - '/list/authors/paginated-orphaned/', {'page': '3'}) - self.assertEqual(res.status_code, 200) - self.assertEqual(res.context['page_obj'].number, 3) - res = self.client.get( - '/list/authors/paginated-orphaned/', {'page': '4'}) - self.assertEqual(res.status_code, 404) - - def test_paginated_non_queryset(self): - res = self.client.get('/list/dict/paginated/') - - self.assertEqual(res.status_code, 200) - self.assertEqual(len(res.context['object_list']), 1) - - def test_verbose_name(self): - res = self.client.get('/list/artists/') - self.assertEqual(res.status_code, 200) - self.assertTemplateUsed(res, 'generic_views/list.html') - self.assertEqual(list(res.context['object_list']), list(Artist.objects.all())) - self.assertIs(res.context['artist_list'], res.context['object_list']) - self.assertIsNone(res.context['paginator']) - self.assertIsNone(res.context['page_obj']) - self.assertFalse(res.context['is_paginated']) - - 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_name(self): - res = self.client.get('/list/authors/template_name/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertTemplateUsed(res, 'generic_views/list.html') - - def test_template_name_suffix(self): - res = self.client.get('/list/authors/template_name_suffix/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertTemplateUsed(res, 'generic_views/author_objects.html') - - def test_context_object_name(self): - res = self.client.get('/list/authors/context_object_name/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) - self.assertNotIn('authors', res.context) - self.assertIs(res.context['author_list'], res.context['object_list']) - self.assertTemplateUsed(res, 'generic_views/author_list.html') - - def test_duplicate_context_object_name(self): - res = self.client.get('/list/authors/dupe_context_object_name/') - self.assertEqual(res.status_code, 200) - self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) - self.assertNotIn('authors', res.context) - self.assertNotIn('author_list', res.context) - self.assertTemplateUsed(res, 'generic_views/author_list.html') - - def test_missing_items(self): - self.assertRaises(ImproperlyConfigured, self.client.get, '/list/authors/invalid/') - - def test_paginated_list_view_does_not_load_entire_table(self): - # Regression test for #17535 - self._make_authors(3) - # 1 query for authors - with self.assertNumQueries(1): - self.client.get('/list/authors/notempty/') - # same as above + 1 query to test if authors exist + 1 query for pagination - with self.assertNumQueries(3): - self.client.get('/list/authors/notempty/paginated/') - - @override_settings(DEBUG=True) - def test_paginated_list_view_returns_useful_message_on_invalid_page(self): - # test for #19240 - # tests that source exception's message is included in page - self._make_authors(1) - res = self.client.get('/list/authors/paginated/2/') - self.assertEqual(res.status_code, 404) - self.assertEqual(force_str(res.context.get('reason')), - "Invalid page (2): That page contains no results") - - 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) diff --git a/tests/generic_views/test_base.py b/tests/generic_views/test_base.py new file mode 100644 index 0000000000..0e84e17132 --- /dev/null +++ b/tests/generic_views/test_base.py @@ -0,0 +1,413 @@ +from __future__ import absolute_import + +import time + +from django.core.exceptions import ImproperlyConfigured +from django.http import HttpResponse +from django.test import TestCase, RequestFactory +from django.utils import unittest +from django.views.generic import View, TemplateView, RedirectView + +from . import views + +class SimpleView(View): + """ + A simple view with a docstring. + """ + def get(self, request): + return HttpResponse('This is a simple view') + + +class SimplePostView(SimpleView): + post = SimpleView.get + + +class PostOnlyView(View): + def post(self, request): + return HttpResponse('This view only accepts POST') + + +class CustomizableView(SimpleView): + parameter = {} + + +def decorator(view): + view.is_decorated = True + return view + + +class DecoratedDispatchView(SimpleView): + + @decorator + def dispatch(self, request, *args, **kwargs): + return super(DecoratedDispatchView, self).dispatch(request, *args, **kwargs) + + +class AboutTemplateView(TemplateView): + def get(self, request): + return self.render_to_response({}) + + def get_template_names(self): + return ['generic_views/about.html'] + + +class AboutTemplateAttributeView(TemplateView): + template_name = 'generic_views/about.html' + + def get(self, request): + return self.render_to_response(context={}) + + +class InstanceView(View): + + def get(self, request): + return self + + +class ViewTest(unittest.TestCase): + rf = RequestFactory() + + def _assert_simple(self, response): + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b'This is a simple view') + + def test_no_init_kwargs(self): + """ + Test that a view can't be accidentally instantiated before deployment + """ + try: + view = SimpleView(key='value').as_view() + self.fail('Should not be able to instantiate a view') + except AttributeError: + pass + + def test_no_init_args(self): + """ + Test that a view can't be accidentally instantiated before deployment + """ + try: + view = SimpleView.as_view('value') + self.fail('Should not be able to use non-keyword arguments instantiating a view') + except TypeError: + pass + + def test_pathological_http_method(self): + """ + The edge case of a http request that spoofs an existing method name is caught. + """ + self.assertEqual(SimpleView.as_view()( + self.rf.get('/', REQUEST_METHOD='DISPATCH') + ).status_code, 405) + + def test_get_only(self): + """ + Test a view which only allows GET doesn't allow other methods. + """ + self._assert_simple(SimpleView.as_view()(self.rf.get('/'))) + self.assertEqual(SimpleView.as_view()(self.rf.post('/')).status_code, 405) + self.assertEqual(SimpleView.as_view()( + self.rf.get('/', REQUEST_METHOD='FAKE') + ).status_code, 405) + + def test_get_and_head(self): + """ + Test a view which supplies a GET method also responds correctly to HEAD. + """ + self._assert_simple(SimpleView.as_view()(self.rf.get('/'))) + response = SimpleView.as_view()(self.rf.head('/')) + self.assertEqual(response.status_code, 200) + + def test_head_no_get(self): + """ + Test a view which supplies no GET method responds to HEAD with HTTP 405. + """ + response = PostOnlyView.as_view()(self.rf.head('/')) + self.assertEqual(response.status_code, 405) + + def test_get_and_post(self): + """ + Test a view which only allows both GET and POST. + """ + self._assert_simple(SimplePostView.as_view()(self.rf.get('/'))) + self._assert_simple(SimplePostView.as_view()(self.rf.post('/'))) + self.assertEqual(SimplePostView.as_view()( + self.rf.get('/', REQUEST_METHOD='FAKE') + ).status_code, 405) + + def test_invalid_keyword_argument(self): + """ + Test that view arguments must be predefined on the class and can't + be named like a HTTP method. + """ + # Check each of the allowed method names + for method in SimpleView.http_method_names: + kwargs = dict(((method, "value"),)) + self.assertRaises(TypeError, SimpleView.as_view, **kwargs) + + # Check the case view argument is ok if predefined on the class... + CustomizableView.as_view(parameter="value") + # ...but raises errors otherwise. + self.assertRaises(TypeError, CustomizableView.as_view, foobar="value") + + def test_calling_more_than_once(self): + """ + Test a view can only be called once. + """ + request = self.rf.get('/') + view = InstanceView.as_view() + self.assertNotEqual(view(request), view(request)) + + def test_class_attributes(self): + """ + Test that the callable returned from as_view() has proper + docstring, name and module. + """ + self.assertEqual(SimpleView.__doc__, SimpleView.as_view().__doc__) + self.assertEqual(SimpleView.__name__, SimpleView.as_view().__name__) + self.assertEqual(SimpleView.__module__, SimpleView.as_view().__module__) + + def test_dispatch_decoration(self): + """ + Test that attributes set by decorators on the dispatch method + are also present on the closure. + """ + self.assertTrue(DecoratedDispatchView.as_view().is_decorated) + + def test_options(self): + """ + Test that views respond to HTTP OPTIONS requests with an Allow header + appropriate for the methods implemented by the view class. + """ + request = self.rf.options('/') + view = SimpleView.as_view() + response = view(request) + self.assertEqual(200, response.status_code) + self.assertTrue(response['Allow']) + + def test_options_for_get_view(self): + """ + Test that a view implementing GET allows GET and HEAD. + """ + request = self.rf.options('/') + view = SimpleView.as_view() + response = view(request) + self._assert_allows(response, 'GET', 'HEAD') + + def test_options_for_get_and_post_view(self): + """ + Test that a view implementing GET and POST allows GET, HEAD, and POST. + """ + request = self.rf.options('/') + view = SimplePostView.as_view() + response = view(request) + self._assert_allows(response, 'GET', 'HEAD', 'POST') + + def test_options_for_post_view(self): + """ + Test that a view implementing POST allows POST. + """ + request = self.rf.options('/') + view = PostOnlyView.as_view() + response = view(request) + self._assert_allows(response, 'POST') + + def _assert_allows(self, response, *expected_methods): + "Assert allowed HTTP methods reported in the Allow response header" + response_allows = set(response['Allow'].split(', ')) + self.assertEqual(set(expected_methods + ('OPTIONS',)), response_allows) + + def test_args_kwargs_request_on_self(self): + """ + Test a view only has args, kwargs & request once `as_view` + has been called. + """ + bare_view = InstanceView() + view = InstanceView.as_view()(self.rf.get('/')) + for attribute in ('args', 'kwargs', 'request'): + self.assertNotIn(attribute, dir(bare_view)) + self.assertIn(attribute, dir(view)) + + +class TemplateViewTest(TestCase): + urls = 'generic_views.urls' + + rf = RequestFactory() + + def _assert_about(self, response): + response.render() + self.assertEqual(response.status_code, 200) + self.assertContains(response, '

About

') + + def test_get(self): + """ + Test a view that simply renders a template on GET + """ + self._assert_about(AboutTemplateView.as_view()(self.rf.get('/about/'))) + + def test_head(self): + """ + Test a TemplateView responds correctly to HEAD + """ + response = AboutTemplateView.as_view()(self.rf.head('/about/')) + self.assertEqual(response.status_code, 200) + + def test_get_template_attribute(self): + """ + Test a view that renders a template on GET with the template name as + an attribute on the class. + """ + self._assert_about(AboutTemplateAttributeView.as_view()(self.rf.get('/about/'))) + + def test_get_generic_template(self): + """ + Test a completely generic view that renders a template on GET + with the template name as an argument at instantiation. + """ + self._assert_about(TemplateView.as_view(template_name='generic_views/about.html')(self.rf.get('/about/'))) + + def test_template_name_required(self): + """ + A template view must provide a template name + """ + self.assertRaises(ImproperlyConfigured, self.client.get, '/template/no_template/') + + def test_template_params(self): + """ + A generic template view passes kwargs as context. + """ + response = self.client.get('/template/simple/bar/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['foo'], 'bar') + self.assertTrue(isinstance(response.context['view'], View)) + + def test_extra_template_params(self): + """ + A template view can be customized to return extra context. + """ + response = self.client.get('/template/custom/bar/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['foo'], 'bar') + self.assertEqual(response.context['key'], 'value') + self.assertTrue(isinstance(response.context['view'], View)) + + def test_cached_views(self): + """ + A template view can be cached + """ + response = self.client.get('/template/cached/bar/') + self.assertEqual(response.status_code, 200) + + time.sleep(1.0) + + response2 = self.client.get('/template/cached/bar/') + self.assertEqual(response2.status_code, 200) + + self.assertEqual(response.content, response2.content) + + time.sleep(2.0) + + # Let the cache expire and test again + response2 = self.client.get('/template/cached/bar/') + self.assertEqual(response2.status_code, 200) + + self.assertNotEqual(response.content, response2.content) + + def test_content_type(self): + response = self.client.get('/template/content_type/') + self.assertEqual(response['Content-Type'], 'text/plain') + + +class RedirectViewTest(unittest.TestCase): + rf = RequestFactory() + + def test_no_url(self): + "Without any configuration, returns HTTP 410 GONE" + response = RedirectView.as_view()(self.rf.get('/foo/')) + self.assertEqual(response.status_code, 410) + + def test_permanent_redirect(self): + "Default is a permanent redirect" + response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/') + + def test_temporary_redirect(self): + "Permanent redirects are an option" + response = RedirectView.as_view(url='/bar/', permanent=False)(self.rf.get('/foo/')) + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, '/bar/') + + def test_include_args(self): + "GET arguments can be included in the redirected URL" + response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/') + + response = RedirectView.as_view(url='/bar/', query_string=True)(self.rf.get('/foo/?pork=spam')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/?pork=spam') + + def test_include_urlencoded_args(self): + "GET arguments can be URL-encoded when included in the redirected URL" + response = RedirectView.as_view(url='/bar/', query_string=True)( + self.rf.get('/foo/?unicode=%E2%9C%93')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/?unicode=%E2%9C%93') + + def test_parameter_substitution(self): + "Redirection URLs can be parameterized" + response = RedirectView.as_view(url='/bar/%(object_id)d/')(self.rf.get('/foo/42/'), object_id=42) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/42/') + + def test_redirect_POST(self): + "Default is a permanent redirect" + response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/') + + def test_redirect_HEAD(self): + "Default is a permanent redirect" + response = RedirectView.as_view(url='/bar/')(self.rf.head('/foo/')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/') + + def test_redirect_OPTIONS(self): + "Default is a permanent redirect" + response = RedirectView.as_view(url='/bar/')(self.rf.options('/foo/')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/') + + def test_redirect_PUT(self): + "Default is a permanent redirect" + response = RedirectView.as_view(url='/bar/')(self.rf.put('/foo/')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/') + + def test_redirect_DELETE(self): + "Default is a permanent redirect" + response = RedirectView.as_view(url='/bar/')(self.rf.delete('/foo/')) + self.assertEqual(response.status_code, 301) + self.assertEqual(response.url, '/bar/') + + def test_redirect_when_meta_contains_no_query_string(self): + "regression for #16705" + # we can't use self.rf.get because it always sets QUERY_STRING + response = RedirectView.as_view(url='/bar/')(self.rf.request(PATH_INFO='/foo/')) + self.assertEqual(response.status_code, 301) + + +class GetContextDataTest(unittest.TestCase): + + def test_get_context_data_super(self): + test_view = views.CustomContextView() + context = test_view.get_context_data(kwarg_test='kwarg_value') + + # the test_name key is inserted by the test classes parent + self.assertTrue('test_name' in context) + self.assertEqual(context['kwarg_test'], 'kwarg_value') + self.assertEqual(context['custom_key'], 'custom_value') + + # test that kwarg overrides values assigned higher up + context = test_view.get_context_data(test_name='test_value') + self.assertEqual(context['test_name'], 'test_value') diff --git a/tests/generic_views/test_dates.py b/tests/generic_views/test_dates.py new file mode 100644 index 0000000000..263326322e --- /dev/null +++ b/tests/generic_views/test_dates.py @@ -0,0 +1,638 @@ +from __future__ import absolute_import + +import time +import datetime + +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase, skipUnlessDBFeature +from django.test.utils import override_settings +from django.utils import timezone +from django.utils.unittest import skipUnless + +from .models import Book, BookSigning + +TZ_SUPPORT = hasattr(time, 'tzset') + +# On OSes that don't provide tzset (Windows), we can't set the timezone +# in which the program runs. As a consequence, we must skip tests that +# don't enforce a specific timezone (with timezone.override or equivalent), +# or attempt to interpret naive datetimes in the default timezone. + +requires_tz_support = skipUnless(TZ_SUPPORT, + "This test relies on the ability to run a program in an arbitrary " + "time zone, but your operating system isn't able to do that.") + + +def _make_books(n, base_date): + for i in range(n): + b = Book.objects.create( + name='Book %d' % i, + slug='book-%d' % i, + pages=100+i, + pubdate=base_date - datetime.timedelta(days=i)) + +class ArchiveIndexViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'generic_views.urls' + + + def test_archive_view(self): + res = self.client.get('/dates/books/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) + self.assertEqual(list(res.context['latest']), list(Book.objects.all())) + self.assertTemplateUsed(res, 'generic_views/book_archive.html') + + def test_archive_view_context_object_name(self): + res = self.client.get('/dates/books/context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) + self.assertEqual(list(res.context['thingies']), list(Book.objects.all())) + self.assertFalse('latest' in res.context) + self.assertTemplateUsed(res, 'generic_views/book_archive.html') + + def test_empty_archive_view(self): + Book.objects.all().delete() + res = self.client.get('/dates/books/') + self.assertEqual(res.status_code, 404) + + def test_allow_empty_archive_view(self): + Book.objects.all().delete() + res = self.client.get('/dates/books/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), []) + self.assertTemplateUsed(res, 'generic_views/book_archive.html') + + def test_archive_view_template(self): + res = self.client.get('/dates/books/template_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) + self.assertEqual(list(res.context['latest']), list(Book.objects.all())) + self.assertTemplateUsed(res, 'generic_views/list.html') + + def test_archive_view_template_suffix(self): + res = self.client.get('/dates/books/template_name_suffix/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) + self.assertEqual(list(res.context['latest']), list(Book.objects.all())) + self.assertTemplateUsed(res, 'generic_views/book_detail.html') + + def test_archive_view_invalid(self): + self.assertRaises(ImproperlyConfigured, self.client.get, '/dates/books/invalid/') + + def test_archive_view_by_month(self): + res = self.client.get('/dates/books/by_month/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'month', 'DESC'))) + + def test_paginated_archive_view(self): + _make_books(20, base_date=datetime.date.today()) + res = self.client.get('/dates/books/paginated/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), list(Book.objects.dates('pubdate', 'year', 'DESC'))) + self.assertEqual(list(res.context['latest']), list(Book.objects.all()[0:10])) + self.assertTemplateUsed(res, 'generic_views/book_archive.html') + + res = self.client.get('/dates/books/paginated/?page=2') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['page_obj'].number, 2) + self.assertEqual(list(res.context['latest']), list(Book.objects.all()[10:20])) + + def test_paginated_archive_view_does_not_load_entire_table(self): + # Regression test for #18087 + _make_books(20, base_date=datetime.date.today()) + # 1 query for years list + 1 query for books + with self.assertNumQueries(2): + self.client.get('/dates/books/') + # same as above + 1 query to test if books exist + 1 query to count them + with self.assertNumQueries(4): + self.client.get('/dates/books/paginated/') + + def test_no_duplicate_query(self): + # Regression test for #18354 + with self.assertNumQueries(2): + self.client.get('/dates/books/reverse/') + + def test_datetime_archive_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) + res = self.client.get('/dates/booksignings/') + self.assertEqual(res.status_code, 200) + + @requires_tz_support + @skipUnlessDBFeature('has_zoneinfo_database') + @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') + def test_aware_datetime_archive_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) + res = self.client.get('/dates/booksignings/') + self.assertEqual(res.status_code, 200) + + def test_date_list_order(self): + """date_list should be sorted descending in index""" + _make_books(5, base_date=datetime.date(2011, 12, 25)) + res = self.client.get('/dates/books/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), list(reversed(sorted(res.context['date_list'])))) + + +class YearArchiveViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'generic_views.urls' + + def test_year_view(self): + res = self.client.get('/dates/books/2008/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), [datetime.date(2008, 10, 1)]) + self.assertEqual(res.context['year'], datetime.date(2008, 1, 1)) + self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') + + # Since allow_empty=False, next/prev years must be valid (#7164) + self.assertEqual(res.context['next_year'], None) + self.assertEqual(res.context['previous_year'], datetime.date(2006, 1, 1)) + + def test_year_view_make_object_list(self): + res = self.client.get('/dates/books/2006/make_object_list/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), [datetime.date(2006, 5, 1)]) + self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2006))) + self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006))) + self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') + + def test_year_view_empty(self): + res = self.client.get('/dates/books/1999/') + self.assertEqual(res.status_code, 404) + res = self.client.get('/dates/books/1999/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), []) + self.assertEqual(list(res.context['book_list']), []) + + # Since allow_empty=True, next/prev are allowed to be empty years (#7164) + self.assertEqual(res.context['next_year'], datetime.date(2000, 1, 1)) + self.assertEqual(res.context['previous_year'], datetime.date(1998, 1, 1)) + + def test_year_view_allow_future(self): + # Create a new book in the future + year = datetime.date.today().year + 1 + b = Book.objects.create(name="The New New Testement", pages=600, pubdate=datetime.date(year, 1, 1)) + res = self.client.get('/dates/books/%s/' % year) + self.assertEqual(res.status_code, 404) + + res = self.client.get('/dates/books/%s/allow_empty/' % year) + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), []) + + res = self.client.get('/dates/books/%s/allow_future/' % year) + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), [datetime.date(year, 1, 1)]) + + def test_year_view_paginated(self): + res = self.client.get('/dates/books/2006/paginated/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2006))) + self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2006))) + self.assertTemplateUsed(res, 'generic_views/book_archive_year.html') + + def test_year_view_invalid_pattern(self): + res = self.client.get('/dates/books/no_year/') + self.assertEqual(res.status_code, 404) + + def test_no_duplicate_query(self): + # Regression test for #18354 + with self.assertNumQueries(4): + self.client.get('/dates/books/2008/reverse/') + + def test_datetime_year_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) + res = self.client.get('/dates/booksignings/2008/') + self.assertEqual(res.status_code, 200) + + @skipUnlessDBFeature('has_zoneinfo_database') + @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') + def test_aware_datetime_year_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) + res = self.client.get('/dates/booksignings/2008/') + self.assertEqual(res.status_code, 200) + + def test_date_list_order(self): + """date_list should be sorted ascending in year view""" + _make_books(10, base_date=datetime.date(2011, 12, 25)) + res = self.client.get('/dates/books/2011/') + self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list']))) + + +class MonthArchiveViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'generic_views.urls' + + def test_month_view(self): + res = self.client.get('/dates/books/2008/oct/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/book_archive_month.html') + self.assertEqual(list(res.context['date_list']), [datetime.date(2008, 10, 1)]) + self.assertEqual(list(res.context['book_list']), + list(Book.objects.filter(pubdate=datetime.date(2008, 10, 1)))) + self.assertEqual(res.context['month'], datetime.date(2008, 10, 1)) + + # Since allow_empty=False, next/prev months must be valid (#7164) + self.assertEqual(res.context['next_month'], None) + self.assertEqual(res.context['previous_month'], datetime.date(2006, 5, 1)) + + def test_month_view_allow_empty(self): + # allow_empty = False, empty month + res = self.client.get('/dates/books/2000/jan/') + self.assertEqual(res.status_code, 404) + + # allow_empty = True, empty month + res = self.client.get('/dates/books/2000/jan/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['date_list']), []) + self.assertEqual(list(res.context['book_list']), []) + self.assertEqual(res.context['month'], datetime.date(2000, 1, 1)) + + # Since allow_empty=True, next/prev are allowed to be empty months (#7164) + self.assertEqual(res.context['next_month'], datetime.date(2000, 2, 1)) + self.assertEqual(res.context['previous_month'], datetime.date(1999, 12, 1)) + + # allow_empty but not allow_future: next_month should be empty (#7164) + url = datetime.date.today().strftime('/dates/books/%Y/%b/allow_empty/').lower() + res = self.client.get(url) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_month'], None) + + def test_month_view_allow_future(self): + future = (datetime.date.today() + datetime.timedelta(days=60)).replace(day=1) + urlbit = future.strftime('%Y/%b').lower() + b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) + + # allow_future = False, future month + res = self.client.get('/dates/books/%s/' % urlbit) + self.assertEqual(res.status_code, 404) + + # allow_future = True, valid future month + res = self.client.get('/dates/books/%s/allow_future/' % urlbit) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'][0], b.pubdate) + self.assertEqual(list(res.context['book_list']), [b]) + self.assertEqual(res.context['month'], future) + + # Since allow_future = True but not allow_empty, next/prev are not + # allowed to be empty months (#7164) + self.assertEqual(res.context['next_month'], None) + self.assertEqual(res.context['previous_month'], datetime.date(2008, 10, 1)) + + # allow_future, but not allow_empty, with a current month. So next + # should be in the future (yup, #7164, again) + res = self.client.get('/dates/books/2008/oct/allow_future/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_month'], future) + self.assertEqual(res.context['previous_month'], datetime.date(2006, 5, 1)) + + def test_month_view_paginated(self): + res = self.client.get('/dates/books/2008/oct/paginated/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2008, pubdate__month=10))) + self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2008, pubdate__month=10))) + self.assertTemplateUsed(res, 'generic_views/book_archive_month.html') + + def test_custom_month_format(self): + res = self.client.get('/dates/books/2008/10/') + self.assertEqual(res.status_code, 200) + + def test_month_view_invalid_pattern(self): + res = self.client.get('/dates/books/2007/no_month/') + self.assertEqual(res.status_code, 404) + + def test_previous_month_without_content(self): + "Content can exist on any day of the previous month. Refs #14711" + self.pubdate_list = [ + datetime.date(2010, month, day) + for month,day in ((9,1), (10,2), (11,3)) + ] + for pubdate in self.pubdate_list: + name = str(pubdate) + Book.objects.create(name=name, slug=name, pages=100, pubdate=pubdate) + + res = self.client.get('/dates/books/2010/nov/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['previous_month'], datetime.date(2010,10,1)) + # The following test demonstrates the bug + res = self.client.get('/dates/books/2010/nov/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['previous_month'], datetime.date(2010,10,1)) + # The bug does not occur here because a Book with pubdate of Sep 1 exists + res = self.client.get('/dates/books/2010/oct/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['previous_month'], datetime.date(2010,9,1)) + + def test_datetime_month_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0)) + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) + BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0)) + res = self.client.get('/dates/booksignings/2008/apr/') + self.assertEqual(res.status_code, 200) + + @skipUnlessDBFeature('has_zoneinfo_database') + @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') + def test_aware_datetime_month_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 2, 1, 12, 0, tzinfo=timezone.utc)) + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) + BookSigning.objects.create(event_date=datetime.datetime(2008, 6, 3, 12, 0, tzinfo=timezone.utc)) + res = self.client.get('/dates/booksignings/2008/apr/') + self.assertEqual(res.status_code, 200) + + def test_date_list_order(self): + """date_list should be sorted ascending in month view""" + _make_books(10, base_date=datetime.date(2011, 12, 25)) + res = self.client.get('/dates/books/2011/dec/') + self.assertEqual(list(res.context['date_list']), list(sorted(res.context['date_list']))) + + +class WeekArchiveViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'generic_views.urls' + + def test_week_view(self): + res = self.client.get('/dates/books/2008/week/39/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/book_archive_week.html') + self.assertEqual(res.context['book_list'][0], Book.objects.get(pubdate=datetime.date(2008, 10, 1))) + self.assertEqual(res.context['week'], datetime.date(2008, 9, 28)) + + # Since allow_empty=False, next/prev weeks must be valid + self.assertEqual(res.context['next_week'], None) + self.assertEqual(res.context['previous_week'], datetime.date(2006, 4, 30)) + + def test_week_view_allow_empty(self): + # allow_empty = False, empty week + res = self.client.get('/dates/books/2008/week/12/') + self.assertEqual(res.status_code, 404) + + # allow_empty = True, empty month + res = self.client.get('/dates/books/2008/week/12/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), []) + self.assertEqual(res.context['week'], datetime.date(2008, 3, 23)) + + # Since allow_empty=True, next/prev are allowed to be empty weeks + self.assertEqual(res.context['next_week'], datetime.date(2008, 3, 30)) + self.assertEqual(res.context['previous_week'], datetime.date(2008, 3, 16)) + + # allow_empty but not allow_future: next_week should be empty + url = datetime.date.today().strftime('/dates/books/%Y/week/%U/allow_empty/').lower() + res = self.client.get(url) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_week'], None) + + def test_week_view_allow_future(self): + # January 7th always falls in week 1, given Python's definition of week numbers + future = datetime.date(datetime.date.today().year + 1, 1, 7) + future_sunday = future - datetime.timedelta(days=(future.weekday() + 1) % 7) + b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) + + res = self.client.get('/dates/books/%s/week/1/' % future.year) + self.assertEqual(res.status_code, 404) + + res = self.client.get('/dates/books/%s/week/1/allow_future/' % future.year) + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), [b]) + self.assertEqual(res.context['week'], future_sunday) + + # Since allow_future = True but not allow_empty, next/prev are not + # allowed to be empty weeks + self.assertEqual(res.context['next_week'], None) + self.assertEqual(res.context['previous_week'], datetime.date(2008, 9, 28)) + + # allow_future, but not allow_empty, with a current week. So next + # should be in the future + res = self.client.get('/dates/books/2008/week/39/allow_future/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_week'], future_sunday) + self.assertEqual(res.context['previous_week'], datetime.date(2006, 4, 30)) + + def test_week_view_paginated(self): + week_start = datetime.date(2008, 9, 28) + week_end = week_start + datetime.timedelta(days=7) + res = self.client.get('/dates/books/2008/week/39/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__gte=week_start, pubdate__lt=week_end))) + self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__gte=week_start, pubdate__lt=week_end))) + self.assertTemplateUsed(res, 'generic_views/book_archive_week.html') + + def test_week_view_invalid_pattern(self): + res = self.client.get('/dates/books/2007/week/no_week/') + self.assertEqual(res.status_code, 404) + + def test_week_start_Monday(self): + # Regression for #14752 + res = self.client.get('/dates/books/2008/week/39/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['week'], datetime.date(2008, 9, 28)) + + res = self.client.get('/dates/books/2008/week/39/monday/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['week'], datetime.date(2008, 9, 29)) + + def test_datetime_week_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) + res = self.client.get('/dates/booksignings/2008/week/13/') + self.assertEqual(res.status_code, 200) + + @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') + def test_aware_datetime_week_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) + res = self.client.get('/dates/booksignings/2008/week/13/') + self.assertEqual(res.status_code, 200) + + +class DayArchiveViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'generic_views.urls' + + def test_day_view(self): + res = self.client.get('/dates/books/2008/oct/01/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/book_archive_day.html') + self.assertEqual(list(res.context['book_list']), + list(Book.objects.filter(pubdate=datetime.date(2008, 10, 1)))) + self.assertEqual(res.context['day'], datetime.date(2008, 10, 1)) + + # Since allow_empty=False, next/prev days must be valid. + self.assertEqual(res.context['next_day'], None) + self.assertEqual(res.context['previous_day'], datetime.date(2006, 5, 1)) + + def test_day_view_allow_empty(self): + # allow_empty = False, empty month + res = self.client.get('/dates/books/2000/jan/1/') + self.assertEqual(res.status_code, 404) + + # allow_empty = True, empty month + res = self.client.get('/dates/books/2000/jan/1/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), []) + self.assertEqual(res.context['day'], datetime.date(2000, 1, 1)) + + # Since it's allow empty, next/prev are allowed to be empty months (#7164) + self.assertEqual(res.context['next_day'], datetime.date(2000, 1, 2)) + self.assertEqual(res.context['previous_day'], datetime.date(1999, 12, 31)) + + # allow_empty but not allow_future: next_month should be empty (#7164) + url = datetime.date.today().strftime('/dates/books/%Y/%b/%d/allow_empty/').lower() + res = self.client.get(url) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_day'], None) + + def test_day_view_allow_future(self): + future = (datetime.date.today() + datetime.timedelta(days=60)) + urlbit = future.strftime('%Y/%b/%d').lower() + b = Book.objects.create(name="The New New Testement", pages=600, pubdate=future) + + # allow_future = False, future month + res = self.client.get('/dates/books/%s/' % urlbit) + self.assertEqual(res.status_code, 404) + + # allow_future = True, valid future month + res = self.client.get('/dates/books/%s/allow_future/' % urlbit) + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), [b]) + self.assertEqual(res.context['day'], future) + + # allow_future but not allow_empty, next/prev must be valid + self.assertEqual(res.context['next_day'], None) + self.assertEqual(res.context['previous_day'], datetime.date(2008, 10, 1)) + + # allow_future, but not allow_empty, with a current month. + res = self.client.get('/dates/books/2008/oct/01/allow_future/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['next_day'], future) + self.assertEqual(res.context['previous_day'], datetime.date(2006, 5, 1)) + + # allow_future for yesterday, next_day is today (#17192) + today = datetime.date.today() + yesterday = today - datetime.timedelta(days=1) + res = self.client.get('/dates/books/%s/allow_empty_and_future/' + % yesterday.strftime('%Y/%b/%d').lower()) + self.assertEqual(res.context['next_day'], today) + + def test_day_view_paginated(self): + res = self.client.get('/dates/books/2008/oct/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['book_list']), list(Book.objects.filter(pubdate__year=2008, pubdate__month=10, pubdate__day=1))) + self.assertEqual(list(res.context['object_list']), list(Book.objects.filter(pubdate__year=2008, pubdate__month=10, pubdate__day=1))) + self.assertTemplateUsed(res, 'generic_views/book_archive_day.html') + + def test_next_prev_context(self): + res = self.client.get('/dates/books/2008/oct/01/') + self.assertEqual(res.content, b"Archive for Oct. 1, 2008. Previous day is May 1, 2006") + + def test_custom_month_format(self): + res = self.client.get('/dates/books/2008/10/01/') + self.assertEqual(res.status_code, 200) + + def test_day_view_invalid_pattern(self): + res = self.client.get('/dates/books/2007/oct/no_day/') + self.assertEqual(res.status_code, 404) + + def test_today_view(self): + res = self.client.get('/dates/books/today/') + self.assertEqual(res.status_code, 404) + res = self.client.get('/dates/books/today/allow_empty/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['day'], datetime.date.today()) + + def test_datetime_day_view(self): + BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) + res = self.client.get('/dates/booksignings/2008/apr/2/') + self.assertEqual(res.status_code, 200) + + @requires_tz_support + @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') + def test_aware_datetime_day_view(self): + bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) + res = self.client.get('/dates/booksignings/2008/apr/2/') + self.assertEqual(res.status_code, 200) + # 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date) + bs.event_date = datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc) + bs.save() + res = self.client.get('/dates/booksignings/2008/apr/2/') + self.assertEqual(res.status_code, 200) + # 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date) + bs.event_date = datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc) + bs.save() + res = self.client.get('/dates/booksignings/2008/apr/2/') + self.assertEqual(res.status_code, 404) + + +class DateDetailViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'generic_views.urls' + + def test_date_detail_by_pk(self): + res = self.client.get('/dates/books/2008/oct/01/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Book.objects.get(pk=1)) + self.assertEqual(res.context['book'], Book.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/book_detail.html') + + def test_date_detail_by_slug(self): + res = self.client.get('/dates/books/2006/may/01/byslug/dreaming-in-code/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['book'], Book.objects.get(slug='dreaming-in-code')) + + def test_date_detail_custom_month_format(self): + res = self.client.get('/dates/books/2008/10/01/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['book'], Book.objects.get(pk=1)) + + def test_date_detail_allow_future(self): + future = (datetime.date.today() + datetime.timedelta(days=60)) + urlbit = future.strftime('%Y/%b/%d').lower() + b = Book.objects.create(name="The New New Testement", slug="new-new", pages=600, pubdate=future) + + res = self.client.get('/dates/books/%s/new-new/' % urlbit) + self.assertEqual(res.status_code, 404) + + res = self.client.get('/dates/books/%s/%s/allow_future/' % (urlbit, b.id)) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['book'], b) + self.assertTemplateUsed(res, 'generic_views/book_detail.html') + + def test_invalid_url(self): + self.assertRaises(AttributeError, self.client.get, "/dates/books/2008/oct/01/nopk/") + + def test_get_object_custom_queryset(self): + """ + Ensure that custom querysets are used when provided to + BaseDateDetailView.get_object() + Refs #16918. + """ + res = self.client.get( + '/dates/books/get_object_custom_queryset/2006/may/01/2/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Book.objects.get(pk=2)) + self.assertEqual(res.context['book'], Book.objects.get(pk=2)) + self.assertTemplateUsed(res, 'generic_views/book_detail.html') + + res = self.client.get( + '/dates/books/get_object_custom_queryset/2008/oct/01/1/') + self.assertEqual(res.status_code, 404) + + def test_datetime_date_detail(self): + bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0)) + res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) + self.assertEqual(res.status_code, 200) + + @requires_tz_support + @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') + def test_aware_datetime_date_detail(self): + bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) + res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) + self.assertEqual(res.status_code, 200) + # 2008-04-02T00:00:00+03:00 (beginning of day) > 2008-04-01T22:00:00+00:00 (book signing event date) + bs.event_date = datetime.datetime(2008, 4, 1, 22, 0, tzinfo=timezone.utc) + bs.save() + res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) + self.assertEqual(res.status_code, 200) + # 2008-04-03T00:00:00+03:00 (end of day) > 2008-04-02T22:00:00+00:00 (book signing event date) + bs.event_date = datetime.datetime(2008, 4, 2, 22, 0, tzinfo=timezone.utc) + bs.save() + res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) + self.assertEqual(res.status_code, 404) diff --git a/tests/generic_views/test_detail.py b/tests/generic_views/test_detail.py new file mode 100644 index 0000000000..faee05688e --- /dev/null +++ b/tests/generic_views/test_detail.py @@ -0,0 +1,101 @@ +from __future__ import absolute_import + +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase +from django.views.generic.base import View + +from .models import Artist, Author, Page + + +class DetailViewTest(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'generic_views.urls' + + def test_simple_object(self): + res = self.client.get('/detail/obj/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], {'foo': 'bar'}) + self.assertTrue(isinstance(res.context['view'], View)) + self.assertTemplateUsed(res, 'generic_views/detail.html') + + def test_detail_by_pk(self): + res = self.client.get('/detail/author/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_detail_by_custom_pk(self): + res = self.client.get('/detail/author/bycustompk/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_detail_by_slug(self): + res = self.client.get('/detail/author/byslug/scott-rosenberg/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(slug='scott-rosenberg')) + self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg')) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_detail_by_custom_slug(self): + res = self.client.get('/detail/author/bycustomslug/scott-rosenberg/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(slug='scott-rosenberg')) + self.assertEqual(res.context['author'], Author.objects.get(slug='scott-rosenberg')) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_verbose_name(self): + res = self.client.get('/detail/artist/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Artist.objects.get(pk=1)) + self.assertEqual(res.context['artist'], Artist.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/artist_detail.html') + + def test_template_name(self): + res = self.client.get('/detail/author/1/template_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/about.html') + + def test_template_name_suffix(self): + res = self.client.get('/detail/author/1/template_name_suffix/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['author'], Author.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/author_view.html') + + def test_template_name_field(self): + res = self.client.get('/detail/page/1/field/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Page.objects.get(pk=1)) + self.assertEqual(res.context['page'], Page.objects.get(pk=1)) + self.assertTemplateUsed(res, 'generic_views/page_template.html') + + def test_context_object_name(self): + res = self.client.get('/detail/author/1/context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertEqual(res.context['thingy'], Author.objects.get(pk=1)) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_duplicated_context_object_name(self): + res = self.client.get('/detail/author/1/dupe_context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=1)) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/author_detail.html') + + def test_invalid_url(self): + self.assertRaises(AttributeError, self.client.get, '/detail/author/invalid/url/') + + def test_invalid_queryset(self): + self.assertRaises(ImproperlyConfigured, self.client.get, '/detail/author/invalid/qs/') + + def test_non_model_object_with_meta(self): + res = self.client.get('/detail/nonmodel/1/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'].id, "non_model_1") diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py new file mode 100644 index 0000000000..c54d632363 --- /dev/null +++ b/tests/generic_views/test_edit.py @@ -0,0 +1,317 @@ +from __future__ import absolute_import + +from django.core.exceptions import ImproperlyConfigured +from django.core.urlresolvers import reverse +from django import forms +from django.test import TestCase +from django.utils.unittest import expectedFailure +from django.views.generic.base import View +from django.views.generic.edit import FormMixin + +from . import views +from .models import Artist, Author + + +class FormMixinTests(TestCase): + def test_initial_data(self): + """ Test instance independence of initial data dict (see #16138) """ + initial_1 = FormMixin().get_initial() + initial_1['foo'] = 'bar' + initial_2 = FormMixin().get_initial() + self.assertNotEqual(initial_1, initial_2) + + +class BasicFormTests(TestCase): + urls = 'generic_views.urls' + + def test_post_data(self): + res = self.client.post('/contact/', {'name': "Me", 'message': "Hello"}) + self.assertRedirects(res, 'http://testserver/list/authors/') + + +class ModelFormMixinTests(TestCase): + def test_get_form(self): + form_class = views.AuthorGetQuerySetFormView().get_form_class() + self.assertEqual(form_class._meta.model, Author) + +class CreateViewTests(TestCase): + urls = 'generic_views.urls' + + def test_create(self): + res = self.client.get('/edit/authors/create/') + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertTrue(isinstance(res.context['view'], View)) + self.assertFalse('object' in res.context) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + + res = self.client.post('/edit/authors/create/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), ['']) + + def test_create_invalid(self): + res = self.client.post('/edit/authors/create/', + {'name': 'A' * 101, 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + self.assertEqual(len(res.context['form'].errors), 1) + self.assertEqual(Author.objects.count(), 0) + + def test_create_with_object_url(self): + res = self.client.post('/edit/artists/create/', + {'name': 'Rene Magritte'}) + self.assertEqual(res.status_code, 302) + artist = Artist.objects.get(name='Rene Magritte') + self.assertRedirects(res, 'http://testserver/detail/artist/%d/' % artist.pk) + self.assertQuerysetEqual(Artist.objects.all(), ['']) + + def test_create_with_redirect(self): + res = self.client.post('/edit/authors/create/redirect/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/edit/authors/create/') + self.assertQuerysetEqual(Author.objects.all(), ['']) + + def test_create_with_interpolated_redirect(self): + res = self.client.post('/edit/authors/create/interpolate_redirect/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertQuerysetEqual(Author.objects.all(), ['']) + self.assertEqual(res.status_code, 302) + pk = Author.objects.all()[0].pk + self.assertRedirects(res, 'http://testserver/edit/author/%d/update/' % pk) + + def test_create_with_special_properties(self): + res = self.client.get('/edit/authors/create/special/') + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], views.AuthorForm)) + self.assertFalse('object' in res.context) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/form.html') + + res = self.client.post('/edit/authors/create/special/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + obj = Author.objects.get(slug='randall-munroe') + self.assertRedirects(res, reverse('author_detail', kwargs={'pk': obj.pk})) + self.assertQuerysetEqual(Author.objects.all(), ['']) + + def test_create_without_redirect(self): + try: + res = self.client.post('/edit/authors/create/naive/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') + except ImproperlyConfigured: + pass + + def test_create_restricted(self): + res = self.client.post('/edit/authors/create/restricted/', + {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/') + + +class UpdateViewTests(TestCase): + urls = 'generic_views.urls' + + def test_update_post(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.get('/edit/author/%d/update/' % a.pk) + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) + self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + + # Modification with both POST and PUT (browser compatible) + res = self.client.post('/edit/author/%d/update/' % a.pk, + {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), ['']) + + @expectedFailure + def test_update_put(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.get('/edit/author/%d/update/' % a.pk) + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + + res = self.client.put('/edit/author/%d/update/' % a.pk, + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + # Here is the expected failure. PUT data are not processed in any special + # way by django. So the request will equal to a POST without data, hence + # the form will be invalid and redisplayed with errors (status code 200). + # See also #12635 + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), ['']) + + def test_update_invalid(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/%d/update/' % a.pk, + {'name': 'A' * 101, 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + self.assertEqual(len(res.context['form'].errors), 1) + self.assertQuerysetEqual(Author.objects.all(), ['']) + + def test_update_with_object_url(self): + a = Artist.objects.create(name='Rene Magritte') + res = self.client.post('/edit/artists/%d/update/' % a.pk, + {'name': 'Rene Magritte'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/detail/artist/%d/' % a.pk) + self.assertQuerysetEqual(Artist.objects.all(), ['']) + + def test_update_with_redirect(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/%d/update/redirect/' % a.pk, + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/edit/authors/create/') + self.assertQuerysetEqual(Author.objects.all(), ['']) + + def test_update_with_interpolated_redirect(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/%d/update/interpolate_redirect/' % a.pk, + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + self.assertQuerysetEqual(Author.objects.all(), ['']) + self.assertEqual(res.status_code, 302) + pk = Author.objects.all()[0].pk + self.assertRedirects(res, 'http://testserver/edit/author/%d/update/' % pk) + + def test_update_with_special_properties(self): + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.get('/edit/author/%d/update/special/' % a.pk) + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], views.AuthorForm)) + self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) + self.assertEqual(res.context['thingy'], Author.objects.get(pk=a.pk)) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/form.html') + + res = self.client.post('/edit/author/%d/update/special/' % a.pk, + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/detail/author/%d/' % a.pk) + self.assertQuerysetEqual(Author.objects.all(), ['']) + + def test_update_without_redirect(self): + try: + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/%d/update/naive/' % a.pk, + {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'}) + self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') + except ImproperlyConfigured: + pass + + def test_update_get_object(self): + a = Author.objects.create( + pk=1, + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.get('/edit/author/update/') + self.assertEqual(res.status_code, 200) + self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertTrue(isinstance(res.context['view'], View)) + self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) + self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) + self.assertTemplateUsed(res, 'generic_views/author_form.html') + + # Modification with both POST and PUT (browser compatible) + res = self.client.post('/edit/author/update/', + {'name': 'Randall Munroe (xkcd)', 'slug': 'randall-munroe'}) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), ['']) + + +class DeleteViewTests(TestCase): + urls = 'generic_views.urls' + + def test_delete_by_post(self): + a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.get('/edit/author/%d/delete/' % a.pk) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) + self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) + self.assertTemplateUsed(res, 'generic_views/author_confirm_delete.html') + + # Deletion with POST + res = self.client.post('/edit/author/%d/delete/' % a.pk) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_by_delete(self): + # Deletion with browser compatible DELETE method + a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.delete('/edit/author/%d/delete/' % a.pk) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_with_redirect(self): + a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.post('/edit/author/%d/delete/redirect/' % a.pk) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/edit/authors/create/') + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_with_interpolated_redirect(self): + a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.post('/edit/author/%d/delete/interpolate_redirect/' % a.pk) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/edit/authors/create/?deleted=%d' % a.pk) + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_with_special_properties(self): + a = Author.objects.create(**{'name': 'Randall Munroe', 'slug': 'randall-munroe'}) + res = self.client.get('/edit/author/%d/delete/special/' % a.pk) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) + self.assertEqual(res.context['thingy'], Author.objects.get(pk=a.pk)) + self.assertFalse('author' in res.context) + self.assertTemplateUsed(res, 'generic_views/confirm_delete.html') + + res = self.client.post('/edit/author/%d/delete/special/' % a.pk) + self.assertEqual(res.status_code, 302) + self.assertRedirects(res, 'http://testserver/list/authors/') + self.assertQuerysetEqual(Author.objects.all(), []) + + def test_delete_without_redirect(self): + try: + a = Author.objects.create( + name='Randall Munroe', + slug='randall-munroe', + ) + res = self.client.post('/edit/author/%d/delete/naive/' % a.pk) + self.fail('Should raise exception -- No redirect URL provided, and no get_absolute_url provided') + except ImproperlyConfigured: + pass + diff --git a/tests/generic_views/test_forms.py b/tests/generic_views/test_forms.py new file mode 100644 index 0000000000..e036ad8afc --- /dev/null +++ b/tests/generic_views/test_forms.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import + +from django import forms + +from .models import Author + + +class AuthorForm(forms.ModelForm): + name = forms.CharField() + slug = forms.SlugField() + + class Meta: + model = Author + + +class ContactForm(forms.Form): + name = forms.CharField() + message = forms.CharField(widget=forms.Textarea) diff --git a/tests/generic_views/test_list.py b/tests/generic_views/test_list.py new file mode 100644 index 0000000000..cc4d2f5966 --- /dev/null +++ b/tests/generic_views/test_list.py @@ -0,0 +1,217 @@ +from __future__ import absolute_import + +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase +from django.test.utils import override_settings +from django.views.generic.base import View +from django.utils.encoding import force_str + +from .models import Author, Artist + + +class ListViewTests(TestCase): + fixtures = ['generic-views-test-data.json'] + urls = 'generic_views.urls' + + def test_items(self): + res = self.client.get('/list/dict/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/list.html') + self.assertEqual(res.context['object_list'][0]['first'], 'John') + + def test_queryset(self): + res = self.client.get('/list/authors/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertTrue(isinstance(res.context['view'], View)) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertIsNone(res.context['paginator']) + self.assertIsNone(res.context['page_obj']) + self.assertFalse(res.context['is_paginated']) + + 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, 'generic_views/author_list.html') + self.assertEqual(len(res.context['object_list']), 30) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertTrue(res.context['is_paginated']) + self.assertEqual(res.context['page_obj'].number, 1) + self.assertEqual(res.context['paginator'].num_pages, 4) + self.assertEqual(res.context['author_list'][0].name, 'Author 00') + self.assertEqual(list(res.context['author_list'])[-1].name, 'Author 29') + + def test_paginated_queryset_shortdata(self): + # Test that short datasets ALSO result in a paginated view. + res = self.client.get('/list/authors/paginated/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertEqual(res.context['page_obj'].number, 1) + self.assertEqual(res.context['paginator'].num_pages, 1) + self.assertFalse(res.context['is_paginated']) + + 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, 'generic_views/author_list.html') + self.assertEqual(len(res.context['object_list']), 30) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertEqual(res.context['author_list'][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['object_list']), 10) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertEqual(res.context['author_list'][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, 'generic_views/author_list.html') + self.assertEqual(len(res.context['object_list']), 30) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertEqual(res.context['author_list'][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_paginated_custom_paginator_class(self): + self._make_authors(7) + res = self.client.get('/list/authors/paginated/custom_class/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['paginator'].num_pages, 1) + # Custom pagination allows for 2 orphans on a page size of 5 + self.assertEqual(len(res.context['object_list']), 7) + + def test_paginated_custom_page_kwarg(self): + self._make_authors(100) + res = self.client.get('/list/authors/paginated/custom_page_kwarg/', {'pagina': '2'}) + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + self.assertEqual(len(res.context['object_list']), 30) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertEqual(res.context['author_list'][0].name, 'Author 30') + self.assertEqual(res.context['page_obj'].number, 2) + + def test_paginated_custom_paginator_constructor(self): + self._make_authors(7) + res = self.client.get('/list/authors/paginated/custom_constructor/') + self.assertEqual(res.status_code, 200) + # Custom pagination allows for 2 orphans on a page size of 5 + self.assertEqual(len(res.context['object_list']), 7) + + def test_paginated_orphaned_queryset(self): + self._make_authors(92) + res = self.client.get('/list/authors/paginated-orphaned/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['page_obj'].number, 1) + res = self.client.get( + '/list/authors/paginated-orphaned/', {'page': 'last'}) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['page_obj'].number, 3) + res = self.client.get( + '/list/authors/paginated-orphaned/', {'page': '3'}) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['page_obj'].number, 3) + res = self.client.get( + '/list/authors/paginated-orphaned/', {'page': '4'}) + self.assertEqual(res.status_code, 404) + + def test_paginated_non_queryset(self): + res = self.client.get('/list/dict/paginated/') + + self.assertEqual(res.status_code, 200) + self.assertEqual(len(res.context['object_list']), 1) + + def test_verbose_name(self): + res = self.client.get('/list/artists/') + self.assertEqual(res.status_code, 200) + self.assertTemplateUsed(res, 'generic_views/list.html') + self.assertEqual(list(res.context['object_list']), list(Artist.objects.all())) + self.assertIs(res.context['artist_list'], res.context['object_list']) + self.assertIsNone(res.context['paginator']) + self.assertIsNone(res.context['page_obj']) + self.assertFalse(res.context['is_paginated']) + + 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_name(self): + res = self.client.get('/list/authors/template_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertTemplateUsed(res, 'generic_views/list.html') + + def test_template_name_suffix(self): + res = self.client.get('/list/authors/template_name_suffix/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertTemplateUsed(res, 'generic_views/author_objects.html') + + def test_context_object_name(self): + res = self.client.get('/list/authors/context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertNotIn('authors', res.context) + self.assertIs(res.context['author_list'], res.context['object_list']) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + + def test_duplicate_context_object_name(self): + res = self.client.get('/list/authors/dupe_context_object_name/') + self.assertEqual(res.status_code, 200) + self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertNotIn('authors', res.context) + self.assertNotIn('author_list', res.context) + self.assertTemplateUsed(res, 'generic_views/author_list.html') + + def test_missing_items(self): + self.assertRaises(ImproperlyConfigured, self.client.get, '/list/authors/invalid/') + + def test_paginated_list_view_does_not_load_entire_table(self): + # Regression test for #17535 + self._make_authors(3) + # 1 query for authors + with self.assertNumQueries(1): + self.client.get('/list/authors/notempty/') + # same as above + 1 query to test if authors exist + 1 query for pagination + with self.assertNumQueries(3): + self.client.get('/list/authors/notempty/paginated/') + + @override_settings(DEBUG=True) + def test_paginated_list_view_returns_useful_message_on_invalid_page(self): + # test for #19240 + # tests that source exception's message is included in page + self._make_authors(1) + res = self.client.get('/list/authors/paginated/2/') + self.assertEqual(res.status_code, 404) + self.assertEqual(force_str(res.context.get('reason')), + "Invalid page (2): That page contains no results") + + 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) diff --git a/tests/generic_views/tests.py b/tests/generic_views/tests.py index 3f80600efe..244aa3c701 100644 --- a/tests/generic_views/tests.py +++ b/tests/generic_views/tests.py @@ -1,11 +1,11 @@ from __future__ import absolute_import -from .base import (ViewTest, TemplateViewTest, RedirectViewTest, +from .test_base import (ViewTest, TemplateViewTest, RedirectViewTest, GetContextDataTest) -from .dates import (ArchiveIndexViewTests, YearArchiveViewTests, +from .test_dates import (ArchiveIndexViewTests, YearArchiveViewTests, MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests, DateDetailViewTests) -from .detail import DetailViewTest -from .edit import (FormMixinTests, BasicFormTests, ModelFormMixinTests, +from .test_detail import DetailViewTest +from .test_edit import (FormMixinTests, BasicFormTests, ModelFormMixinTests, CreateViewTests, UpdateViewTests, DeleteViewTests) -from .list import ListViewTests +from .test_list import ListViewTests diff --git a/tests/generic_views/views.py b/tests/generic_views/views.py index 856565a953..69bfcb32dd 100644 --- a/tests/generic_views/views.py +++ b/tests/generic_views/views.py @@ -7,7 +7,7 @@ from django.core.urlresolvers import reverse, reverse_lazy from django.utils.decorators import method_decorator from django.views import generic -from .forms import AuthorForm, ContactForm +from .test_forms import AuthorForm, ContactForm from .models import Artist, Author, Book, Page, BookSigning -- cgit v1.3