diff options
| author | Andrew Godwin <andrew@aeracode.org> | 2013-06-19 12:03:20 +0100 |
|---|---|---|
| committer | Andrew Godwin <andrew@aeracode.org> | 2013-06-19 12:05:23 +0100 |
| commit | 9daf81b94e7945d7adfdf62206cf9cb436166f0d (patch) | |
| tree | 0ca2a77977432d0c139886297afb433569b588ca /tests | |
| parent | 315ab41e416c777d4f42932d42df07872e8f8895 (diff) | |
| parent | d9a43545be1af95a13c181c8b178f5631d3a4148 (diff) | |
Merge remote-tracking branch 'core/master' into schema-alteration
Conflicts:
django/db/models/loading.py
Diffstat (limited to 'tests')
59 files changed, 1411 insertions, 666 deletions
diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index 78ccf074d5..41dc0e32de 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -505,9 +505,11 @@ class TestInlinePermissions(TestCase): @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): - webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' + + available_apps = ['admin_inlines'] + AdminSeleniumWebDriverTestCase.available_apps fixtures = ['admin-views-users.xml'] urls = "admin_inlines.urls" + webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' def test_add_stackeds(self): """ diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 9e6b1f557b..2f399acb23 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -1192,6 +1192,21 @@ class ManageRunserver(AdminScriptTestCase): self.cmd.handle(addrport="deadbeef:7654") self.assertServerSettings('deadbeef', '7654') +class ManageRunserverEmptyAllowedHosts(AdminScriptTestCase): + def setUp(self): + self.write_settings('settings.py', sdict={ + 'ALLOWED_HOSTS': [], + 'DEBUG': False, + }) + + def tearDown(self): + self.remove_settings('settings.py') + + def test_empty_allowed_hosts_error(self): + out, err = self.run_manage(['runserver']) + self.assertNoOutput(out) + self.assertOutput(err, 'CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.') + ########################################################################## # COMMAND PROCESSING TESTS @@ -1459,6 +1474,13 @@ class ArgumentOrder(AdminScriptTestCase): class StartProject(LiveServerTestCase, AdminScriptTestCase): + available_apps = [ + 'admin_scripts', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + ] + def test_wrong_args(self): "Make sure passing the wrong kinds of arguments raises a CommandError" out, err = self.run_django_admin(['startproject']) diff --git a/tests/admin_util/tests.py b/tests/admin_util/tests.py index 4a9a203f50..637f643261 100644 --- a/tests/admin_util/tests.py +++ b/tests/admin_util/tests.py @@ -301,7 +301,7 @@ class UtilTests(SimpleTestCase): self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(), '<label for="id_text" class="required inline"><i>text</i>:</label>') self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(), - '<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i></label>') + '<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i>:</label>') # normal strings needs to be escaped class MyForm(forms.Form): @@ -312,7 +312,7 @@ class UtilTests(SimpleTestCase): self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(), '<label for="id_text" class="required inline">&text:</label>') self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(), - '<label for="id_cb" class="vCheckboxLabel required inline">&cb</label>') + '<label for="id_cb" class="vCheckboxLabel required inline">&cb:</label>') def test_flatten_fieldsets(self): """ diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 8c8a65318c..528c728069 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -22,7 +22,7 @@ from django.contrib.admin.util import quote from django.contrib.admin.views.main import IS_POPUP_VAR from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.auth import REDIRECT_FIELD_NAME -from django.contrib.auth.models import Group, User, Permission, UNUSABLE_PASSWORD +from django.contrib.auth.models import Group, User, Permission from django.contrib.contenttypes.models import ContentType from django.core.urlresolvers import reverse from django.db import connection @@ -3308,9 +3308,11 @@ class PrePopulatedTest(TestCase): @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class SeleniumAdminViewsFirefoxTests(AdminSeleniumWebDriverTestCase): - webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' - urls = "admin_views.urls" + + available_apps = ['admin_views'] + AdminSeleniumWebDriverTestCase.available_apps fixtures = ['admin-views-users.xml'] + urls = "admin_views.urls" + webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' def test_prepopulated_fields(self): """ @@ -3630,7 +3632,7 @@ class UserAdminTest(TestCase): new_user = User.objects.order_by('-id')[0] self.assertRedirects(response, '/test_admin/admin/auth/user/%s/' % new_user.pk) self.assertEqual(User.objects.count(), user_count + 1) - self.assertNotEqual(new_user.password, UNUSABLE_PASSWORD) + self.assertTrue(new_user.has_usable_password()) def test_save_continue_editing_button(self): user_count = User.objects.count() @@ -3643,7 +3645,7 @@ class UserAdminTest(TestCase): new_user = User.objects.order_by('-id')[0] self.assertRedirects(response, '/test_admin/admin/auth/user/%s/' % new_user.pk) self.assertEqual(User.objects.count(), user_count + 1) - self.assertNotEqual(new_user.password, UNUSABLE_PASSWORD) + self.assertTrue(new_user.has_usable_password()) def test_password_mismatch(self): response = self.client.post('/test_admin/admin/auth/user/add/', { @@ -3689,7 +3691,7 @@ class UserAdminTest(TestCase): new_user = User.objects.order_by('-id')[0] self.assertRedirects(response, '/test_admin/admin/auth/user/add/') self.assertEqual(User.objects.count(), user_count + 1) - self.assertNotEqual(new_user.password, UNUSABLE_PASSWORD) + self.assertTrue(new_user.has_usable_password()) def test_user_permission_performance(self): u = User.objects.all()[0] @@ -4155,3 +4157,156 @@ class AdminUserMessageTest(TestCase): self.assertContains(response, '<li class="extra_tag info">Test tags</li>', html=True) + + +@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class AdminKeepChangeListFiltersTests(TestCase): + urls = "admin_views.urls" + fixtures = ['admin-views-users.xml'] + + def setUp(self): + self.client.login(username='super', password='secret') + + def tearDown(self): + self.client.logout() + + def get_changelist_filters_querystring(self): + return urlencode({ + 'is_superuser__exact': 0, + 'is_staff__exact': 0, + }) + + def get_preserved_filters_querystring(self): + return urlencode({ + '_changelist_filters': self.get_changelist_filters_querystring() + }) + + def get_sample_user_id(self): + return 104 + + def get_changelist_url(self): + return '%s?%s' % ( + reverse('admin:auth_user_changelist'), + self.get_changelist_filters_querystring(), + ) + + def get_add_url(self): + return '%s?%s' % ( + reverse('admin:auth_user_add'), + self.get_preserved_filters_querystring(), + ) + + def get_change_url(self, user_id=None): + if user_id is None: + user_id = self.get_sample_user_id() + return "%s?%s" % ( + reverse('admin:auth_user_change', args=(user_id,)), + self.get_preserved_filters_querystring(), + ) + + def get_history_url(self, user_id=None): + if user_id is None: + user_id = self.get_sample_user_id() + return "%s?%s" % ( + reverse('admin:auth_user_history', args=(user_id,)), + self.get_preserved_filters_querystring(), + ) + + def get_delete_url(self, user_id=None): + if user_id is None: + user_id = self.get_sample_user_id() + return "%s?%s" % ( + reverse('admin:auth_user_delete', args=(user_id,)), + self.get_preserved_filters_querystring(), + ) + + def test_changelist_view(self): + response = self.client.get(self.get_changelist_url()) + self.assertEqual(response.status_code, 200) + + # Check the `change_view` link has the correct querystring. + detail_link = """<a href="%s">joepublic</a>""" % self.get_change_url() + self.assertContains(response, detail_link, count=1) + + def test_change_view(self): + # Get the `change_view`. + response = self.client.get(self.get_change_url()) + self.assertEqual(response.status_code, 200) + + # Check the form action. + form_action = """<form enctype="multipart/form-data" action="?%s" method="post" id="user_form">""" % self.get_preserved_filters_querystring() + self.assertContains(response, form_action, count=1) + + # Check the history link. + history_link = """<a href="%s" class="historylink">History</a>""" % self.get_history_url() + self.assertContains(response, history_link, count=1) + + # Check the delete link. + delete_link = """<a href="%s" class="deletelink">Delete</a>""" % (self.get_delete_url()) + self.assertContains(response, delete_link, count=1) + + # Test redirect on "Save". + post_data = { + 'username': 'joepublic', + 'last_login_0': '2007-05-30', + 'last_login_1': '13:20:10', + 'date_joined_0': '2007-05-30', + 'date_joined_1': '13:20:10', + } + + post_data['_save'] = 1 + response = self.client.post(self.get_change_url(), data=post_data) + self.assertRedirects(response, self.get_changelist_url()) + post_data.pop('_save') + + # Test redirect on "Save and continue". + post_data['_continue'] = 1 + response = self.client.post(self.get_change_url(), data=post_data) + self.assertRedirects(response, self.get_change_url()) + post_data.pop('_continue') + + # Test redirect on "Save and add new". + post_data['_addanother'] = 1 + response = self.client.post(self.get_change_url(), data=post_data) + self.assertRedirects(response, self.get_add_url()) + post_data.pop('_addanother') + + def test_add_view(self): + # Get the `add_view`. + response = self.client.get(self.get_add_url()) + self.assertEqual(response.status_code, 200) + + # Check the form action. + form_action = """<form enctype="multipart/form-data" action="?%s" method="post" id="user_form">""" % self.get_preserved_filters_querystring() + self.assertContains(response, form_action, count=1) + + # Test redirect on "Save". + post_data = { + 'username': 'dummy', + 'password1': 'test', + 'password2': 'test', + } + + post_data['_save'] = 1 + response = self.client.post(self.get_add_url(), data=post_data) + self.assertRedirects(response, self.get_change_url(User.objects.latest('pk').pk)) + post_data.pop('_save') + + # Test redirect on "Save and continue". + post_data['username'] = 'dummy2' + post_data['_continue'] = 1 + response = self.client.post(self.get_add_url(), data=post_data) + self.assertRedirects(response, self.get_change_url(User.objects.latest('pk').pk)) + post_data.pop('_continue') + + # Test redirect on "Save and add new". + post_data['username'] = 'dummy3' + post_data['_addanother'] = 1 + response = self.client.post(self.get_add_url(), data=post_data) + self.assertRedirects(response, self.get_add_url()) + post_data.pop('_addanother') + + def test_delete_view(self): + # Test redirect on "Delete". + response = self.client.post(self.get_delete_url(), {'post': 'yes'}) + self.assertRedirects(response, self.get_changelist_url()) diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 98f41c1490..4823883f42 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -470,9 +470,11 @@ class RelatedFieldWidgetWrapperTests(DjangoTestCase): @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class DateTimePickerSeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): - webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' + + available_apps = ['admin_widgets'] + AdminSeleniumWebDriverTestCase.available_apps fixtures = ['admin-widgets-users.xml'] urls = "admin_widgets.urls" + webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' def test_show_hide_date_time_picker_widgets(self): """ @@ -526,9 +528,11 @@ class DateTimePickerSeleniumIETests(DateTimePickerSeleniumFirefoxTests): @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): - webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' + + available_apps = ['admin_widgets'] + AdminSeleniumWebDriverTestCase.available_apps fixtures = ['admin-widgets-users.xml'] urls = "admin_widgets.urls" + webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' def setUp(self): self.lisa = models.Student.objects.create(name='Lisa') diff --git a/tests/backends/tests.py b/tests/backends/tests.py index 08cae0f7c3..b6e3e9e36f 100644 --- a/tests/backends/tests.py +++ b/tests/backends/tests.py @@ -345,6 +345,8 @@ class PostgresNewConnectionTest(TestCase): # connection would implicitly rollback and cause problems during teardown. class ConnectionCreatedSignalTest(TransactionTestCase): + available_apps = [] + # Unfortunately with sqlite3 the in-memory test database cannot be closed, # and so it cannot be re-opened during testing. @skipUnlessDBFeature('test_db_allows_multiple_connections') @@ -514,6 +516,8 @@ class BackendTestCase(TestCase): # verify if its type is django.database.db.IntegrityError. class FkConstraintsTests(TransactionTestCase): + available_apps = ['backends'] + def setUp(self): # Create a Reporter. self.r = models.Reporter.objects.create(first_name='John', last_name='Smith') @@ -777,6 +781,9 @@ class MySQLPKZeroTests(TestCase): class DBConstraintTestCase(TransactionTestCase): + + available_apps = ['backends'] + def test_can_reference_existant(self): obj = models.Object.objects.create() ref = models.ObjectReference.objects.create(obj=obj) diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 6bf46cce9b..4b79c47be5 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -686,6 +686,9 @@ class ModelTest(TestCase): class ConcurrentSaveTests(TransactionTestCase): + + available_apps = ['basic'] + @skipUnlessDBFeature('test_db_allows_multiple_connections') def test_concurrent_delete_with_save(self): """ diff --git a/tests/cache/tests.py b/tests/cache/tests.py index da80c48058..7413a4aae6 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -827,6 +827,8 @@ def custom_key_func(key, key_prefix, version): class DBCacheTests(BaseCacheTests, TransactionTestCase): + + available_apps = ['cache'] backend_name = 'django.core.cache.backends.db.DatabaseCache' def setUp(self): diff --git a/tests/shared_models/__init__.py b/tests/compat_checks/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/tests/shared_models/__init__.py +++ b/tests/compat_checks/__init__.py diff --git a/tests/compat_checks/models.py b/tests/compat_checks/models.py new file mode 100644 index 0000000000..78a10abba6 --- /dev/null +++ b/tests/compat_checks/models.py @@ -0,0 +1 @@ +# Stubby. diff --git a/tests/compat_checks/tests.py b/tests/compat_checks/tests.py new file mode 100644 index 0000000000..879988c905 --- /dev/null +++ b/tests/compat_checks/tests.py @@ -0,0 +1,107 @@ +from django.core.compat_checks import base +from django.core.compat_checks import django_1_6_0 +from django.core.management.commands import checksetup +from django.core.management import call_command +from django.test import TestCase + + +class StubCheckModule(object): + # Has no ``run_checks`` attribute & will trigger a warning. + __name__ = 'StubCheckModule' + + +class FakeWarnings(object): + def __init__(self): + self._warnings = [] + + def warn(self, message): + self._warnings.append(message) + + +class CompatChecksTestCase(TestCase): + def setUp(self): + super(CompatChecksTestCase, self).setUp() + + # We're going to override the list of checks to perform for test + # consistency in the future. + self.old_compat_checks = base.COMPAT_CHECKS + base.COMPAT_CHECKS = [ + django_1_6_0, + ] + + def tearDown(self): + # Restore what's supposed to be in ``COMPAT_CHECKS``. + base.COMPAT_CHECKS = self.old_compat_checks + super(CompatChecksTestCase, self).tearDown() + + def test_check_test_runner_new_default(self): + with self.settings(TEST_RUNNER='django.test.runner.DiscoverRunner'): + result = django_1_6_0.check_test_runner() + self.assertTrue("You have not explicitly set 'TEST_RUNNER'" in result) + + def test_check_test_runner_overridden(self): + with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'): + self.assertEqual(django_1_6_0.check_test_runner(), None) + + def test_run_checks_new_default(self): + with self.settings(TEST_RUNNER='django.test.runner.DiscoverRunner'): + result = django_1_6_0.run_checks() + self.assertEqual(len(result), 1) + self.assertTrue("You have not explicitly set 'TEST_RUNNER'" in result[0]) + + def test_run_checks_overridden(self): + with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'): + self.assertEqual(len(django_1_6_0.run_checks()), 0) + + def test_check_compatibility(self): + with self.settings(TEST_RUNNER='django.test.runner.DiscoverRunner'): + result = base.check_compatibility() + self.assertEqual(len(result), 1) + self.assertTrue("You have not explicitly set 'TEST_RUNNER'" in result[0]) + + with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'): + self.assertEqual(len(base.check_compatibility()), 0) + + def test_check_compatibility_warning(self): + # First, we're patching over the ``COMPAT_CHECKS`` with a stub which + # will trigger the warning. + base.COMPAT_CHECKS = [ + StubCheckModule(), + ] + + # Next, we unfortunately have to patch out ``warnings``. + old_warnings = base.warnings + base.warnings = FakeWarnings() + + self.assertEqual(len(base.warnings._warnings), 0) + + with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'): + self.assertEqual(len(base.check_compatibility()), 0) + + self.assertEqual(len(base.warnings._warnings), 1) + self.assertTrue("The 'StubCheckModule' module lacks a 'run_checks'" in base.warnings._warnings[0]) + + # Restore the ``warnings``. + base.warnings = old_warnings + + def test_management_command(self): + # Again, we unfortunately have to patch out ``warnings``. Different + old_warnings = checksetup.warnings + checksetup.warnings = FakeWarnings() + + self.assertEqual(len(checksetup.warnings._warnings), 0) + + # Should not produce any warnings. + with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'): + call_command('checksetup') + + self.assertEqual(len(checksetup.warnings._warnings), 0) + + with self.settings(TEST_RUNNER='django.test.runner.DiscoverRunner'): + call_command('checksetup') + + self.assertEqual(len(checksetup.warnings._warnings), 1) + self.assertTrue("You have not explicitly set 'TEST_RUNNER'" in checksetup.warnings._warnings[0]) + + # Restore the ``warnings``. + base.warnings = old_warnings diff --git a/tests/delete_regress/tests.py b/tests/delete_regress/tests.py index e88c95e229..a4908b2121 100644 --- a/tests/delete_regress/tests.py +++ b/tests/delete_regress/tests.py @@ -16,6 +16,9 @@ from .models import (Book, Award, AwardNote, Person, Child, Toy, PlayedWith, # Can't run this test under SQLite, because you can't # get two connections to an in-memory database. class DeleteLockingTest(TransactionTestCase): + + available_apps = ['delete_regress'] + def setUp(self): # Create a second connection to the default database new_connections = ConnectionHandler(settings.DATABASES) @@ -107,6 +110,9 @@ class DeleteCascadeTests(TestCase): class DeleteCascadeTransactionTests(TransactionTestCase): + + available_apps = ['delete_regress'] + def test_inheritance(self): """ Auto-created many-to-many through tables referencing a parent model are diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index 406a4d79b6..6c3c559660 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -7,6 +7,10 @@ import shutil import sys import tempfile import time +try: + from urllib.request import urlopen +except ImportError: # Python 2 + from urllib2 import urlopen import zlib from datetime import datetime, timedelta from io import BytesIO @@ -22,12 +26,11 @@ from django.core.files.base import File, ContentFile from django.core.files.images import get_image_dimensions from django.core.files.storage import FileSystemStorage, get_storage_class from django.core.files.uploadedfile import UploadedFile -from django.test import SimpleTestCase +from django.test import LiveServerTestCase, SimpleTestCase from django.utils import six from django.utils import unittest from django.utils._os import upath from django.test.utils import override_settings -from servers.tests import LiveServerBase try: from django.utils.image import Image @@ -613,10 +616,15 @@ class NoNameFileTestCase(unittest.TestCase): def test_noname_file_get_size(self): self.assertEqual(File(BytesIO(b'A file with no name')).size, 19) -class FileLikeObjectTestCase(LiveServerBase): + +class FileLikeObjectTestCase(LiveServerTestCase): """ Test file-like objects (#15644). """ + + available_apps = [] + urls = 'file_storage.urls' + def setUp(self): self.temp_dir = tempfile.mkdtemp() self.storage = FileSystemStorage(location=self.temp_dir) @@ -628,12 +636,10 @@ class FileLikeObjectTestCase(LiveServerBase): """ Test the File storage API with a file like object coming from urllib2.urlopen() """ - - file_like_object = self.urlopen('/example_view/') + file_like_object = urlopen(self.live_server_url + '/') f = File(file_like_object) stored_filename = self.storage.save("remote_file.html", f) - remote_file = self.urlopen('/example_view/') - + remote_file = urlopen(self.live_server_url + '/') with self.storage.open(stored_filename) as stored_file: self.assertEqual(stored_file.read(), remote_file.read()) diff --git a/tests/file_storage/urls.py b/tests/file_storage/urls.py new file mode 100644 index 0000000000..aefd8549e1 --- /dev/null +++ b/tests/file_storage/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import patterns, url +from django.http import HttpResponse + + +urlpatterns = patterns('', + url(r'^$', lambda req: HttpResponse('example view')), +) diff --git a/tests/fixtures/tests.py b/tests/fixtures/tests.py index 1b4d6eeeef..d0942afdb7 100644 --- a/tests/fixtures/tests.py +++ b/tests/fixtures/tests.py @@ -354,6 +354,13 @@ class FixtureLoadingTests(DumpDataAssertMixin, TestCase): class FixtureTransactionTests(DumpDataAssertMixin, TransactionTestCase): + available_apps = [ + 'fixtures', + 'django.contrib.contenttypes', + 'django.contrib.auth', + 'django.contrib.sites', + ] + @skipUnlessDBFeature('supports_forward_references') def test_format_discovery(self): # Load fixture 1 again, using format discovery diff --git a/tests/fixtures_model_package/tests.py b/tests/fixtures_model_package/tests.py index e0a35e300d..dbcc721d8f 100644 --- a/tests/fixtures_model_package/tests.py +++ b/tests/fixtures_model_package/tests.py @@ -26,6 +26,9 @@ class SampleTestCase(TestCase): class TestNoInitialDataLoading(TransactionTestCase): + + available_apps = ['fixtures_model_package'] + def test_syncdb(self): transaction.set_autocommit(False) try: diff --git a/tests/fixtures_regress/tests.py b/tests/fixtures_regress/tests.py index 52526ec338..0f6ac65976 100644 --- a/tests/fixtures_regress/tests.py +++ b/tests/fixtures_regress/tests.py @@ -685,6 +685,12 @@ class NaturalKeyFixtureTests(TestCase): class TestTicket11101(TransactionTestCase): + available_apps = [ + 'fixtures_regress', + 'django.contrib.auth', + 'django.contrib.contenttypes', + ] + def ticket_11101(self): management.call_command( 'loaddata', diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index 1a3fb44a66..633fde5026 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -1589,9 +1589,9 @@ class FormsTestCase(TestCase): # Recall from above that passing the "auto_id" argument to a Form gives each # field an "id" attribute. t = Template('''<form action=""> -<p>{{ form.username.label_tag }}: {{ form.username }}</p> -<p>{{ form.password1.label_tag }}: {{ form.password1 }}</p> -<p>{{ form.password2.label_tag }}: {{ form.password2 }}</p> +<p>{{ form.username.label_tag }} {{ form.username }}</p> +<p>{{ form.password1.label_tag }} {{ form.password1 }}</p> +<p>{{ form.password2.label_tag }} {{ form.password2 }}</p> <input type="submit" /> </form>''') self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action=""> @@ -1601,18 +1601,18 @@ class FormsTestCase(TestCase): <input type="submit" /> </form>""") self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id='id_%s')})), """<form action=""> -<p><label for="id_username">Username</label>: <input id="id_username" type="text" name="username" maxlength="10" /></p> -<p><label for="id_password1">Password1</label>: <input type="password" name="password1" id="id_password1" /></p> -<p><label for="id_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p> +<p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p> +<p><label for="id_password1">Password1:</label> <input type="password" name="password1" id="id_password1" /></p> +<p><label for="id_password2">Password2:</label> <input type="password" name="password2" id="id_password2" /></p> <input type="submit" /> </form>""") # User form.[field].help_text to output a field's help text. If the given field # does not have help text, nothing will be output. t = Template('''<form action=""> -<p>{{ form.username.label_tag }}: {{ form.username }}<br />{{ form.username.help_text }}</p> -<p>{{ form.password1.label_tag }}: {{ form.password1 }}</p> -<p>{{ form.password2.label_tag }}: {{ form.password2 }}</p> +<p>{{ form.username.label_tag }} {{ form.username }}<br />{{ form.username.help_text }}</p> +<p>{{ form.password1.label_tag }} {{ form.password1 }}</p> +<p>{{ form.password2.label_tag }} {{ form.password2 }}</p> <input type="submit" /> </form>''') self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action=""> @@ -1819,17 +1819,17 @@ class FormsTestCase(TestCase): testcases = [ # (args, kwargs, expected) # without anything: just print the <label> - ((), {}, '<label for="id_field">Field</label>'), + ((), {}, '<label for="id_field">Field:</label>'), # passing just one argument: overrides the field's label - (('custom',), {}, '<label for="id_field">custom</label>'), + (('custom',), {}, '<label for="id_field">custom:</label>'), # the overriden label is escaped - (('custom&',), {}, '<label for="id_field">custom&</label>'), - ((mark_safe('custom&'),), {}, '<label for="id_field">custom&</label>'), + (('custom&',), {}, '<label for="id_field">custom&:</label>'), + ((mark_safe('custom&'),), {}, '<label for="id_field">custom&:</label>'), # Passing attrs to add extra attributes on the <label> - ((), {'attrs': {'class': 'pretty'}}, '<label for="id_field" class="pretty">Field</label>') + ((), {'attrs': {'class': 'pretty'}}, '<label for="id_field" class="pretty">Field:</label>') ] for args, kwargs, expected in testcases: @@ -1844,8 +1844,8 @@ class FormsTestCase(TestCase): field = CharField() boundfield = SomeForm(auto_id='')['field'] - self.assertHTMLEqual(boundfield.label_tag(), 'Field') - self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&') + self.assertHTMLEqual(boundfield.label_tag(), 'Field:') + self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&:') def test_boundfield_label_tag_custom_widget_id_for_label(self): class CustomIdForLabelTextInput(TextInput): @@ -1861,5 +1861,12 @@ class FormsTestCase(TestCase): empty = CharField(widget=EmptyIdForLabelTextInput) form = SomeForm() - self.assertHTMLEqual(form['custom'].label_tag(), '<label for="custom_id_custom">Custom</label>') - self.assertHTMLEqual(form['empty'].label_tag(), '<label>Empty</label>') + self.assertHTMLEqual(form['custom'].label_tag(), '<label for="custom_id_custom">Custom:</label>') + self.assertHTMLEqual(form['empty'].label_tag(), '<label>Empty:</label>') + + def test_boundfield_empty_label(self): + class SomeForm(Form): + field = CharField(label='') + boundfield = SomeForm()['field'] + + self.assertHTMLEqual(boundfield.label_tag(), '<label for="id_field"></label>') diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py index 1e9e7db30c..b26017bc78 100644 --- a/tests/forms_tests/tests/test_formsets.py +++ b/tests/forms_tests/tests/test_formsets.py @@ -55,81 +55,82 @@ SplitDateTimeFormSet = formset_factory(SplitDateTimeForm) class FormsFormsetTestCase(TestCase): + + def make_choiceformset(self, formset_data=None, formset_class=ChoiceFormSet, + total_forms=None, initial_forms=0, max_num_forms=0, **kwargs): + """ + Make a ChoiceFormset from the given formset_data. + The data should be given as a list of (choice, votes) tuples. + """ + kwargs.setdefault('prefix', 'choices') + kwargs.setdefault('auto_id', False) + + if formset_data is None: + return formset_class(**kwargs) + + if total_forms is None: + total_forms = len(formset_data) + + def prefixed(*args): + args = (kwargs['prefix'],) + args + return '-'.join(args) + + data = { + prefixed('TOTAL_FORMS'): str(total_forms), + prefixed('INITIAL_FORMS'): str(initial_forms), + prefixed('MAX_NUM_FORMS'): str(max_num_forms), + } + for i, (choice, votes) in enumerate(formset_data): + data[prefixed(str(i), 'choice')] = choice + data[prefixed(str(i), 'votes')] = votes + + return formset_class(data, **kwargs) + def test_basic_formset(self): # A FormSet constructor takes the same arguments as Form. Let's create a FormSet # for adding data. By default, it displays 1 blank form. It can display more, # but we'll look at how to do so later. - formset = ChoiceFormSet(auto_id=False, prefix='choices') + formset = self.make_choiceformset() + self.assertHTMLEqual(str(formset), """<input type="hidden" name="choices-TOTAL_FORMS" value="1" /><input type="hidden" name="choices-INITIAL_FORMS" value="0" /><input type="hidden" name="choices-MAX_NUM_FORMS" value="1000" /> <tr><th>Choice:</th><td><input type="text" name="choices-0-choice" /></td></tr> <tr><th>Votes:</th><td><input type="number" name="choices-0-votes" /></td></tr>""") - # On thing to note is that there needs to be a special value in the data. This - # value tells the FormSet how many forms were displayed so it can tell how - # many forms it needs to clean and validate. You could use javascript to create - # new forms on the client side, but they won't get validated unless you increment - # the TOTAL_FORMS field appropriately. - - data = { - 'choices-TOTAL_FORMS': '1', # the number of forms rendered - 'choices-INITIAL_FORMS': '0', # the number of forms with initial data - 'choices-MAX_NUM_FORMS': '0', # max number of forms - 'choices-0-choice': 'Calexico', - 'choices-0-votes': '100', - } # We treat FormSet pretty much like we would treat a normal Form. FormSet has an # is_valid method, and a cleaned_data or errors attribute depending on whether all # the forms passed validation. However, unlike a Form instance, cleaned_data and # errors will be a list of dicts rather than just a single dict. - formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + formset = self.make_choiceformset([('Calexico', '100')]) self.assertTrue(formset.is_valid()) self.assertEqual([form.cleaned_data for form in formset.forms], [{'votes': 100, 'choice': 'Calexico'}]) # If a FormSet was not passed any data, its is_valid and has_changed # methods should return False. - formset = ChoiceFormSet() + formset = self.make_choiceformset() self.assertFalse(formset.is_valid()) self.assertFalse(formset.has_changed()) def test_formset_validation(self): # FormSet instances can also have an error attribute if validation failed for # any of the forms. - - data = { - 'choices-TOTAL_FORMS': '1', # the number of forms rendered - 'choices-INITIAL_FORMS': '0', # the number of forms with initial data - 'choices-MAX_NUM_FORMS': '0', # max number of forms - 'choices-0-choice': 'Calexico', - 'choices-0-votes': '', - } - - formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + formset = self.make_choiceformset([('Calexico', '')]) self.assertFalse(formset.is_valid()) self.assertEqual(formset.errors, [{'votes': ['This field is required.']}]) def test_formset_has_changed(self): # FormSet instances has_changed method will be True if any data is # passed to his forms, even if the formset didn't validate - data = { - 'choices-TOTAL_FORMS': '1', # the number of forms rendered - 'choices-INITIAL_FORMS': '0', # the number of forms with initial data - 'choices-MAX_NUM_FORMS': '0', # max number of forms - 'choices-0-choice': '', - 'choices-0-votes': '', - } - blank_formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + blank_formset = self.make_choiceformset([('', '')]) self.assertFalse(blank_formset.has_changed()) # invalid formset test - data['choices-0-choice'] = 'Calexico' - invalid_formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + invalid_formset = self.make_choiceformset([('Calexico', '')]) self.assertFalse(invalid_formset.is_valid()) self.assertTrue(invalid_formset.has_changed()) # valid formset test - data['choices-0-votes'] = '100' - valid_formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + valid_formset = self.make_choiceformset([('Calexico', '100')]) self.assertTrue(valid_formset.is_valid()) self.assertTrue(valid_formset.has_changed()) @@ -139,7 +140,7 @@ class FormsFormsetTestCase(TestCase): # an extra blank form is included. initial = [{'choice': 'Calexico', 'votes': 100}] - formset = ChoiceFormSet(initial=initial, auto_id=False, prefix='choices') + formset = self.make_choiceformset(initial=initial) form_output = [] for form in formset.forms: @@ -151,18 +152,7 @@ class FormsFormsetTestCase(TestCase): <li>Votes: <input type="number" name="choices-1-votes" /></li>""") # Let's simulate what would happen if we submitted this form. - - data = { - 'choices-TOTAL_FORMS': '2', # the number of forms rendered - 'choices-INITIAL_FORMS': '1', # the number of forms with initial data - 'choices-MAX_NUM_FORMS': '0', # max number of forms - 'choices-0-choice': 'Calexico', - 'choices-0-votes': '100', - 'choices-1-choice': '', - 'choices-1-votes': '', - } - - formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + formset = self.make_choiceformset([('Calexico', '100'), ('', '')], initial_forms=1) self.assertTrue(formset.is_valid()) self.assertEqual([form.cleaned_data for form in formset.forms], [{'votes': 100, 'choice': 'Calexico'}, {}]) @@ -172,18 +162,7 @@ class FormsFormsetTestCase(TestCase): # one of the fields of a blank form though, it will be validated. We may want to # required that at least x number of forms are completed, but we'll show how to # handle that later. - - data = { - 'choices-TOTAL_FORMS': '2', # the number of forms rendered - 'choices-INITIAL_FORMS': '1', # the number of forms with initial data - 'choices-MAX_NUM_FORMS': '0', # max number of forms - 'choices-0-choice': 'Calexico', - 'choices-0-votes': '100', - 'choices-1-choice': 'The Decemberists', - 'choices-1-votes': '', # missing value - } - - formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + formset = self.make_choiceformset([('Calexico', '100'), ('The Decemberists', '')], initial_forms=1) self.assertFalse(formset.is_valid()) self.assertEqual(formset.errors, [{}, {'votes': ['This field is required.']}]) @@ -191,18 +170,7 @@ class FormsFormsetTestCase(TestCase): # If we delete data that was pre-filled, we should get an error. Simply removing # data from form fields isn't the proper way to delete it. We'll see how to # handle that case later. - - data = { - 'choices-TOTAL_FORMS': '2', # the number of forms rendered - 'choices-INITIAL_FORMS': '1', # the number of forms with initial data - 'choices-MAX_NUM_FORMS': '0', # max number of forms - 'choices-0-choice': '', # deleted value - 'choices-0-votes': '', # deleted value - 'choices-1-choice': '', - 'choices-1-votes': '', - } - - formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + formset = self.make_choiceformset([('', ''), ('', '')], initial_forms=1) self.assertFalse(formset.is_valid()) self.assertEqual(formset.errors, [{'votes': ['This field is required.'], 'choice': ['This field is required.']}, {}]) @@ -1027,6 +995,40 @@ class FormsFormsetTestCase(TestCase): self.assertTrue(formset.is_valid()) + def test_formset_total_error_count(self): + """A valid formset should have 0 total errors.""" + data = [ # formset_data, expected error count + ([('Calexico', '100')], 0), + ([('Calexico', '')], 1), + ([('', 'invalid')], 2), + ([('Calexico', '100'), ('Calexico', '')], 1), + ([('Calexico', ''), ('Calexico', '')], 2), + ] + + for formset_data, expected_error_count in data: + formset = self.make_choiceformset(formset_data) + self.assertEqual(formset.total_error_count(), expected_error_count) + + def test_formset_total_error_count_with_non_form_errors(self): + data = { + 'choices-TOTAL_FORMS': '2', # the number of forms rendered + 'choices-INITIAL_FORMS': '0', # the number of forms with initial data + 'choices-MAX_NUM_FORMS': '2', # max number of forms - should be ignored + 'choices-0-choice': 'Zero', + 'choices-0-votes': '0', + 'choices-1-choice': 'One', + 'choices-1-votes': '1', + } + + ChoiceFormSet = formset_factory(Choice, extra=1, max_num=1, validate_max=True) + formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + self.assertEqual(formset.total_error_count(), 1) + + data['choices-1-votes'] = '' + formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + self.assertEqual(formset.total_error_count(), 2) + + data = { 'choices-TOTAL_FORMS': '1', # the number of forms rendered 'choices-INITIAL_FORMS': '0', # the number of forms with initial data @@ -1087,7 +1089,7 @@ class TestIsBoundBehavior(TestCase): self.assertEqual([{}], formset.cleaned_data) - def test_form_errors_are_cought_by_formset(self): + def test_form_errors_are_caught_by_formset(self): data = { 'form-TOTAL_FORMS': '2', 'form-INITIAL_FORMS': '0', diff --git a/tests/forms_tests/tests/test_regressions.py b/tests/forms_tests/tests/test_regressions.py index ea138d32d5..d230d68f75 100644 --- a/tests/forms_tests/tests/test_regressions.py +++ b/tests/forms_tests/tests/test_regressions.py @@ -45,8 +45,8 @@ class FormsRegressionsTestCase(TransRealMixin, TestCase): field_2 = CharField(max_length=10, label=ugettext_lazy('field_2'), widget=TextInput(attrs={'id': 'field_2_id'})) f = SomeForm() - self.assertHTMLEqual(f['field_1'].label_tag(), '<label for="id_field_1">field_1</label>') - self.assertHTMLEqual(f['field_2'].label_tag(), '<label for="field_2_id">field_2</label>') + self.assertHTMLEqual(f['field_1'].label_tag(), '<label for="id_field_1">field_1:</label>') + self.assertHTMLEqual(f['field_2'].label_tag(), '<label for="field_2_id">field_2:</label>') # Unicode decoding problems... GENDERS = (('\xc5', 'En tied\xe4'), ('\xf8', 'Mies'), ('\xdf', 'Nainen')) diff --git a/tests/forms_tests/tests/test_widgets.py b/tests/forms_tests/tests/test_widgets.py index 4664553aa7..4c566dc8e4 100644 --- a/tests/forms_tests/tests/test_widgets.py +++ b/tests/forms_tests/tests/test_widgets.py @@ -1028,6 +1028,8 @@ class WidgetTests(TestCase): class LiveWidgetTests(AdminSeleniumWebDriverTestCase): + + available_apps = ['forms_tests'] + AdminSeleniumWebDriverTestCase.available_apps urls = 'forms_tests.urls' def test_textarea_trailing_newlines(self): diff --git a/tests/forms_tests/tests/tests.py b/tests/forms_tests/tests/tests.py index 2616ddaf7d..99a67c320c 100644 --- a/tests/forms_tests/tests/tests.py +++ b/tests/forms_tests/tests/tests.py @@ -208,7 +208,8 @@ class RelatedModelFormTests(TestCase): ref = models.ForeignKey("B") class Meta: - model=A + model = A + fields = '__all__' self.assertRaises(ValueError, ModelFormMetaclass, str('Form'), (ModelForm,), {'Meta': Meta}) @@ -226,7 +227,8 @@ class RelatedModelFormTests(TestCase): pass class Meta: - model=A + model = A + fields = '__all__' self.assertTrue(issubclass(ModelFormMetaclass(str('Form'), (ModelForm,), {'Meta': Meta}), ModelForm)) diff --git a/tests/generic_views/test_base.py b/tests/generic_views/test_base.py index ffd9b1b480..f95732f8b5 100644 --- a/tests/generic_views/test_base.py +++ b/tests/generic_views/test_base.py @@ -317,7 +317,9 @@ class TemplateViewTest(TestCase): self.assertEqual(response['Content-Type'], 'text/plain') -class RedirectViewTest(unittest.TestCase): +class RedirectViewTest(TestCase): + urls = 'generic_views.urls' + rf = RequestFactory() def test_no_url(self): @@ -360,6 +362,22 @@ class RedirectViewTest(unittest.TestCase): self.assertEqual(response.status_code, 301) self.assertEqual(response.url, '/bar/42/') + def test_named_url_pattern(self): + "Named pattern parameter should reverse to the matching pattern" + response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), pk=1) + self.assertEqual(response.status_code, 301) + self.assertEqual(response['Location'], '/detail/artist/1/') + + def test_named_url_pattern_using_args(self): + response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), 1) + self.assertEqual(response.status_code, 301) + self.assertEqual(response['Location'], '/detail/artist/1/') + + def test_wrong_named_url_pattern(self): + "A wrong pattern name returns 410 GONE" + response = RedirectView.as_view(pattern_name='wrong.pattern_name')(self.rf.get('/foo/')) + self.assertEqual(response.status_code, 410) + def test_redirect_POST(self): "Default is a permanent redirect" response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/')) diff --git a/tests/get_or_create/tests.py b/tests/get_or_create/tests.py index 36c248b169..847a6dec01 100644 --- a/tests/get_or_create/tests.py +++ b/tests/get_or_create/tests.py @@ -94,6 +94,8 @@ class GetOrCreateTests(TestCase): class GetOrCreateTransactionTests(TransactionTestCase): + available_apps = ['get_or_create'] + def test_get_or_create_integrityerror(self): # Regression test for #15117. Requires a TransactionTestCase on # databases that delay integrity checks until the end of transactions, diff --git a/tests/handlers/tests.py b/tests/handlers/tests.py index 397b63647a..2f9f304b81 100644 --- a/tests/handlers/tests.py +++ b/tests/handlers/tests.py @@ -37,6 +37,8 @@ class HandlerTests(TestCase): class TransactionsPerRequestTests(TransactionTestCase): + + available_apps = [] urls = 'handlers.urls' def test_no_transaction(self): diff --git a/tests/lookup/models.py b/tests/lookup/models.py index f60c4962fd..f388ddf403 100644 --- a/tests/lookup/models.py +++ b/tests/lookup/models.py @@ -10,16 +10,29 @@ from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible -from shared_models.models import Author, Book +class Author(models.Model): + name = models.CharField(max_length=100) + class Meta: + ordering = ('name', ) + +@python_2_unicode_compatible +class Article(models.Model): + headline = models.CharField(max_length=100) + pub_date = models.DateTimeField() + author = models.ForeignKey(Author, blank=True, null=True) + class Meta: + ordering = ('-pub_date', 'headline') + + def __str__(self): + return self.headline class Tag(models.Model): - articles = models.ManyToManyField(Book) + articles = models.ManyToManyField(Article) name = models.CharField(max_length=100) class Meta: ordering = ('name', ) - @python_2_unicode_compatible class Season(models.Model): year = models.PositiveSmallIntegerField() @@ -28,7 +41,6 @@ class Season(models.Model): def __str__(self): return six.text_type(self.year) - @python_2_unicode_compatible class Game(models.Model): season = models.ForeignKey(Season, related_name='games') @@ -38,7 +50,6 @@ class Game(models.Model): def __str__(self): return "%s at %s" % (self.away, self.home) - @python_2_unicode_compatible class Player(models.Model): name = models.CharField(max_length=100) diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index 6a6729ef22..de7105f92d 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -5,11 +5,8 @@ from operator import attrgetter from django.core.exceptions import FieldError from django.test import TestCase, skipUnlessDBFeature -from django.utils import six -from shared_models.models import Author, Book - -from .models import Tag, Game, Season, Player +from .models import Author, Article, Tag, Game, Season, Player class LookupTests(TestCase): @@ -20,165 +17,165 @@ class LookupTests(TestCase): self.au1.save() self.au2 = Author(name='Author 2') self.au2.save() - # Create a couple of Books. - self.b1 = Book(title='Book 1', pubdate=datetime(2005, 7, 26), author=self.au1) - self.b1.save() - self.b2 = Book(title='Book 2', pubdate=datetime(2005, 7, 27), author=self.au1) - self.b2.save() - self.b3 = Book(title='Book 3', pubdate=datetime(2005, 7, 27), author=self.au1) - self.b3.save() - self.b4 = Book(title='Book 4', pubdate=datetime(2005, 7, 28), author=self.au1) - self.b4.save() - self.b5 = Book(title='Book 5', pubdate=datetime(2005, 8, 1, 9, 0), author=self.au2) - self.b5.save() - self.b6 = Book(title='Book 6', pubdate=datetime(2005, 8, 1, 8, 0), author=self.au2) - self.b6.save() - self.b7 = Book(title='Book 7', pubdate=datetime(2005, 7, 27), author=self.au2) - self.b7.save() + # Create a couple of Articles. + self.a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26), author=self.au1) + self.a1.save() + self.a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27), author=self.au1) + self.a2.save() + self.a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27), author=self.au1) + self.a3.save() + self.a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28), author=self.au1) + self.a4.save() + self.a5 = Article(headline='Article 5', pub_date=datetime(2005, 8, 1, 9, 0), author=self.au2) + self.a5.save() + self.a6 = Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0), author=self.au2) + self.a6.save() + self.a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27), author=self.au2) + self.a7.save() # Create a few Tags. self.t1 = Tag(name='Tag 1') self.t1.save() - self.t1.articles.add(self.b1, self.b2, self.b3) + self.t1.articles.add(self.a1, self.a2, self.a3) self.t2 = Tag(name='Tag 2') self.t2.save() - self.t2.articles.add(self.b3, self.b4, self.b5) + self.t2.articles.add(self.a3, self.a4, self.a5) self.t3 = Tag(name='Tag 3') self.t3.save() - self.t3.articles.add(self.b5, self.b6, self.b7) + self.t3.articles.add(self.a5, self.a6, self.a7) def test_exists(self): # We can use .exists() to check that there are some - self.assertTrue(Book.objects.exists()) - for a in Book.objects.all(): + self.assertTrue(Article.objects.exists()) + for a in Article.objects.all(): a.delete() # There should be none now! - self.assertFalse(Book.objects.exists()) + self.assertFalse(Article.objects.exists()) def test_lookup_int_as_str(self): # Integer value can be queried using string - self.assertQuerysetEqual(Book.objects.filter(id__iexact=str(self.b1.id)), - ['<Book: Book 1>']) + self.assertQuerysetEqual(Article.objects.filter(id__iexact=str(self.a1.id)), + ['<Article: Article 1>']) @skipUnlessDBFeature('supports_date_lookup_using_string') def test_lookup_date_as_str(self): # A date lookup can be performed using a string search - self.assertQuerysetEqual(Book.objects.filter(pubdate__startswith='2005'), + self.assertQuerysetEqual(Article.objects.filter(pub_date__startswith='2005'), [ - '<Book: Book 5>', - '<Book: Book 6>', - '<Book: Book 4>', - '<Book: Book 2>', - '<Book: Book 3>', - '<Book: Book 7>', - '<Book: Book 1>', + '<Article: Article 5>', + '<Article: Article 6>', + '<Article: Article 4>', + '<Article: Article 2>', + '<Article: Article 3>', + '<Article: Article 7>', + '<Article: Article 1>', ]) def test_iterator(self): # Each QuerySet gets iterator(), which is a generator that "lazily" # returns results using database-level iteration. - self.assertQuerysetEqual(Book.objects.iterator(), + self.assertQuerysetEqual(Article.objects.iterator(), [ - 'Book 5', - 'Book 6', - 'Book 4', - 'Book 2', - 'Book 3', - 'Book 7', - 'Book 1', + 'Article 5', + 'Article 6', + 'Article 4', + 'Article 2', + 'Article 3', + 'Article 7', + 'Article 1', ], - transform=attrgetter('title')) + transform=attrgetter('headline')) # iterator() can be used on any QuerySet. self.assertQuerysetEqual( - Book.objects.filter(title__endswith='4').iterator(), - ['Book 4'], - transform=attrgetter('title')) + Article.objects.filter(headline__endswith='4').iterator(), + ['Article 4'], + transform=attrgetter('headline')) def test_count(self): # count() returns the number of objects matching search criteria. - self.assertEqual(Book.objects.count(), 7) - self.assertEqual(Book.objects.filter(pubdate__exact=datetime(2005, 7, 27)).count(), 3) - self.assertEqual(Book.objects.filter(title__startswith='Blah blah').count(), 0) + self.assertEqual(Article.objects.count(), 7) + self.assertEqual(Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).count(), 3) + self.assertEqual(Article.objects.filter(headline__startswith='Blah blah').count(), 0) # count() should respect sliced query sets. - articles = Book.objects.all() + articles = Article.objects.all() self.assertEqual(articles.count(), 7) self.assertEqual(articles[:4].count(), 4) self.assertEqual(articles[1:100].count(), 6) self.assertEqual(articles[10:100].count(), 0) # Date and date/time lookups can also be done with strings. - self.assertEqual(Book.objects.filter(pubdate__exact='2005-07-27').count(), 3) + self.assertEqual(Article.objects.filter(pub_date__exact='2005-07-27 00:00:00').count(), 3) def test_in_bulk(self): # in_bulk() takes a list of IDs and returns a dictionary mapping IDs to objects. - arts = Book.objects.in_bulk([self.b1.id, self.b2.id]) - self.assertEqual(arts[self.b1.id], self.b1) - self.assertEqual(arts[self.b2.id], self.b2) - self.assertEqual(Book.objects.in_bulk([self.b3.id]), {self.b3.id: self.b3}) - self.assertEqual(Book.objects.in_bulk(set([self.b3.id])), {self.b3.id: self.b3}) - self.assertEqual(Book.objects.in_bulk(frozenset([self.b3.id])), {self.b3.id: self.b3}) - self.assertEqual(Book.objects.in_bulk((self.b3.id,)), {self.b3.id: self.b3}) - self.assertEqual(Book.objects.in_bulk([1000]), {}) - self.assertEqual(Book.objects.in_bulk([]), {}) - self.assertEqual(Book.objects.in_bulk(iter([self.b1.id])), {self.b1.id: self.b1}) - self.assertEqual(Book.objects.in_bulk(iter([])), {}) - self.assertRaises(TypeError, Book.objects.in_bulk) - self.assertRaises(TypeError, Book.objects.in_bulk, name__startswith='Blah') + arts = Article.objects.in_bulk([self.a1.id, self.a2.id]) + self.assertEqual(arts[self.a1.id], self.a1) + self.assertEqual(arts[self.a2.id], self.a2) + self.assertEqual(Article.objects.in_bulk([self.a3.id]), {self.a3.id: self.a3}) + self.assertEqual(Article.objects.in_bulk(set([self.a3.id])), {self.a3.id: self.a3}) + self.assertEqual(Article.objects.in_bulk(frozenset([self.a3.id])), {self.a3.id: self.a3}) + self.assertEqual(Article.objects.in_bulk((self.a3.id,)), {self.a3.id: self.a3}) + self.assertEqual(Article.objects.in_bulk([1000]), {}) + self.assertEqual(Article.objects.in_bulk([]), {}) + self.assertEqual(Article.objects.in_bulk(iter([self.a1.id])), {self.a1.id: self.a1}) + self.assertEqual(Article.objects.in_bulk(iter([])), {}) + self.assertRaises(TypeError, Article.objects.in_bulk) + self.assertRaises(TypeError, Article.objects.in_bulk, headline__startswith='Blah') def test_values(self): # values() returns a list of dictionaries instead of object instances -- # and you can specify which fields you want to retrieve. identity = lambda x:x - self.assertQuerysetEqual(Book.objects.values('title'), + self.assertQuerysetEqual(Article.objects.values('headline'), [ - {'title': 'Book 5'}, - {'title': 'Book 6'}, - {'title': 'Book 4'}, - {'title': 'Book 2'}, - {'title': 'Book 3'}, - {'title': 'Book 7'}, - {'title': 'Book 1'}, + {'headline': 'Article 5'}, + {'headline': 'Article 6'}, + {'headline': 'Article 4'}, + {'headline': 'Article 2'}, + {'headline': 'Article 3'}, + {'headline': 'Article 7'}, + {'headline': 'Article 1'}, ], transform=identity) self.assertQuerysetEqual( - Book.objects.filter(pubdate__exact=datetime(2005, 7, 27)).values('id'), - [{'id': self.b2.id}, {'id': self.b3.id}, {'id': self.b7.id}], + Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).values('id'), + [{'id': self.a2.id}, {'id': self.a3.id}, {'id': self.a7.id}], transform=identity) - self.assertQuerysetEqual(Book.objects.values('id', 'title'), + self.assertQuerysetEqual(Article.objects.values('id', 'headline'), [ - {'id': self.b5.id, 'title': 'Book 5'}, - {'id': self.b6.id, 'title': 'Book 6'}, - {'id': self.b4.id, 'title': 'Book 4'}, - {'id': self.b2.id, 'title': 'Book 2'}, - {'id': self.b3.id, 'title': 'Book 3'}, - {'id': self.b7.id, 'title': 'Book 7'}, - {'id': self.b1.id, 'title': 'Book 1'}, + {'id': self.a5.id, 'headline': 'Article 5'}, + {'id': self.a6.id, 'headline': 'Article 6'}, + {'id': self.a4.id, 'headline': 'Article 4'}, + {'id': self.a2.id, 'headline': 'Article 2'}, + {'id': self.a3.id, 'headline': 'Article 3'}, + {'id': self.a7.id, 'headline': 'Article 7'}, + {'id': self.a1.id, 'headline': 'Article 1'}, ], transform=identity) # You can use values() with iterator() for memory savings, # because iterator() uses database-level iteration. - self.assertQuerysetEqual(Book.objects.values('id', 'title').iterator(), + self.assertQuerysetEqual(Article.objects.values('id', 'headline').iterator(), [ - {'title': 'Book 5', 'id': self.b5.id}, - {'title': 'Book 6', 'id': self.b6.id}, - {'title': 'Book 4', 'id': self.b4.id}, - {'title': 'Book 2', 'id': self.b2.id}, - {'title': 'Book 3', 'id': self.b3.id}, - {'title': 'Book 7', 'id': self.b7.id}, - {'title': 'Book 1', 'id': self.b1.id}, + {'headline': 'Article 5', 'id': self.a5.id}, + {'headline': 'Article 6', 'id': self.a6.id}, + {'headline': 'Article 4', 'id': self.a4.id}, + {'headline': 'Article 2', 'id': self.a2.id}, + {'headline': 'Article 3', 'id': self.a3.id}, + {'headline': 'Article 7', 'id': self.a7.id}, + {'headline': 'Article 1', 'id': self.a1.id}, ], transform=identity) # The values() method works with "extra" fields specified in extra(select). self.assertQuerysetEqual( - Book.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'), + Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'), [ - {'id': self.b5.id, 'id_plus_one': self.b5.id + 1}, - {'id': self.b6.id, 'id_plus_one': self.b6.id + 1}, - {'id': self.b4.id, 'id_plus_one': self.b4.id + 1}, - {'id': self.b2.id, 'id_plus_one': self.b2.id + 1}, - {'id': self.b3.id, 'id_plus_one': self.b3.id + 1}, - {'id': self.b7.id, 'id_plus_one': self.b7.id + 1}, - {'id': self.b1.id, 'id_plus_one': self.b1.id + 1}, + {'id': self.a5.id, 'id_plus_one': self.a5.id + 1}, + {'id': self.a6.id, 'id_plus_one': self.a6.id + 1}, + {'id': self.a4.id, 'id_plus_one': self.a4.id + 1}, + {'id': self.a2.id, 'id_plus_one': self.a2.id + 1}, + {'id': self.a3.id, 'id_plus_one': self.a3.id + 1}, + {'id': self.a7.id, 'id_plus_one': self.a7.id + 1}, + {'id': self.a1.id, 'id_plus_one': self.a1.id + 1}, ], transform=identity) data = { @@ -192,67 +189,66 @@ class LookupTests(TestCase): 'id_plus_eight': 'id+8', } self.assertQuerysetEqual( - Book.objects.filter(id=self.b1.id).extra(select=data).values(*data.keys()), + Article.objects.filter(id=self.a1.id).extra(select=data).values(*data.keys()), [{ - 'id_plus_one': self.b1.id + 1, - 'id_plus_two': self.b1.id + 2, - 'id_plus_three': self.b1.id + 3, - 'id_plus_four': self.b1.id + 4, - 'id_plus_five': self.b1.id + 5, - 'id_plus_six': self.b1.id + 6, - 'id_plus_seven': self.b1.id + 7, - 'id_plus_eight': self.b1.id + 8, + 'id_plus_one': self.a1.id + 1, + 'id_plus_two': self.a1.id + 2, + 'id_plus_three': self.a1.id + 3, + 'id_plus_four': self.a1.id + 4, + 'id_plus_five': self.a1.id + 5, + 'id_plus_six': self.a1.id + 6, + 'id_plus_seven': self.a1.id + 7, + 'id_plus_eight': self.a1.id + 8, }], transform=identity) # You can specify fields from forward and reverse relations, just like filter(). self.assertQuerysetEqual( - Book.objects.values('title', 'author__name'), + Article.objects.values('headline', 'author__name'), [ - {'title': self.b5.title, 'author__name': self.au2.name}, - {'title': self.b6.title, 'author__name': self.au2.name}, - {'title': self.b4.title, 'author__name': self.au1.name}, - {'title': self.b2.title, 'author__name': self.au1.name}, - {'title': self.b3.title, 'author__name': self.au1.name}, - {'title': self.b7.title, 'author__name': self.au2.name}, - {'title': self.b1.title, 'author__name': self.au1.name}, + {'headline': self.a5.headline, 'author__name': self.au2.name}, + {'headline': self.a6.headline, 'author__name': self.au2.name}, + {'headline': self.a4.headline, 'author__name': self.au1.name}, + {'headline': self.a2.headline, 'author__name': self.au1.name}, + {'headline': self.a3.headline, 'author__name': self.au1.name}, + {'headline': self.a7.headline, 'author__name': self.au2.name}, + {'headline': self.a1.headline, 'author__name': self.au1.name}, ], transform=identity) self.assertQuerysetEqual( - Author.objects.values('name', 'book__title').order_by('name', 'book__title'), + Author.objects.values('name', 'article__headline').order_by('name', 'article__headline'), [ - {'name': self.au1.name, 'book__title': self.b1.title}, - {'name': self.au1.name, 'book__title': self.b2.title}, - {'name': self.au1.name, 'book__title': self.b3.title}, - {'name': self.au1.name, 'book__title': self.b4.title}, - {'name': self.au2.name, 'book__title': self.b5.title}, - {'name': self.au2.name, 'book__title': self.b6.title}, - {'name': self.au2.name, 'book__title': self.b7.title}, + {'name': self.au1.name, 'article__headline': self.a1.headline}, + {'name': self.au1.name, 'article__headline': self.a2.headline}, + {'name': self.au1.name, 'article__headline': self.a3.headline}, + {'name': self.au1.name, 'article__headline': self.a4.headline}, + {'name': self.au2.name, 'article__headline': self.a5.headline}, + {'name': self.au2.name, 'article__headline': self.a6.headline}, + {'name': self.au2.name, 'article__headline': self.a7.headline}, ], transform=identity) self.assertQuerysetEqual( - Author.objects.values('name', 'book__title', 'book__tag__name').order_by('name', 'book__title', 'book__tag__name'), + Author.objects.values('name', 'article__headline', 'article__tag__name').order_by('name', 'article__headline', 'article__tag__name'), [ - {'name': self.au1.name, 'book__title': self.b1.title, 'book__tag__name': self.t1.name}, - {'name': self.au1.name, 'book__title': self.b2.title, 'book__tag__name': self.t1.name}, - {'name': self.au1.name, 'book__title': self.b3.title, 'book__tag__name': self.t1.name}, - {'name': self.au1.name, 'book__title': self.b3.title, 'book__tag__name': self.t2.name}, - {'name': self.au1.name, 'book__title': self.b4.title, 'book__tag__name': self.t2.name}, - {'name': self.au2.name, 'book__title': self.b5.title, 'book__tag__name': self.t2.name}, - {'name': self.au2.name, 'book__title': self.b5.title, 'book__tag__name': self.t3.name}, - {'name': self.au2.name, 'book__title': self.b6.title, 'book__tag__name': self.t3.name}, - {'name': self.au2.name, 'book__title': self.b7.title, 'book__tag__name': self.t3.name}, + {'name': self.au1.name, 'article__headline': self.a1.headline, 'article__tag__name': self.t1.name}, + {'name': self.au1.name, 'article__headline': self.a2.headline, 'article__tag__name': self.t1.name}, + {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t1.name}, + {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t2.name}, + {'name': self.au1.name, 'article__headline': self.a4.headline, 'article__tag__name': self.t2.name}, + {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t2.name}, + {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t3.name}, + {'name': self.au2.name, 'article__headline': self.a6.headline, 'article__tag__name': self.t3.name}, + {'name': self.au2.name, 'article__headline': self.a7.headline, 'article__tag__name': self.t3.name}, ], transform=identity) # However, an exception FieldDoesNotExist will be thrown if you specify # a non-existent field name in values() (a field that is neither in the # model nor in extra(select)). self.assertRaises(FieldError, - Book.objects.extra(select={'id_plus_one': 'id + 1'}).values, + Article.objects.extra(select={'id_plus_one': 'id + 1'}).values, 'id', 'id_plus_two') # If you don't specify field names to values(), all are returned. - self.assertQuerysetEqual(Book.objects.filter(id=self.b5.id).values(), + self.assertQuerysetEqual(Article.objects.filter(id=self.a5.id).values(), [{ - 'id': self.b5.id, + 'id': self.a5.id, 'author_id': self.au2.id, - 'title': 'Book 5', - 'pages': 0, - 'pubdate': datetime(2005, 8, 1, 9, 0) + 'headline': 'Article 5', + 'pub_date': datetime(2005, 8, 1, 9, 0) }], transform=identity) def test_values_list(self): @@ -261,353 +257,358 @@ class LookupTests(TestCase): # Within each tuple, the order of the elements is the same as the order # of fields in the values_list() call. identity = lambda x:x - self.assertQuerysetEqual(Book.objects.values_list('title'), + self.assertQuerysetEqual(Article.objects.values_list('headline'), [ - ('Book 5',), - ('Book 6',), - ('Book 4',), - ('Book 2',), - ('Book 3',), - ('Book 7',), - ('Book 1',), + ('Article 5',), + ('Article 6',), + ('Article 4',), + ('Article 2',), + ('Article 3',), + ('Article 7',), + ('Article 1',), ], transform=identity) - self.assertQuerysetEqual(Book.objects.values_list('id').order_by('id'), - [(self.b1.id,), (self.b2.id,), (self.b3.id,), (self.b4.id,), (self.b5.id,), (self.b6.id,), (self.b7.id,)], + self.assertQuerysetEqual(Article.objects.values_list('id').order_by('id'), + [(self.a1.id,), (self.a2.id,), (self.a3.id,), (self.a4.id,), (self.a5.id,), (self.a6.id,), (self.a7.id,)], transform=identity) self.assertQuerysetEqual( - Book.objects.values_list('id', flat=True).order_by('id'), - [self.b1.id, self.b2.id, self.b3.id, self.b4.id, self.b5.id, self.b6.id, self.b7.id], + Article.objects.values_list('id', flat=True).order_by('id'), + [self.a1.id, self.a2.id, self.a3.id, self.a4.id, self.a5.id, self.a6.id, self.a7.id], transform=identity) self.assertQuerysetEqual( - Book.objects.extra(select={'id_plus_one': 'id+1'}) + Article.objects.extra(select={'id_plus_one': 'id+1'}) .order_by('id').values_list('id'), - [(self.b1.id,), (self.b2.id,), (self.b3.id,), (self.b4.id,), (self.b5.id,), (self.b6.id,), (self.b7.id,)], + [(self.a1.id,), (self.a2.id,), (self.a3.id,), (self.a4.id,), (self.a5.id,), (self.a6.id,), (self.a7.id,)], transform=identity) self.assertQuerysetEqual( - Book.objects.extra(select={'id_plus_one': 'id+1'}) + Article.objects.extra(select={'id_plus_one': 'id+1'}) .order_by('id').values_list('id_plus_one', 'id'), [ - (self.b1.id+1, self.b1.id), - (self.b2.id+1, self.b2.id), - (self.b3.id+1, self.b3.id), - (self.b4.id+1, self.b4.id), - (self.b5.id+1, self.b5.id), - (self.b6.id+1, self.b6.id), - (self.b7.id+1, self.b7.id) + (self.a1.id+1, self.a1.id), + (self.a2.id+1, self.a2.id), + (self.a3.id+1, self.a3.id), + (self.a4.id+1, self.a4.id), + (self.a5.id+1, self.a5.id), + (self.a6.id+1, self.a6.id), + (self.a7.id+1, self.a7.id) ], transform=identity) self.assertQuerysetEqual( - Book.objects.extra(select={'id_plus_one': 'id+1'}) + Article.objects.extra(select={'id_plus_one': 'id+1'}) .order_by('id').values_list('id', 'id_plus_one'), [ - (self.b1.id, self.b1.id+1), - (self.b2.id, self.b2.id+1), - (self.b3.id, self.b3.id+1), - (self.b4.id, self.b4.id+1), - (self.b5.id, self.b5.id+1), - (self.b6.id, self.b6.id+1), - (self.b7.id, self.b7.id+1) + (self.a1.id, self.a1.id+1), + (self.a2.id, self.a2.id+1), + (self.a3.id, self.a3.id+1), + (self.a4.id, self.a4.id+1), + (self.a5.id, self.a5.id+1), + (self.a6.id, self.a6.id+1), + (self.a7.id, self.a7.id+1) ], transform=identity) self.assertQuerysetEqual( - Author.objects.values_list('name', 'book__title', 'book__tag__name').order_by('name', 'book__title', 'book__tag__name'), + Author.objects.values_list('name', 'article__headline', 'article__tag__name').order_by('name', 'article__headline', 'article__tag__name'), [ - (self.au1.name, self.b1.title, self.t1.name), - (self.au1.name, self.b2.title, self.t1.name), - (self.au1.name, self.b3.title, self.t1.name), - (self.au1.name, self.b3.title, self.t2.name), - (self.au1.name, self.b4.title, self.t2.name), - (self.au2.name, self.b5.title, self.t2.name), - (self.au2.name, self.b5.title, self.t3.name), - (self.au2.name, self.b6.title, self.t3.name), - (self.au2.name, self.b7.title, self.t3.name), + (self.au1.name, self.a1.headline, self.t1.name), + (self.au1.name, self.a2.headline, self.t1.name), + (self.au1.name, self.a3.headline, self.t1.name), + (self.au1.name, self.a3.headline, self.t2.name), + (self.au1.name, self.a4.headline, self.t2.name), + (self.au2.name, self.a5.headline, self.t2.name), + (self.au2.name, self.a5.headline, self.t3.name), + (self.au2.name, self.a6.headline, self.t3.name), + (self.au2.name, self.a7.headline, self.t3.name), ], transform=identity) - self.assertRaises(TypeError, Book.objects.values_list, 'id', 'title', flat=True) + self.assertRaises(TypeError, Article.objects.values_list, 'id', 'headline', flat=True) def test_get_next_previous_by(self): # Every DateField and DateTimeField creates get_next_by_FOO() and # get_previous_by_FOO() methods. In the case of identical date values, # these methods will use the ID as a fallback check. This guarantees # that no records are skipped or duplicated. - self.assertEqual(repr(self.b1.get_next_by_pubdate()), - '<Book: Book 2>') - self.assertEqual(repr(self.b2.get_next_by_pubdate()), - '<Book: Book 3>') - self.assertEqual(repr(self.b2.get_next_by_pubdate(title__endswith='6')), - '<Book: Book 6>') - self.assertEqual(repr(self.b3.get_next_by_pubdate()), - '<Book: Book 7>') - self.assertEqual(repr(self.b4.get_next_by_pubdate()), - '<Book: Book 6>') - self.assertRaises(Book.DoesNotExist, self.b5.get_next_by_pubdate) - self.assertEqual(repr(self.b6.get_next_by_pubdate()), - '<Book: Book 5>') - self.assertEqual(repr(self.b7.get_next_by_pubdate()), - '<Book: Book 4>') + self.assertEqual(repr(self.a1.get_next_by_pub_date()), + '<Article: Article 2>') + self.assertEqual(repr(self.a2.get_next_by_pub_date()), + '<Article: Article 3>') + self.assertEqual(repr(self.a2.get_next_by_pub_date(headline__endswith='6')), + '<Article: Article 6>') + self.assertEqual(repr(self.a3.get_next_by_pub_date()), + '<Article: Article 7>') + self.assertEqual(repr(self.a4.get_next_by_pub_date()), + '<Article: Article 6>') + self.assertRaises(Article.DoesNotExist, self.a5.get_next_by_pub_date) + self.assertEqual(repr(self.a6.get_next_by_pub_date()), + '<Article: Article 5>') + self.assertEqual(repr(self.a7.get_next_by_pub_date()), + '<Article: Article 4>') - self.assertEqual(repr(self.b7.get_previous_by_pubdate()), - '<Book: Book 3>') - self.assertEqual(repr(self.b6.get_previous_by_pubdate()), - '<Book: Book 4>') - self.assertEqual(repr(self.b5.get_previous_by_pubdate()), - '<Book: Book 6>') - self.assertEqual(repr(self.b4.get_previous_by_pubdate()), - '<Book: Book 7>') - self.assertEqual(repr(self.b3.get_previous_by_pubdate()), - '<Book: Book 2>') - self.assertEqual(repr(self.b2.get_previous_by_pubdate()), - '<Book: Book 1>') + self.assertEqual(repr(self.a7.get_previous_by_pub_date()), + '<Article: Article 3>') + self.assertEqual(repr(self.a6.get_previous_by_pub_date()), + '<Article: Article 4>') + self.assertEqual(repr(self.a5.get_previous_by_pub_date()), + '<Article: Article 6>') + self.assertEqual(repr(self.a4.get_previous_by_pub_date()), + '<Article: Article 7>') + self.assertEqual(repr(self.a3.get_previous_by_pub_date()), + '<Article: Article 2>') + self.assertEqual(repr(self.a2.get_previous_by_pub_date()), + '<Article: Article 1>') def test_escaping(self): # Underscores, percent signs and backslashes have special meaning in the # underlying SQL code, but Django handles the quoting of them automatically. - b8 = Book(title='Book_ with underscore', pubdate=datetime(2005, 11, 20)) - b8.save() - self.assertQuerysetEqual(Book.objects.filter(title__startswith='Book'), + a8 = Article(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20)) + a8.save() + self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article'), [ - '<Book: Book_ with underscore>', - '<Book: Book 5>', - '<Book: Book 6>', - '<Book: Book 4>', - '<Book: Book 2>', - '<Book: Book 3>', - '<Book: Book 7>', - '<Book: Book 1>', + '<Article: Article_ with underscore>', + '<Article: Article 5>', + '<Article: Article 6>', + '<Article: Article 4>', + '<Article: Article 2>', + '<Article: Article 3>', + '<Article: Article 7>', + '<Article: Article 1>', ]) - self.assertQuerysetEqual(Book.objects.filter(title__startswith='Book_'), - ['<Book: Book_ with underscore>']) - b9 = Book(title='Book% with percent sign', pubdate=datetime(2005, 11, 21)) - b9.save() - self.assertQuerysetEqual(Book.objects.filter(title__startswith='Book'), + self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article_'), + ['<Article: Article_ with underscore>']) + a9 = Article(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21)) + a9.save() + self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article'), [ - '<Book: Book% with percent sign>', - '<Book: Book_ with underscore>', - '<Book: Book 5>', - '<Book: Book 6>', - '<Book: Book 4>', - '<Book: Book 2>', - '<Book: Book 3>', - '<Book: Book 7>', - '<Book: Book 1>', + '<Article: Article% with percent sign>', + '<Article: Article_ with underscore>', + '<Article: Article 5>', + '<Article: Article 6>', + '<Article: Article 4>', + '<Article: Article 2>', + '<Article: Article 3>', + '<Article: Article 7>', + '<Article: Article 1>', ]) - self.assertQuerysetEqual(Book.objects.filter(title__startswith='Book%'), - ['<Book: Book% with percent sign>']) - b10 = Book(title='Book with \\ backslash', pubdate=datetime(2005, 11, 22)) - b10.save() - self.assertQuerysetEqual(Book.objects.filter(title__contains='\\'), - ['<Book: Book with \ backslash>']) + self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article%'), + ['<Article: Article% with percent sign>']) + a10 = Article(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22)) + a10.save() + self.assertQuerysetEqual(Article.objects.filter(headline__contains='\\'), + ['<Article: Article with \ backslash>']) def test_exclude(self): - b8 = Book.objects.create(title='Book_ with underscore', pubdate=datetime(2005, 11, 20)) - b9 = Book.objects.create(title='Book% with percent sign', pubdate=datetime(2005, 11, 21)) - b10 = Book.objects.create(title='Book with \\ backslash', pubdate=datetime(2005, 11, 22)) + a8 = Article.objects.create(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20)) + a9 = Article.objects.create(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21)) + a10 = Article.objects.create(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22)) # exclude() is the opposite of filter() when doing lookups: self.assertQuerysetEqual( - Book.objects.filter(title__contains='Book').exclude(title__contains='with'), + Article.objects.filter(headline__contains='Article').exclude(headline__contains='with'), [ - '<Book: Book 5>', - '<Book: Book 6>', - '<Book: Book 4>', - '<Book: Book 2>', - '<Book: Book 3>', - '<Book: Book 7>', - '<Book: Book 1>', + '<Article: Article 5>', + '<Article: Article 6>', + '<Article: Article 4>', + '<Article: Article 2>', + '<Article: Article 3>', + '<Article: Article 7>', + '<Article: Article 1>', ]) - self.assertQuerysetEqual(Book.objects.exclude(title__startswith="Book_"), + self.assertQuerysetEqual(Article.objects.exclude(headline__startswith="Article_"), [ - '<Book: Book with \\ backslash>', - '<Book: Book% with percent sign>', - '<Book: Book 5>', - '<Book: Book 6>', - '<Book: Book 4>', - '<Book: Book 2>', - '<Book: Book 3>', - '<Book: Book 7>', - '<Book: Book 1>', + '<Article: Article with \\ backslash>', + '<Article: Article% with percent sign>', + '<Article: Article 5>', + '<Article: Article 6>', + '<Article: Article 4>', + '<Article: Article 2>', + '<Article: Article 3>', + '<Article: Article 7>', + '<Article: Article 1>', ]) - self.assertQuerysetEqual(Book.objects.exclude(title="Book 7"), + self.assertQuerysetEqual(Article.objects.exclude(headline="Article 7"), [ - '<Book: Book with \\ backslash>', - '<Book: Book% with percent sign>', - '<Book: Book_ with underscore>', - '<Book: Book 5>', - '<Book: Book 6>', - '<Book: Book 4>', - '<Book: Book 2>', - '<Book: Book 3>', - '<Book: Book 1>', + '<Article: Article with \\ backslash>', + '<Article: Article% with percent sign>', + '<Article: Article_ with underscore>', + '<Article: Article 5>', + '<Article: Article 6>', + '<Article: Article 4>', + '<Article: Article 2>', + '<Article: Article 3>', + '<Article: Article 1>', ]) def test_none(self): # none() returns a QuerySet that behaves like any other QuerySet object - self.assertQuerysetEqual(Book.objects.none(), []) + self.assertQuerysetEqual(Article.objects.none(), []) self.assertQuerysetEqual( - Book.objects.none().filter(title__startswith='Book'), []) + Article.objects.none().filter(headline__startswith='Article'), []) self.assertQuerysetEqual( - Book.objects.filter(title__startswith='Book').none(), []) - self.assertEqual(Book.objects.none().count(), 0) + Article.objects.filter(headline__startswith='Article').none(), []) + self.assertEqual(Article.objects.none().count(), 0) self.assertEqual( - Book.objects.none().update(title="This should not take effect"), 0) + Article.objects.none().update(headline="This should not take effect"), 0) self.assertQuerysetEqual( - [article for article in Book.objects.none().iterator()], + [article for article in Article.objects.none().iterator()], []) def test_in(self): # using __in with an empty list should return an empty query set - self.assertQuerysetEqual(Book.objects.filter(id__in=[]), []) - self.assertQuerysetEqual(Book.objects.exclude(id__in=[]), + self.assertQuerysetEqual(Article.objects.filter(id__in=[]), []) + self.assertQuerysetEqual(Article.objects.exclude(id__in=[]), [ - '<Book: Book 5>', - '<Book: Book 6>', - '<Book: Book 4>', - '<Book: Book 2>', - '<Book: Book 3>', - '<Book: Book 7>', - '<Book: Book 1>', + '<Article: Article 5>', + '<Article: Article 6>', + '<Article: Article 4>', + '<Article: Article 2>', + '<Article: Article 3>', + '<Article: Article 7>', + '<Article: Article 1>', ]) def test_error_messages(self): # Programming errors are pointed out with nice error messages - with six.assertRaisesRegex(self, FieldError, "Cannot resolve keyword 'pubdate_year' " - "into field. Choices are: .+"): - Book.objects.filter(pubdate_year='2005').count() - - with self.assertRaises(FieldError, msg="Join on field 'title' not permitted. " - "Did you misspell 'starts' for the lookup type?"): - Book.objects.filter(title__starts='Book') + try: + Article.objects.filter(pub_date_year='2005').count() + self.fail('FieldError not raised') + except FieldError as ex: + self.assertEqual(str(ex), "Cannot resolve keyword 'pub_date_year' " + "into field. Choices are: author, headline, id, pub_date, tag") + try: + Article.objects.filter(headline__starts='Article') + self.fail('FieldError not raised') + except FieldError as ex: + self.assertEqual(str(ex), "Join on field 'headline' not permitted. " + "Did you misspell 'starts' for the lookup type?") def test_regex(self): - # Create some articles with a bit more interesting names for testing field lookups: - for a in Book.objects.all(): + # Create some articles with a bit more interesting headlines for testing field lookups: + for a in Article.objects.all(): a.delete() now = datetime.now() - b1 = Book(pubdate=now, title='f') - b1.save() - b2 = Book(pubdate=now, title='fo') - b2.save() - b3 = Book(pubdate=now, title='foo') - b3.save() - b4 = Book(pubdate=now, title='fooo') - b4.save() - b5 = Book(pubdate=now, title='hey-Foo') - b5.save() - b6 = Book(pubdate=now, title='bar') - b6.save() - b7 = Book(pubdate=now, title='AbBa') - b7.save() - b8 = Book(pubdate=now, title='baz') - b8.save() - b9 = Book(pubdate=now, title='baxZ') - b9.save() + a1 = Article(pub_date=now, headline='f') + a1.save() + a2 = Article(pub_date=now, headline='fo') + a2.save() + a3 = Article(pub_date=now, headline='foo') + a3.save() + a4 = Article(pub_date=now, headline='fooo') + a4.save() + a5 = Article(pub_date=now, headline='hey-Foo') + a5.save() + a6 = Article(pub_date=now, headline='bar') + a6.save() + a7 = Article(pub_date=now, headline='AbBa') + a7.save() + a8 = Article(pub_date=now, headline='baz') + a8.save() + a9 = Article(pub_date=now, headline='baxZ') + a9.save() # zero-or-more - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'fo*'), - ['<Book: f>', '<Book: fo>', '<Book: foo>', '<Book: fooo>']) - self.assertQuerysetEqual(Book.objects.filter(title__iregex=r'fo*'), + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fo*'), + ['<Article: f>', '<Article: fo>', '<Article: foo>', '<Article: fooo>']) + self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'fo*'), [ - '<Book: f>', - '<Book: fo>', - '<Book: foo>', - '<Book: fooo>', - '<Book: hey-Foo>', + '<Article: f>', + '<Article: fo>', + '<Article: foo>', + '<Article: fooo>', + '<Article: hey-Foo>', ]) # one-or-more - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'fo+'), - ['<Book: fo>', '<Book: foo>', '<Book: fooo>']) + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fo+'), + ['<Article: fo>', '<Article: foo>', '<Article: fooo>']) # wildcard - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'fooo?'), - ['<Book: foo>', '<Book: fooo>']) + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fooo?'), + ['<Article: foo>', '<Article: fooo>']) # leading anchor - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'^b'), - ['<Book: bar>', '<Book: baxZ>', '<Book: baz>']) - self.assertQuerysetEqual(Book.objects.filter(title__iregex=r'^a'), - ['<Book: AbBa>']) + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'^b'), + ['<Article: bar>', '<Article: baxZ>', '<Article: baz>']) + self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'^a'), + ['<Article: AbBa>']) # trailing anchor - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'z$'), - ['<Book: baz>']) - self.assertQuerysetEqual(Book.objects.filter(title__iregex=r'z$'), - ['<Book: baxZ>', '<Book: baz>']) + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'z$'), + ['<Article: baz>']) + self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'z$'), + ['<Article: baxZ>', '<Article: baz>']) # character sets - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'ba[rz]'), - ['<Book: bar>', '<Book: baz>']) - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'ba.[RxZ]'), - ['<Book: baxZ>']) - self.assertQuerysetEqual(Book.objects.filter(title__iregex=r'ba[RxZ]'), - ['<Book: bar>', '<Book: baxZ>', '<Book: baz>']) + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'ba[rz]'), + ['<Article: bar>', '<Article: baz>']) + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'ba.[RxZ]'), + ['<Article: baxZ>']) + self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'ba[RxZ]'), + ['<Article: bar>', '<Article: baxZ>', '<Article: baz>']) # and more articles: - b10 = Book(pubdate=now, title='foobar') - b10.save() - b11 = Book(pubdate=now, title='foobaz') - b11.save() - b12 = Book(pubdate=now, title='ooF') - b12.save() - b13 = Book(pubdate=now, title='foobarbaz') - b13.save() - b14 = Book(pubdate=now, title='zoocarfaz') - b14.save() - b15 = Book(pubdate=now, title='barfoobaz') - b15.save() - b16 = Book(pubdate=now, title='bazbaRFOO') - b16.save() + a10 = Article(pub_date=now, headline='foobar') + a10.save() + a11 = Article(pub_date=now, headline='foobaz') + a11.save() + a12 = Article(pub_date=now, headline='ooF') + a12.save() + a13 = Article(pub_date=now, headline='foobarbaz') + a13.save() + a14 = Article(pub_date=now, headline='zoocarfaz') + a14.save() + a15 = Article(pub_date=now, headline='barfoobaz') + a15.save() + a16 = Article(pub_date=now, headline='bazbaRFOO') + a16.save() # alternation - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'oo(f|b)'), + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'oo(f|b)'), [ - '<Book: barfoobaz>', - '<Book: foobar>', - '<Book: foobarbaz>', - '<Book: foobaz>', + '<Article: barfoobaz>', + '<Article: foobar>', + '<Article: foobarbaz>', + '<Article: foobaz>', ]) - self.assertQuerysetEqual(Book.objects.filter(title__iregex=r'oo(f|b)'), + self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'oo(f|b)'), [ - '<Book: barfoobaz>', - '<Book: foobar>', - '<Book: foobarbaz>', - '<Book: foobaz>', - '<Book: ooF>', + '<Article: barfoobaz>', + '<Article: foobar>', + '<Article: foobarbaz>', + '<Article: foobaz>', + '<Article: ooF>', ]) - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'^foo(f|b)'), - ['<Book: foobar>', '<Book: foobarbaz>', '<Book: foobaz>']) + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'^foo(f|b)'), + ['<Article: foobar>', '<Article: foobarbaz>', '<Article: foobaz>']) # greedy matching - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'b.*az'), + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b.*az'), [ - '<Book: barfoobaz>', - '<Book: baz>', - '<Book: bazbaRFOO>', - '<Book: foobarbaz>', - '<Book: foobaz>', + '<Article: barfoobaz>', + '<Article: baz>', + '<Article: bazbaRFOO>', + '<Article: foobarbaz>', + '<Article: foobaz>', ]) - self.assertQuerysetEqual(Book.objects.filter(title__iregex=r'b.*ar'), + self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'b.*ar'), [ - '<Book: bar>', - '<Book: barfoobaz>', - '<Book: bazbaRFOO>', - '<Book: foobar>', - '<Book: foobarbaz>', + '<Article: bar>', + '<Article: barfoobaz>', + '<Article: bazbaRFOO>', + '<Article: foobar>', + '<Article: foobarbaz>', ]) @skipUnlessDBFeature('supports_regex_backreferencing') def test_regex_backreferencing(self): # grouping and backreferences now = datetime.now() - b10 = Book(pubdate=now, title='foobar') - b10.save() - b11 = Book(pubdate=now, title='foobaz') - b11.save() - b12 = Book(pubdate=now, title='ooF') - b12.save() - b13 = Book(pubdate=now, title='foobarbaz') - b13.save() - b14 = Book(pubdate=now, title='zoocarfaz') - b14.save() - b15 = Book(pubdate=now, title='barfoobaz') - b15.save() - b16 = Book(pubdate=now, title='bazbaRFOO') - b16.save() - self.assertQuerysetEqual(Book.objects.filter(title__regex=r'b(.).*b\1'), - ['<Book: barfoobaz>', '<Book: bazbaRFOO>', '<Book: foobarbaz>']) + a10 = Article(pub_date=now, headline='foobar') + a10.save() + a11 = Article(pub_date=now, headline='foobaz') + a11.save() + a12 = Article(pub_date=now, headline='ooF') + a12.save() + a13 = Article(pub_date=now, headline='foobarbaz') + a13.save() + a14 = Article(pub_date=now, headline='zoocarfaz') + a14.save() + a15 = Article(pub_date=now, headline='barfoobaz') + a15.save() + a16 = Article(pub_date=now, headline='bazbaRFOO') + a16.save() + self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b(.).*b\1'), + ['<Article: barfoobaz>', '<Article: bazbaRFOO>', '<Article: foobarbaz>']) def test_nonfield_lookups(self): """ @@ -615,11 +616,11 @@ class LookupTests(TestCase): exception. """ with self.assertRaises(FieldError): - Book.objects.filter(name__blahblah=99) + Article.objects.filter(headline__blahblah=99) with self.assertRaises(FieldError): - Book.objects.filter(name__blahblah__exact=99) + Article.objects.filter(headline__blahblah__exact=99) with self.assertRaises(FieldError): - Book.objects.filter(blahblah=99) + Article.objects.filter(blahblah=99) def test_lookup_collision(self): """ diff --git a/tests/m2m_through_regress/tests.py b/tests/m2m_through_regress/tests.py index 5ac10462fa..de4d52a2db 100644 --- a/tests/m2m_through_regress/tests.py +++ b/tests/m2m_through_regress/tests.py @@ -10,6 +10,7 @@ from .models import (Person, Group, Membership, UserMembership, Car, Driver, class M2MThroughTestCase(TestCase): + def test_everything(self): bob = Person.objects.create(name="Bob") jim = Person.objects.create(name="Jim") diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py index 265eb97c36..20645cf91f 100644 --- a/tests/middleware/tests.py +++ b/tests/middleware/tests.py @@ -707,6 +707,9 @@ class TransactionMiddlewareTest(IgnorePendingDeprecationWarningsMixin, Transacti """ Test the transaction middleware. """ + + available_apps = ['middleware'] + def setUp(self): super(TransactionMiddlewareTest, self).setUp() self.request = HttpRequest() diff --git a/tests/model_forms/models.py b/tests/model_forms/models.py index 610dc34001..a4cf9471de 100644 --- a/tests/model_forms/models.py +++ b/tests/model_forms/models.py @@ -11,13 +11,13 @@ from __future__ import unicode_literals import os import tempfile +from django.core import validators from django.core.exceptions import ImproperlyConfigured from django.core.files.storage import FileSystemStorage from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible -from shared_models.models import Author, Book temp_storage_dir = tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR']) temp_storage = FileSystemStorage(temp_storage_dir) @@ -47,12 +47,22 @@ class Category(models.Model): return self.__str__() @python_2_unicode_compatible +class Writer(models.Model): + name = models.CharField(max_length=50, help_text='Use both first and last names.') + + class Meta: + ordering = ('name',) + + def __str__(self): + return self.name + +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=50) slug = models.SlugField() pub_date = models.DateField() created = models.DateField(editable=False) - writer = models.ForeignKey(Author) + writer = models.ForeignKey(Writer) article = models.TextField() categories = models.ManyToManyField(Category, blank=True) status = models.PositiveIntegerField(choices=ARTICLE_STATUS, blank=True, null=True) @@ -72,12 +82,12 @@ class ImprovedArticle(models.Model): class ImprovedArticleWithParentLink(models.Model): article = models.OneToOneField(Article, parent_link=True) -class BetterAuthor(Author): +class BetterWriter(Writer): score = models.IntegerField() @python_2_unicode_compatible -class AuthorProfile(models.Model): - writer = models.OneToOneField(Author, primary_key=True) +class WriterProfile(models.Model): + writer = models.OneToOneField(Writer, primary_key=True) age = models.PositiveIntegerField() def __str__(self): @@ -177,6 +187,14 @@ class Inventory(models.Model): def __repr__(self): return self.__str__() +class Book(models.Model): + title = models.CharField(max_length=40) + author = models.ForeignKey(Writer, blank=True, null=True) + special_id = models.IntegerField(blank=True, null=True, unique=True) + + class Meta: + unique_together = ('title', 'author') + class BookXtra(models.Model): isbn = models.CharField(max_length=16, unique=True) suffix1 = models.IntegerField(blank=True, default=0) @@ -269,3 +287,12 @@ class ColourfulItem(models.Model): class ArticleStatusNote(models.Model): name = models.CharField(max_length=20) status = models.ManyToManyField(ArticleStatus) + +class CustomErrorMessage(models.Model): + name1 = models.CharField(max_length=50, + validators=[validators.validate_slug], + error_messages={'invalid': 'Model custom error message.'}) + + name2 = models.CharField(max_length=50, + validators=[validators.validate_slug], + error_messages={'invalid': 'Model custom error message.'}) diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 58dde13a8a..39be824798 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -17,14 +17,12 @@ from django.utils.unittest import skipUnless from django.test import TestCase from django.utils import six -from shared_models.models import Author, Book - -from .models import (Article, ArticleStatus, BetterAuthor, BigInt, +from .models import (Article, ArticleStatus, BetterWriter, BigInt, Book, Category, CommaSeparatedInteger, CustomFieldForExclusionModel, DerivedBook, DerivedPost, ExplicitPK, FlexibleDatePost, ImprovedArticle, ImprovedArticleWithParentLink, Inventory, Post, Price, - Product, TextFile, AuthorProfile, Colour, ColourfulItem, - ArticleStatusNote, DateTimePost, test_images) + Product, TextFile, Writer, WriterProfile, Colour, ColourfulItem, + ArticleStatusNote, DateTimePost, CustomErrorMessage, test_images) if test_images: from .models import ImageFile, OptionalImageFile @@ -54,14 +52,15 @@ class PriceForm(forms.ModelForm): class BookForm(forms.ModelForm): class Meta: - fields = ['title', 'author', 'pubdate'] model = Book + fields = '__all__' class DerivedBookForm(forms.ModelForm): class Meta: - fields = ['title', 'author', 'isbn', 'suffix1', 'suffix2'] model = DerivedBook + fields = '__all__' + class ExplicitPKForm(forms.ModelForm): @@ -88,11 +87,11 @@ class DerivedPostForm(forms.ModelForm): fields = '__all__' -class CustomAuthorForm(forms.ModelForm): +class CustomWriterForm(forms.ModelForm): name = forms.CharField(required=False) class Meta: - model = Author + model = Writer fields = '__all__' @@ -128,7 +127,7 @@ class PartialArticleForm(forms.ModelForm): class RoykoForm(forms.ModelForm): class Meta: - model = Author + model = Writer fields = '__all__' @@ -188,15 +187,14 @@ class ImprovedArticleWithParentLinkForm(forms.ModelForm): fields = '__all__' -class BetterAuthorForm(forms.ModelForm): +class BetterWriterForm(forms.ModelForm): class Meta: - model = BetterAuthor + model = BetterWriter fields = '__all__' - -class AuthorProfileForm(forms.ModelForm): +class WriterProfileForm(forms.ModelForm): class Meta: - model = AuthorProfile + model = WriterProfile fields = '__all__' @@ -254,6 +252,12 @@ class StatusNoteCBM2mForm(forms.ModelForm): fields = '__all__' widgets = {'status': forms.CheckboxSelectMultiple} +class CustomErrorMessageForm(forms.ModelForm): + name1 = forms.CharField(error_messages={'invalid': 'Form custom error message.'}) + + class Meta: + model = CustomErrorMessage + class ModelFormBaseTest(TestCase): def test_base_form(self): @@ -324,14 +328,14 @@ class ModelFormBaseTest(TestCase): forms.fields.BooleanField) def test_override_field(self): - class AuthorForm(forms.ModelForm): + class WriterForm(forms.ModelForm): book = forms.CharField(required=False) class Meta: - model = Author + model = Writer fields = '__all__' - wf = AuthorForm({'name': 'Richard Lockridge'}) + wf = WriterForm({'name': 'Richard Lockridge'}) self.assertTrue(wf.is_valid()) def test_limit_nonexistent_field(self): @@ -495,7 +499,7 @@ class ModelFormBaseTest(TestCase): ['slug', 'name']) -class TestWidgetForm(forms.ModelForm): +class FieldOverridesTroughFormMetaForm(forms.ModelForm): class Meta: model = Category fields = ['name', 'url', 'slug'] @@ -503,25 +507,74 @@ class TestWidgetForm(forms.ModelForm): 'name': forms.Textarea, 'url': forms.TextInput(attrs={'class': 'url'}) } + labels = { + 'name': 'Title', + } + help_texts = { + 'slug': 'Watch out! Letters, numbers, underscores and hyphens only.', + } + error_messages = { + 'slug': { + 'invalid': ( + "Didn't you read the help text? " + "We said letters, numbers, underscores and hyphens only!" + ) + } + } +class TestFieldOverridesTroughFormMeta(TestCase): + def test_widget_overrides(self): + form = FieldOverridesTroughFormMetaForm() + self.assertHTMLEqual( + str(form['name']), + '<textarea id="id_name" rows="10" cols="40" name="name"></textarea>', + ) + self.assertHTMLEqual( + str(form['url']), + '<input id="id_url" type="text" class="url" name="url" maxlength="40" />', + ) + self.assertHTMLEqual( + str(form['slug']), + '<input id="id_slug" type="text" name="slug" maxlength="20" />', + ) -class TestWidgets(TestCase): - def test_base_widgets(self): - frm = TestWidgetForm() + def test_label_overrides(self): + form = FieldOverridesTroughFormMetaForm() self.assertHTMLEqual( - str(frm['name']), - '<textarea id="id_name" rows="10" cols="40" name="name"></textarea>' + str(form['name'].label_tag()), + '<label for="id_name">Title:</label>', ) self.assertHTMLEqual( - str(frm['url']), - '<input id="id_url" type="text" class="url" name="url" maxlength="40" />' + str(form['url'].label_tag()), + '<label for="id_url">The URL:</label>', ) self.assertHTMLEqual( - str(frm['slug']), - '<input id="id_slug" type="text" name="slug" maxlength="20" />' + str(form['slug'].label_tag()), + '<label for="id_slug">Slug:</label>', + ) + + def test_help_text_overrides(self): + form = FieldOverridesTroughFormMetaForm() + self.assertEqual( + form['slug'].help_text, + 'Watch out! Letters, numbers, underscores and hyphens only.', ) + def test_error_messages_overrides(self): + form = FieldOverridesTroughFormMetaForm(data={ + 'name': 'Category', + 'url': '/category/', + 'slug': '!%#*@', + }) + form.full_clean() + + error = [ + "Didn't you read the help text? " + "We said letters, numbers, underscores and hyphens only!", + ] + self.assertEqual(form.errors, {'slug': error}) + class IncompleteCategoryFormWithFields(forms.ModelForm): """ @@ -556,7 +609,7 @@ class ValidationTest(TestCase): assert form.is_valid() def test_notrequired_overrides_notblank(self): - form = CustomAuthorForm({}) + form = CustomWriterForm({}) assert form.is_valid() @@ -565,7 +618,7 @@ class ValidationTest(TestCase): # unique/unique_together validation class UniqueTest(TestCase): def setUp(self): - self.author = Author.objects.create(name='Mike Royko') + self.writer = Writer.objects.create(name='Mike Royko') def test_simple_unique(self): form = ProductForm({'slug': 'teddy-bear-blue'}) @@ -589,31 +642,33 @@ class UniqueTest(TestCase): def test_unique_null(self): title = 'I May Be Wrong But I Doubt It' - form = BookForm({'title': title, 'author': self.author.pk, 'pubdate': '2012-12-12 00:00:00'}) + form = BookForm({'title': title, 'author': self.writer.pk}) self.assertTrue(form.is_valid()) form.save() - form = BookForm({'title': title, 'author': self.author.pk, 'pubdate': '2012-12-12 00:00:00'}) + form = BookForm({'title': title, 'author': self.writer.pk}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) self.assertEqual(form.errors['__all__'], ['Book with this Title and Author already exists.']) - form = BookForm({'title': title, 'pubdate': '2012-12-12 00:00:00'}) + form = BookForm({'title': title}) self.assertTrue(form.is_valid()) form.save() - form = BookForm({'title': title, 'pubdate': '2012-12-12 00:00:00'}) + form = BookForm({'title': title}) self.assertTrue(form.is_valid()) def test_inherited_unique(self): - form = BetterAuthorForm({'name': 'Mike Royko', 'score': 3}) + title = 'Boss' + Book.objects.create(title=title, author=self.writer, special_id=1) + form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'special_id': '1', 'isbn': '12345'}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['name'], ['Author with this Name already exists.']) + self.assertEqual(form.errors['special_id'], ['Book with this Special id already exists.']) def test_inherited_unique_together(self): title = 'Boss' - form = BookForm({'title': title, 'author': self.author.pk, 'pubdate': '2012-12-12 00:00:00'}) + form = BookForm({'title': title, 'author': self.writer.pk}) self.assertTrue(form.is_valid()) form.save() - form = DerivedBookForm({'title': title, 'author': self.author.pk, 'isbn': '12345'}) + form = DerivedBookForm({'title': title, 'author': self.writer.pk, 'isbn': '12345'}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) self.assertEqual(form.errors['__all__'], ['Book with this Title and Author already exists.']) @@ -621,9 +676,8 @@ class UniqueTest(TestCase): def test_abstract_inherited_unique(self): title = 'Boss' isbn = '12345' - dbook = DerivedBook.objects.create(title=title, author=self.author, isbn=isbn, - pubdate='2012-12-12 00:00') - form = DerivedBookForm({'title': 'Other', 'author': self.author.pk, 'isbn': isbn}) + dbook = DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn) + form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'isbn': isbn}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) self.assertEqual(form.errors['isbn'], ['Derived book with this Isbn already exists.']) @@ -631,11 +685,10 @@ class UniqueTest(TestCase): def test_abstract_inherited_unique_together(self): title = 'Boss' isbn = '12345' - dbook = DerivedBook.objects.create(title=title, author=self.author, isbn=isbn, - pubdate='2012-12-12 00:00') + dbook = DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn) form = DerivedBookForm({ 'title': 'Other', - 'author': self.author.pk, + 'author': self.writer.pk, 'isbn': '9876', 'suffix1': '0', 'suffix2': '0' @@ -753,7 +806,7 @@ class ModelToDictTests(TestCase): ] for c in categories: c.save() - writer = Author(name='Test writer') + writer = Writer(name='Test writer') writer.save() art = Article( @@ -862,10 +915,10 @@ class OldFormForXTests(TestCase): with self.assertRaises(ValueError): f.save() - # Create a couple of Authors. - w_royko = Author(name='Mike Royko') + # Create a couple of Writers. + w_royko = Writer(name='Mike Royko') w_royko.save() - w_woodward = Author(name='Bob Woodward') + w_woodward = Writer(name='Bob Woodward') w_woodward.save() # ManyToManyFields are represented by a MultipleChoiceField, ForeignKeys and any # fields with the 'choices' attribute are represented by a ChoiceField. @@ -903,9 +956,9 @@ class OldFormForXTests(TestCase): # When the ModelForm is passed an instance, that instance's current values are # inserted as 'initial' data in each Field. - w = Author.objects.get(name='Mike Royko') + w = Writer.objects.get(name='Mike Royko') f = RoykoForm(auto_id=False, instance=w) - self.assertHTMLEqual(six.text_type(f), '''<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="100" /><br /><span class="helptext">Use both first and last names.</span></td></tr>''') + self.assertHTMLEqual(six.text_type(f), '''<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br /><span class="helptext">Use both first and last names.</span></td></tr>''') art = Article( headline='Test article', @@ -1117,7 +1170,7 @@ class OldFormForXTests(TestCase): c4 = Category.objects.create(name='Fourth', url='4th') self.assertEqual(c4.name, 'Fourth') - w_bernstein = Author.objects.create(name='Carl Bernstein') + w_bernstein = Writer.objects.create(name='Carl Bernstein') self.assertEqual(w_bernstein.name, 'Carl Bernstein') self.assertHTMLEqual(f.as_ul(), '''<li>Headline: <input type="text" name="headline" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" maxlength="50" /></li> @@ -1294,17 +1347,17 @@ class OldFormForXTests(TestCase): self.assertEqual(list(ImprovedArticleWithParentLinkForm.base_fields), []) - bw = BetterAuthor(name='Joe Better', score=10) + bw = BetterWriter(name='Joe Better', score=10) bw.save() self.assertEqual(sorted(model_to_dict(bw)), - ['author_ptr', 'id', 'name', 'score']) + ['id', 'name', 'score', 'writer_ptr']) - form = BetterAuthorForm({'name': 'Some Name', 'score': 12}) + form = BetterWriterForm({'name': 'Some Name', 'score': 12}) self.assertEqual(form.is_valid(), True) bw2 = form.save() bw2.delete() - form = AuthorProfileForm() + form = WriterProfileForm() self.assertHTMLEqual(form.as_p(), '''<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer"> <option value="" selected="selected">---------</option> <option value="%s">Bob Woodward</option> @@ -1318,11 +1371,11 @@ class OldFormForXTests(TestCase): 'writer': six.text_type(w_woodward.pk), 'age': '65', } - form = AuthorProfileForm(data) + form = WriterProfileForm(data) instance = form.save() self.assertEqual(six.text_type(instance), 'Bob Woodward is 65') - form = AuthorProfileForm(instance=instance) + form = WriterProfileForm(instance=instance) self.assertHTMLEqual(form.as_p(), '''<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer"> <option value="">---------</option> <option value="%s" selected="selected">Bob Woodward</option> @@ -1715,6 +1768,18 @@ class OldFormForXTests(TestCase): </select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></p>""" % {'blue_pk': colour.pk}) + def test_custom_error_messages(self) : + data = {'name1': '@#$!!**@#$', 'name2': '@#$!!**@#$'} + errors = CustomErrorMessageForm(data).errors + self.assertHTMLEqual( + str(errors['name1']), + '<ul class="errorlist"><li>Form custom error message.</li></ul>' + ) + self.assertHTMLEqual( + str(errors['name2']), + '<ul class="errorlist"><li>Model custom error message.</li></ul>' + ) + class M2mHelpTextTest(TestCase): """Tests for ticket #9321.""" diff --git a/tests/model_forms_regress/tests.py b/tests/model_forms_regress/tests.py index 80900a59e0..39ae857219 100644 --- a/tests/model_forms_regress/tests.py +++ b/tests/model_forms_regress/tests.py @@ -97,12 +97,14 @@ class PartiallyLocalizedTripleForm(forms.ModelForm): class Meta: model = Triple localized_fields = ('left', 'right',) + fields = '__all__' class FullyLocalizedTripleForm(forms.ModelForm): class Meta: model = Triple - localized_fields = "__all__" + localized_fields = '__all__' + fields = '__all__' class LocalizedModelFormTest(TestCase): def test_model_form_applies_localize_to_some_fields(self): diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index 8cfdf53995..03cd3b0159 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -1261,7 +1261,7 @@ class ModelFormsetTest(TestCase): ['Please correct the duplicate data for subtitle which must be unique for the month in posted.']) -class TestModelFormsetWidgets(TestCase): +class TestModelFormsetOverridesTroughFormMeta(TestCase): def test_modelformset_factory_widgets(self): widgets = { 'name': forms.TextInput(attrs={'class': 'poet'}) @@ -1283,3 +1283,53 @@ class TestModelFormsetWidgets(TestCase): "%s" % form['title'], '<input class="book" id="id_title" maxlength="100" name="title" type="text" />' ) + + def test_modelformset_factory_labels_overrides(self): + BookFormSet = modelformset_factory(Book, fields="__all__", labels={ + 'title': 'Name' + }) + form = BookFormSet.form() + self.assertHTMLEqual(form['title'].label_tag(), '<label for="id_title">Name:</label>') + + def test_inlineformset_factory_labels_overrides(self): + BookFormSet = inlineformset_factory(Author, Book, fields="__all__", labels={ + 'title': 'Name' + }) + form = BookFormSet.form() + self.assertHTMLEqual(form['title'].label_tag(), '<label for="id_title">Name:</label>') + + def test_modelformset_factory_help_text_overrides(self): + BookFormSet = modelformset_factory(Book, fields="__all__", help_texts={ + 'title': 'Choose carefully.' + }) + form = BookFormSet.form() + self.assertEqual(form['title'].help_text, 'Choose carefully.') + + def test_inlineformset_factory_help_text_overrides(self): + BookFormSet = inlineformset_factory(Author, Book, fields="__all__", help_texts={ + 'title': 'Choose carefully.' + }) + form = BookFormSet.form() + self.assertEqual(form['title'].help_text, 'Choose carefully.') + + def test_modelformset_factory_error_messages_overrides(self): + author = Author.objects.create(pk=1, name='Charles Baudelaire') + BookFormSet = modelformset_factory(Book, fields="__all__", error_messages={ + 'title': { + 'max_length': 'Title too long!!' + } + }) + form = BookFormSet.form(data={'title': 'Foo ' * 30, 'author': author.id}) + form.full_clean() + self.assertEqual(form.errors, {'title': ['Title too long!!']}) + + def test_inlineformset_factory_error_messages_overrides(self): + author = Author.objects.create(pk=1, name='Charles Baudelaire') + BookFormSet = inlineformset_factory(Author, Book, fields="__all__", error_messages={ + 'title': { + 'max_length': 'Title too long!!' + } + }) + form = BookFormSet.form(data={'title': 'Foo ' * 30, 'author': author.id}) + form.full_clean() + self.assertEqual(form.errors, {'title': ['Title too long!!']}) diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index e72fb6a4f9..a0d937ce81 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -1943,6 +1943,12 @@ class SyncOnlyDefaultDatabaseRouter(object): class SyncDBTestCase(TestCase): + + available_apps = [ + 'multiple_database', + 'django.contrib.auth', + 'django.contrib.contenttypes' + ] multi_db = True def test_syncdb_to_other_database(self): diff --git a/tests/proxy_model_inheritance/tests.py b/tests/proxy_model_inheritance/tests.py index 239bc67809..85bf74a4e9 100644 --- a/tests/proxy_model_inheritance/tests.py +++ b/tests/proxy_model_inheritance/tests.py @@ -22,6 +22,8 @@ class ProxyModelInheritanceTests(TransactionTestCase): apps and calls syncdb, then verifies that the table has been created. """ + available_apps = [] + def setUp(self): self.old_sys_path = sys.path[:] sys.path.append(os.path.dirname(os.path.abspath(upath(__file__)))) @@ -38,7 +40,11 @@ class ProxyModelInheritanceTests(TransactionTestCase): del cache.app_models['app2'] def test_table_exists(self): - call_command('syncdb', verbosity=0) + try: + cache.set_available_apps(settings.INSTALLED_APPS) + call_command('syncdb', verbosity=0) + finally: + cache.unset_available_apps() from .app1.models import ProxyModel from .app2.models import NiceModel self.assertEqual(NiceModel.objects.all().count(), 0) diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 481b690c20..352e87a634 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -16,15 +16,16 @@ from django.test.utils import str_prefix from django.utils import unittest from django.utils.datastructures import SortedDict -from .models import (Annotation, Article, Author, Celebrity, Child, Cover, - Detail, DumbCategory, ExtraInfo, Fan, Item, LeafA, Join, LeafB, LoopX, LoopZ, - ManagedModel, Member, NamedCategory, Note, Number, Plaything, PointerA, - Ranking, Related, Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, - Node, ObjectA, ObjectB, ObjectC, CategoryItem, SimpleCategory, - SpecialCategory, OneToOneCategory, NullableName, ProxyCategory, - SingleObject, RelatedObject, ModelA, ModelB, ModelC, ModelD, Responsibility, - Job, JobResponsibilities, BaseA, Identifier, Program, Channel, Page, - Paragraph, Chapter, Book, MyObject, Order, OrderItem) +from .models import ( + Annotation, Article, Author, Celebrity, Child, Cover, Detail, DumbCategory, + ExtraInfo, Fan, Item, LeafA, Join, LeafB, LoopX, LoopZ, ManagedModel, + Member, NamedCategory, Note, Number, Plaything, PointerA, Ranking, Related, + Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Node, ObjectA, + ObjectB, ObjectC, CategoryItem, SimpleCategory, SpecialCategory, + OneToOneCategory, NullableName, ProxyCategory, SingleObject, RelatedObject, + ModelA, ModelB, ModelC, ModelD, Responsibility, Job, JobResponsibilities, + BaseA, FK1, Identifier, Program, Channel, Page, Paragraph, Chapter, Book, + MyObject, Order, OrderItem) class BaseQuerysetTest(TestCase): @@ -1975,13 +1976,62 @@ class EmptyQuerySetTests(TestCase): class ValuesQuerysetTests(BaseQuerysetTest): - def test_flat_values_lits(self): + def setUp(self): Number.objects.create(num=72) + self.identity = lambda x: x + + def test_flat_values_list(self): qs = Number.objects.values_list("num") qs = qs.values_list("num", flat=True) - self.assertValueQuerysetEqual( - qs, [72] - ) + self.assertValueQuerysetEqual(qs, [72]) + + def test_extra_values(self): + # testing for ticket 14930 issues + qs = Number.objects.extra(select=SortedDict([('value_plus_x', 'num+%s'), + ('value_minus_x', 'num-%s')]), + select_params=(1, 2)) + qs = qs.order_by('value_minus_x') + qs = qs.values('num') + self.assertQuerysetEqual(qs, [{'num': 72}], self.identity) + + def test_extra_values_order_twice(self): + # testing for ticket 14930 issues + qs = Number.objects.extra(select={'value_plus_one': 'num+1', 'value_minus_one': 'num-1'}) + qs = qs.order_by('value_minus_one').order_by('value_plus_one') + qs = qs.values('num') + self.assertQuerysetEqual(qs, [{'num': 72}], self.identity) + + def test_extra_values_order_multiple(self): + # Postgres doesn't allow constants in order by, so check for that. + qs = Number.objects.extra(select={ + 'value_plus_one': 'num+1', + 'value_minus_one': 'num-1', + 'constant_value': '1' + }) + qs = qs.order_by('value_plus_one', 'value_minus_one', 'constant_value') + qs = qs.values('num') + self.assertQuerysetEqual(qs, [{'num': 72}], self.identity) + + def test_extra_values_order_in_extra(self): + # testing for ticket 14930 issues + qs = Number.objects.extra( + select={'value_plus_one': 'num+1', 'value_minus_one': 'num-1'}, + order_by=['value_minus_one']) + qs = qs.values('num') + + def test_extra_values_list(self): + # testing for ticket 14930 issues + qs = Number.objects.extra(select={'value_plus_one': 'num+1'}) + qs = qs.order_by('value_plus_one') + qs = qs.values_list('num') + self.assertQuerysetEqual(qs, [(72,)], self.identity) + + def test_flat_extra_values_list(self): + # testing for ticket 14930 issues + qs = Number.objects.extra(select={'value_plus_one': 'num+1'}) + qs = qs.order_by('value_plus_one') + qs = qs.values_list('num', flat=True) + self.assertQuerysetEqual(qs, [72], self.identity) class WeirdQuerysetSlicingTests(BaseQuerysetTest): @@ -2620,6 +2670,19 @@ class JoinReuseTest(TestCase): self.assertEqual(str(qs.query).count('JOIN'), 2) class DisjunctionPromotionTests(TestCase): + def test_disjuction_promotion_select_related(self): + fk1 = FK1.objects.create(f1='f1', f2='f2') + basea = BaseA.objects.create(a=fk1) + qs = BaseA.objects.filter(Q(a=fk1) | Q(b=2)) + self.assertEqual(str(qs.query).count(' JOIN '), 0) + qs = qs.select_related('a', 'b') + self.assertEqual(str(qs.query).count(' INNER JOIN '), 0) + self.assertEqual(str(qs.query).count(' LEFT OUTER JOIN '), 2) + with self.assertNumQueries(1): + self.assertQuerysetEqual(qs, [basea], lambda x: x) + self.assertEqual(qs[0].a, fk1) + self.assertIs(qs[0].b, None) + def test_disjunction_promotion1(self): # Pre-existing join, add two ORed filters to the same join, # all joins can be INNER JOINS. @@ -2669,17 +2732,23 @@ class DisjunctionPromotionTests(TestCase): self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) - def test_disjunction_promotion4(self): + @unittest.expectedFailure + def test_disjunction_promotion4_failing(self): + # Failure because no join repromotion qs = BaseA.objects.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('JOIN'), 0) qs = qs.filter(a__f1='foo') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) + + def test_disjunction_promotion4(self): qs = BaseA.objects.filter(a__f1='foo') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) qs = qs.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) - def test_disjunction_promotion5(self): + @unittest.expectedFailure + def test_disjunction_promotion5_failing(self): + # Failure because no join repromotion logic. qs = BaseA.objects.filter(Q(a=1) | Q(a=2)) # Note that the above filters on a force the join to an # inner join even if it is trimmed. @@ -2688,15 +2757,10 @@ class DisjunctionPromotionTests(TestCase): # So, now the a__f1 join doesn't need promotion. self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) - - @unittest.expectedFailure - def test_disjunction_promotion5_failing(self): qs = BaseA.objects.filter(Q(a__f1='foo') | Q(b__f1='foo')) # Now the join to a is created as LOUTER self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 0) - # The below filter should force the a to be inner joined. But, - # this is failing as we do not have join unpromotion logic. - qs = BaseA.objects.filter(Q(a=1) | Q(a=2)) + qs = qs.objects.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) diff --git a/tests/queryset_pickle/models.py b/tests/queryset_pickle/models.py index 4bcfcfbf04..3a8973505c 100644 --- a/tests/queryset_pickle/models.py +++ b/tests/queryset_pickle/models.py @@ -36,3 +36,13 @@ class Happening(models.Model): number2 = models.IntegerField(blank=True, default=Numbers.get_static_number) number3 = models.IntegerField(blank=True, default=Numbers.get_class_number) number4 = models.IntegerField(blank=True, default=nn.get_member_number) + +class Container(object): + # To test pickling we need a class that isn't defined on module, but + # is still available from app-cache. So, the Container class moves + # SomeModel outside of module level + class SomeModel(models.Model): + somefield = models.IntegerField() + +class M2MModel(models.Model): + groups = models.ManyToManyField(Group) diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py index da1bb8cb73..b4b540c80d 100644 --- a/tests/queryset_pickle/tests.py +++ b/tests/queryset_pickle/tests.py @@ -3,9 +3,10 @@ from __future__ import absolute_import import pickle import datetime +from django.db import models from django.test import TestCase -from .models import Group, Event, Happening +from .models import Group, Event, Happening, Container, M2MModel class PickleabilityTestCase(TestCase): @@ -49,3 +50,43 @@ class PickleabilityTestCase(TestCase): # can't just use assertEqual(original, unpickled) self.assertEqual(original.__class__, unpickled.__class__) self.assertEqual(original.args, unpickled.args) + + def test_model_pickle(self): + """ + Test that a model not defined on module level is pickleable. + """ + original = Container.SomeModel(pk=1) + dumped = pickle.dumps(original) + reloaded = pickle.loads(dumped) + self.assertEqual(original, reloaded) + # Also, deferred dynamic model works + Container.SomeModel.objects.create(somefield=1) + original = Container.SomeModel.objects.defer('somefield')[0] + dumped = pickle.dumps(original) + reloaded = pickle.loads(dumped) + self.assertEqual(original, reloaded) + self.assertEqual(original.somefield, reloaded.somefield) + + def test_model_pickle_m2m(self): + """ + Test intentionally the automatically created through model. + """ + m1 = M2MModel.objects.create() + g1 = Group.objects.create(name='foof') + m1.groups.add(g1) + m2m_through = M2MModel._meta.get_field_by_name('groups')[0].rel.through + original = m2m_through.objects.get() + dumped = pickle.dumps(original) + reloaded = pickle.loads(dumped) + self.assertEqual(original, reloaded) + + def test_model_pickle_dynamic(self): + class Meta: + proxy = True + dynclass = type("DynamicEventSubclass", (Event, ), + {'Meta': Meta, '__module__': Event.__module__}) + original = dynclass(pk=1) + dumped = pickle.dumps(original) + reloaded = pickle.loads(dumped) + self.assertEqual(original, reloaded) + self.assertIs(reloaded.__class__, dynclass) diff --git a/tests/requests/tests.py b/tests/requests/tests.py index 4d730bb561..d9120db4e7 100644 --- a/tests/requests/tests.py +++ b/tests/requests/tests.py @@ -683,6 +683,8 @@ class RequestsTests(SimpleTestCase): "Cannot establish two connections to an in-memory SQLite database.") class DatabaseConnectionHandlingTests(TransactionTestCase): + available_apps = [] + def setUp(self): # Use a temporary connection to avoid messing with the main one. self._old_default_connection = connections['default'] diff --git a/tests/runtests.py b/tests/runtests.py index a18324f676..da4592ecc0 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -5,6 +5,7 @@ import shutil import subprocess import sys import tempfile +import warnings from django import contrib from django.utils._os import upath @@ -29,7 +30,6 @@ SUBDIRS_TO_SKIP = [ ] ALWAYS_INSTALLED_APPS = [ - 'shared_models', 'django.contrib.contenttypes', 'django.contrib.auth', 'django.contrib.sites', @@ -72,6 +72,15 @@ def get_installed(): def setup(verbosity, test_labels): from django.conf import settings from django.db.models.loading import get_apps, load_app + from django.test.testcases import TransactionTestCase, TestCase + + # Force declaring available_apps in TransactionTestCase for faster tests. + def no_available_apps(self): + raise Exception("Please define available_apps in TransactionTestCase " + "and its subclasses.") + TransactionTestCase.available_apps = property(no_available_apps) + TestCase.available_apps = None + state = { 'INSTALLED_APPS': settings.INSTALLED_APPS, 'ROOT_URLCONF': getattr(settings, "ROOT_URLCONF", ""), @@ -99,7 +108,9 @@ def setup(verbosity, test_labels): logger.addHandler(handler) # Load all the ALWAYS_INSTALLED_APPS. - get_apps() + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', 'django.contrib.comments is deprecated and will be removed before Django 1.8.', PendingDeprecationWarning) + get_apps() # Load all the test model apps. test_modules = get_test_modules() diff --git a/tests/select_for_update/tests.py b/tests/select_for_update/tests.py index c2fa22705a..f087ca123a 100644 --- a/tests/select_for_update/tests.py +++ b/tests/select_for_update/tests.py @@ -23,6 +23,8 @@ requires_threading = unittest.skipUnless(threading, 'requires threading') class SelectForUpdateTests(TransactionTestCase): + available_apps = ['select_for_update'] + def setUp(self): transaction.enter_transaction_management() self.person = Person.objects.create(name='Reinhardt') diff --git a/tests/serializers/tests.py b/tests/serializers/tests.py index a96a1af748..038276ca21 100644 --- a/tests/serializers/tests.py +++ b/tests/serializers/tests.py @@ -259,6 +259,9 @@ class SerializersTestBase(object): class SerializersTransactionTestBase(object): + + available_apps = ['serializers'] + def test_forward_refs(self): """ Tests that objects ids can be referenced before they are diff --git a/tests/servers/tests.py b/tests/servers/tests.py index 920149477c..3377793d68 100644 --- a/tests/servers/tests.py +++ b/tests/servers/tests.py @@ -30,8 +30,15 @@ TEST_SETTINGS = { class LiveServerBase(LiveServerTestCase): - urls = 'servers.urls' + + available_apps = [ + 'servers', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + ] fixtures = ['testdata.json'] + urls = 'servers.urls' @classmethod def setUpClass(cls): diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index 97e43ff0eb..f8d66bb2b7 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -12,6 +12,8 @@ from django.utils import unittest, six @override_settings(TEST='override') class FullyDecoratedTranTestCase(TransactionTestCase): + available_apps = [] + def test_override(self): self.assertEqual(settings.TEST, 'override') diff --git a/tests/shared_models/models.py b/tests/shared_models/models.py deleted file mode 100644 index 145edad1bf..0000000000 --- a/tests/shared_models/models.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import unicode_literals - -from django.db import models -from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible - - -class Tag(models.Model): - name = models.CharField(max_length=255) - - -@python_2_unicode_compatible -class Author(models.Model): - name = models.CharField(max_length=100, help_text='Use both first and last names.', - unique=True) - - class Meta: - ordering = ['name'] - - def __str__(self): - return self.name - - -@python_2_unicode_compatible -class Book(models.Model): - title = models.CharField(max_length=200) - pages = models.IntegerField(default=0) - author = models.ForeignKey(Author, null=True, blank=True) - pubdate = models.DateTimeField() - tags = models.ManyToManyField(Tag) - - class Meta: - ordering = ['-pubdate', 'title'] - unique_together = ['title', 'author'] - - def __str__(self): - return self.title diff --git a/tests/signals_regress/models.py b/tests/signals_regress/models.py index e69de29bb2..829314c06c 100644 --- a/tests/signals_regress/models.py +++ b/tests/signals_regress/models.py @@ -0,0 +1,18 @@ +from django.db import models +from django.utils.encoding import python_2_unicode_compatible + + +@python_2_unicode_compatible +class Author(models.Model): + name = models.CharField(max_length=20) + + def __str__(self): + return self.name + +@python_2_unicode_compatible +class Book(models.Model): + name = models.CharField(max_length=20) + authors = models.ManyToManyField(Author) + + def __str__(self): + return self.name diff --git a/tests/signals_regress/tests.py b/tests/signals_regress/tests.py index b806d2116e..8fb3ad5a48 100644 --- a/tests/signals_regress/tests.py +++ b/tests/signals_regress/tests.py @@ -3,7 +3,7 @@ from __future__ import absolute_import from django.db import models from django.test import TestCase -from shared_models.models import Author, Book +from .models import Author, Book class SignalsRegressTests(TestCase): @@ -77,7 +77,7 @@ class SignalsRegressTests(TestCase): "Is created" ]) - b1 = Book(title='Snow Crash', pubdate='2012-02-02 12:00') + b1 = Book(name='Snow Crash') self.assertEqual(self.get_signal_output(b1.save), [ "pre_save signal, Snow Crash", "post_save signal, Snow Crash", @@ -87,7 +87,7 @@ class SignalsRegressTests(TestCase): def test_m2m_signals(self): """ Assigning and removing to/from m2m shouldn't generate an m2m signal """ - b1 = Book(title='Snow Crash', pubdate='2012-02-02 12:00') + b1 = Book(name='Snow Crash') self.get_signal_output(b1.save) a1 = Author(name='Neal Stephenson') self.get_signal_output(a1.save) diff --git a/tests/swappable_models/tests.py b/tests/swappable_models/tests.py index d3d61341d2..858061db23 100644 --- a/tests/swappable_models/tests.py +++ b/tests/swappable_models/tests.py @@ -13,6 +13,13 @@ from swappable_models.models import Article class SwappableModelTests(TestCase): + + available_apps = [ + 'swappable_models', + 'django.contrib.auth', + 'django.contrib.contenttypes', + ] + def setUp(self): # This test modifies the installed apps, so we need to make sure # we're not dealing with a cached app list. diff --git a/tests/syncdb_signals/tests.py b/tests/syncdb_signals/tests.py index d9be3b65d4..d70fb2c2c0 100644 --- a/tests/syncdb_signals/tests.py +++ b/tests/syncdb_signals/tests.py @@ -3,7 +3,7 @@ from django.test import TestCase from django.core import management from django.utils import six -from shared_models import models +from . import models PRE_SYNCDB_ARGS = ['app', 'create_models', 'verbosity', 'interactive', 'db'] @@ -55,6 +55,11 @@ signals.pre_syncdb.connect(pre_syncdb_receiver, sender=models) class SyncdbSignalTests(TestCase): + + available_apps = [ + 'syncdb_signals', + ] + def test_pre_syncdb_call_time(self): self.assertEqual(pre_syncdb_receiver.call_counter, 1) diff --git a/tests/test_runner/tests.py b/tests/test_runner/tests.py index 95ccf0242a..0dc7a0155a 100644 --- a/tests/test_runner/tests.py +++ b/tests/test_runner/tests.py @@ -9,9 +9,9 @@ from optparse import make_option from django.core.exceptions import ImproperlyConfigured from django.core.management import call_command from django import db -from django.test import runner, TransactionTestCase, skipUnlessDBFeature -from django.test.simple import DjangoTestSuiteRunner, get_tests +from django.test import runner, TestCase, TransactionTestCase, skipUnlessDBFeature from django.test.testcases import connections_support_transactions +from django.test.utils import IgnorePendingDeprecationWarningsMixin from django.utils import unittest from django.utils.importlib import import_module @@ -225,20 +225,24 @@ class Ticket17477RegressionTests(AdminScriptTestCase): self.assertNoOutput(err) -class ModulesTestsPackages(unittest.TestCase): +class ModulesTestsPackages(IgnorePendingDeprecationWarningsMixin, unittest.TestCase): def test_get_tests(self): "Check that the get_tests helper function can find tests in a directory" + from django.test.simple import get_tests module = import_module(TEST_APP_OK) tests = get_tests(module) self.assertIsInstance(tests, type(module)) def test_import_error(self): "Test for #12658 - Tests with ImportError's shouldn't fail silently" + from django.test.simple import get_tests module = import_module(TEST_APP_ERROR) self.assertRaises(ImportError, get_tests, module) -class Sqlite3InMemoryTestDbs(unittest.TestCase): +class Sqlite3InMemoryTestDbs(TestCase): + + available_apps = [] @unittest.skipUnless(all(db.connections[conn].vendor == 'sqlite' for conn in db.connections), "This is a sqlite-specific issue") @@ -258,7 +262,7 @@ class Sqlite3InMemoryTestDbs(unittest.TestCase): }, }) other = db.connections['other'] - DjangoTestSuiteRunner(verbosity=0).setup_databases() + runner.DiscoverRunner(verbosity=0).setup_databases() msg = "DATABASES setting '%s' option set to sqlite3's ':memory:' value shouldn't interfere with transaction support detection." % option # Transaction support should be properly initialised for the 'other' DB self.assertTrue(other.features.supports_transactions, msg) @@ -273,12 +277,12 @@ class DummyBackendTest(unittest.TestCase): """ Test that setup_databases() doesn't fail with dummy database backend. """ - runner = DjangoTestSuiteRunner(verbosity=0) + runner_instance = runner.DiscoverRunner(verbosity=0) old_db_connections = db.connections try: db.connections = db.ConnectionHandler({}) - old_config = runner.setup_databases() - runner.teardown_databases(old_config) + old_config = runner_instance.setup_databases() + runner_instance.teardown_databases(old_config) except Exception as e: self.fail("setup_databases/teardown_databases unexpectedly raised " "an error: %s" % e) @@ -318,6 +322,8 @@ class AutoIncrementResetTest(TransactionTestCase): that AutoField values start from 1 for each transactional test case. """ + available_apps = ['test_runner'] + reset_sequences = True @skipUnlessDBFeature('supports_sequence_reset') diff --git a/tests/test_suite_override/tests.py b/tests/test_suite_override/tests.py index dea110434f..35ca2b060b 100644 --- a/tests/test_suite_override/tests.py +++ b/tests/test_suite_override/tests.py @@ -1,5 +1,5 @@ from django.db.models import get_app -from django.test.simple import build_suite +from django.test.utils import IgnorePendingDeprecationWarningsMixin from django.utils import unittest @@ -9,7 +9,7 @@ def suite(): return testSuite -class SuiteOverrideTest(unittest.TestCase): +class SuiteOverrideTest(IgnorePendingDeprecationWarningsMixin, unittest.TestCase): def test_suite_override(self): """ Validate that you can define a custom suite when running tests with @@ -17,6 +17,7 @@ class SuiteOverrideTest(unittest.TestCase): suite using ``build_suite``). """ + from django.test.simple import build_suite app = get_app("test_suite_override") suite = build_suite(app) self.assertEqual(suite.countTestCases(), 1) diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index d72a482819..5f7fb05eb4 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -8,8 +8,7 @@ from django.http import HttpResponse from django.template.loader import render_to_string from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature from django.test.html import HTMLParseError, parse_html -from django.test.simple import make_doctest -from django.test.utils import CaptureQueriesContext +from django.test.utils import CaptureQueriesContext, IgnorePendingDeprecationWarningsMixin from django.utils import six from django.utils import unittest from django.utils.unittest import skip @@ -624,9 +623,10 @@ class AssertFieldOutputTests(SimpleTestCase): self.assertFieldOutput(MyCustomField, {}, {}, empty_value=None) -class DoctestNormalizerTest(SimpleTestCase): +class DoctestNormalizerTest(IgnorePendingDeprecationWarningsMixin, SimpleTestCase): def test_normalizer(self): + from django.test.simple import make_doctest suite = make_doctest("test_utils.doctest_output") failures = unittest.TextTestRunner(stream=six.StringIO()).run(suite) self.assertEqual(failures.failures, []) diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py index 0f16a9c805..24b7615d6f 100644 --- a/tests/transactions/tests.py +++ b/tests/transactions/tests.py @@ -26,6 +26,8 @@ class AtomicTests(TransactionTestCase): syntax and the bulk of the tests use the context manager syntax. """ + available_apps = ['transactions'] + def test_decorator_syntax_commit(self): @transaction.atomic def make_reporter(): @@ -232,6 +234,8 @@ class AtomicInsideLegacyTransactionManagementTests(AtomicTests): class AtomicMergeTests(TransactionTestCase): """Test merging transactions with savepoint=False.""" + available_apps = ['transactions'] + def test_merged_outer_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") @@ -286,6 +290,8 @@ class AtomicMergeTests(TransactionTestCase): "'atomic' requires transactions and savepoints.") class AtomicErrorsTests(TransactionTestCase): + available_apps = [] + def test_atomic_prevents_setting_autocommit(self): autocommit = transaction.get_autocommit() with transaction.atomic(): @@ -311,6 +317,8 @@ class AtomicErrorsTests(TransactionTestCase): class AtomicMiscTests(TransactionTestCase): + available_apps = [] + def test_wrap_callable_instance(self): # Regression test for #20028 class Callable(object): @@ -322,6 +330,8 @@ class AtomicMiscTests(TransactionTestCase): class TransactionTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): + available_apps = ['transactions'] + def create_a_reporter_then_fail(self, first, last): a = Reporter(first_name=first, last_name=last) a.save() @@ -477,6 +487,9 @@ class TransactionTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCas class TransactionRollbackTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): + + available_apps = ['transactions'] + def execute_bad_sql(self): cursor = connection.cursor() cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');") @@ -494,6 +507,9 @@ class TransactionRollbackTests(IgnorePendingDeprecationWarningsMixin, Transactio transaction.rollback() class TransactionContextManagerTests(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): + + available_apps = ['transactions'] + def create_reporter_and_fail(self): Reporter.objects.create(first_name="Bob", last_name="Holtzman") raise Exception diff --git a/tests/transactions_regress/tests.py b/tests/transactions_regress/tests.py index 5339b4a8ea..8078f1d128 100644 --- a/tests/transactions_regress/tests.py +++ b/tests/transactions_regress/tests.py @@ -10,6 +10,9 @@ from django.utils.unittest import skipIf, skipUnless from .models import Mod, M2mA, M2mB, SubMod class ModelInheritanceTests(TransactionTestCase): + + available_apps = ['transactions_regress'] + def test_save(self): # First, create a SubMod, then try to save another with conflicting # cnt field. The problem was that transactions were committed after @@ -33,6 +36,13 @@ class TestTransactionClosing(IgnorePendingDeprecationWarningsMixin, TransactionT when they should be, and aren't left pending after operations have been performed in them. Refs #9964. """ + + available_apps = [ + 'transactions_regress', + 'django.contrib.auth', + 'django.contrib.contenttypes', + ] + def test_raw_committed_on_success(self): """ Make sure a transaction consisting of raw SQL execution gets @@ -188,6 +198,9 @@ class TestNewConnection(IgnorePendingDeprecationWarningsMixin, TransactionTestCa """ Check that new connections don't have special behaviour. """ + + available_apps = ['transactions_regress'] + def setUp(self): self._old_backend = connections[DEFAULT_DB_ALIAS] settings = self._old_backend.settings_dict.copy() @@ -235,6 +248,9 @@ class TestPostgresAutocommitAndIsolation(IgnorePendingDeprecationWarningsMixin, is restored after entering and leaving transaction management. Refs #16047, #18130. """ + + available_apps = ['transactions_regress'] + def setUp(self): from psycopg2.extensions import (ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_SERIALIZABLE, @@ -311,6 +327,9 @@ class TestPostgresAutocommitAndIsolation(IgnorePendingDeprecationWarningsMixin, class TestManyToManyAddTransaction(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): + + available_apps = ['transactions_regress'] + def test_manyrelated_add_commit(self): "Test for https://code.djangoproject.com/ticket/16818" a = M2mA.objects.create() @@ -327,6 +346,8 @@ class TestManyToManyAddTransaction(IgnorePendingDeprecationWarningsMixin, Transa class SavepointTest(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): + available_apps = ['transactions_regress'] + @skipIf(connection.vendor == 'sqlite', "SQLite doesn't support savepoints in managed mode") @skipUnlessDBFeature('uses_savepoints') diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index f54c796d30..3962f69288 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -192,6 +192,20 @@ class URLPatternReverse(TestCase): self.assertEqual('/%7Eme/places/1/', reverse('places', args=[1], prefix='/~me/')) + def test_patterns_reported(self): + # Regression for #17076 + try: + # this url exists, but requires an argument + reverse("people", args=[]) + except NoReverseMatch as e: + pattern_description = r"1 pattern(s) tried: ['people/(?P<name>\\w+)/$']" + self.assertIn(pattern_description, str(e)) + else: + # we can't use .assertRaises, since we want to inspect the + # exception + self.fail("Expected a NoReverseMatch, but none occurred.") + + class ResolverTests(unittest.TestCase): def test_resolver_repr(self): """ diff --git a/tests/validation/models.py b/tests/validation/models.py index e95a1e0744..958740dd2d 100644 --- a/tests/validation/models.py +++ b/tests/validation/models.py @@ -19,6 +19,7 @@ class ModelToValidate(models.Model): email = models.EmailField(blank=True) url = models.URLField(blank=True) f_with_custom_validator = models.IntegerField(blank=True, null=True, validators=[validate_answer_to_universe]) + slug = models.SlugField(blank=True) def clean(self): super(ModelToValidate, self).clean() diff --git a/tests/validation/tests.py b/tests/validation/tests.py index 9ddf796c2b..c8b679541a 100644 --- a/tests/validation/tests.py +++ b/tests/validation/tests.py @@ -53,7 +53,11 @@ class BaseModelValidationTests(ValidationTestCase): def test_text_greater_that_charfields_max_length_raises_erros(self): mtv = ModelToValidate(number=10, name='Some Name'*100) - self.assertFailsValidation(mtv.full_clean, ['name',]) + self.assertFailsValidation(mtv.full_clean, ['name']) + + def test_malformed_slug_raises_error(self): + mtv = ModelToValidate(number=10, name='Some Name', slug='##invalid##') + self.assertFailsValidation(mtv.full_clean, ['slug']) class ArticleForm(forms.ModelForm): diff --git a/tests/validators/tests.py b/tests/validators/tests.py index 6b46c53cc3..a1555d8e91 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -213,9 +213,9 @@ class TestSimpleValidators(TestCase): self.assertEqual(repr(v), str_prefix("ValidationError([%(_)s'First Problem', %(_)s'Second Problem'])")) def test_message_dict(self): - v = ValidationError({'first': 'First Problem'}) - self.assertEqual(str(v), str_prefix("{%(_)s'first': %(_)s'First Problem'}")) - self.assertEqual(repr(v), str_prefix("ValidationError({%(_)s'first': %(_)s'First Problem'})")) + v = ValidationError({'first': ['First Problem']}) + self.assertEqual(str(v), str_prefix("{%(_)s'first': [%(_)s'First Problem']}")) + self.assertEqual(repr(v), str_prefix("ValidationError({%(_)s'first': [%(_)s'First Problem']})")) test_counter = 0 for validator, value, expected in TEST_DATA: diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index 2da17dd0f5..b35b192fd0 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -183,6 +183,8 @@ skip_selenium = not os.environ.get('DJANGO_SELENIUM_TESTS', False) @unittest.skipIf(skip_selenium, 'Selenium tests not requested') @unittest.skipUnless(firefox, 'Selenium not installed') class JavascriptI18nTests(LiveServerTestCase): + + available_apps = [] urls = 'view_tests.urls' @classmethod |
