diff options
| author | Andrew Godwin <andrew@aeracode.org> | 2013-08-09 14:17:30 +0100 |
|---|---|---|
| committer | Andrew Godwin <andrew@aeracode.org> | 2013-08-09 14:17:30 +0100 |
| commit | de64c4d6e97c980fb4c0ace045fc4070b3f763d9 (patch) | |
| tree | 045c8af6a88fa964cf7e284970a6e93d53941a79 /tests | |
| parent | fddc5957c53bd654312c4a238a8cdcfe5f4ef4cc (diff) | |
| parent | b575d690bbc1c4cd7f575346132c09fca8c736a7 (diff) | |
Merge remote-tracking branch 'core/master' into schema-alteration
Conflicts:
django/core/management/commands/flush.py
django/core/management/commands/syncdb.py
django/db/models/loading.py
docs/internals/deprecation.txt
docs/ref/django-admin.txt
docs/releases/1.7.txt
Diffstat (limited to 'tests')
256 files changed, 1600 insertions, 539 deletions
diff --git a/tests/admin_changelist/admin.py b/tests/admin_changelist/admin.py index 175b1972c9..a63f2b60ce 100644 --- a/tests/admin_changelist/admin.py +++ b/tests/admin_changelist/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib import admin from django.core.paginator import Paginator @@ -107,3 +105,10 @@ class DynamicListFilterChildAdmin(admin.ModelAdmin): my_list_filter.remove('parent') return my_list_filter +class DynamicSearchFieldsChildAdmin(admin.ModelAdmin): + search_fields = ('name',) + + def get_search_fields(self, request): + search_fields = super(DynamicSearchFieldsChildAdmin, self).get_search_fields(request) + search_fields += ('age',) + return search_fields diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index 7f3f0d162e..cb1ac5039f 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime @@ -18,7 +18,8 @@ from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin, GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin, DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin, FilteredChildAdmin, CustomPaginator, site as custom_site, - SwallowAdmin, DynamicListFilterChildAdmin, InvitationAdmin) + SwallowAdmin, DynamicListFilterChildAdmin, InvitationAdmin, + DynamicSearchFieldsChildAdmin) from .models import (Event, Child, Parent, Genre, Band, Musician, Group, Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow, UnorderedObject, OrderedObject, CustomIdUser) @@ -91,7 +92,7 @@ class ChangeListTests(TestCase): context = Context({'cl': cl}) table_output = template.render(context) link = reverse('admin:admin_changelist_child_change', args=(new_child.id,)) - row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % link + row_html = '<tbody><tr class="row1"><th class="field-name"><a href="%s">name</a></th><td class="field-parent nowrap">(None)</td></tr></tbody>' % link self.assertFalse(table_output.find(row_html) == -1, 'Failed to find expected row element: %s' % table_output) @@ -114,7 +115,7 @@ class ChangeListTests(TestCase): context = Context({'cl': cl}) table_output = template.render(context) link = reverse('admin:admin_changelist_child_change', args=(new_child.id,)) - row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % link + row_html = '<tbody><tr class="row1"><th class="field-name"><a href="%s">name</a></th><td class="field-parent nowrap">Parent object</td></tr></tbody>' % link self.assertFalse(table_output.find(row_html) == -1, 'Failed to find expected row element: %s' % table_output) @@ -150,7 +151,7 @@ class ChangeListTests(TestCase): # make sure that list editable fields are rendered in divs correctly editable_name_field = '<input name="form-0-name" value="name" class="vTextField" maxlength="30" type="text" id="id_form-0-name" />' - self.assertInHTML('<td>%s</td>' % editable_name_field, table_output, msg_prefix='Failed to find "name" list_editable field') + self.assertInHTML('<td class="field-name">%s</td>' % editable_name_field, table_output, msg_prefix='Failed to find "name" list_editable field') def test_result_list_editable(self): """ @@ -588,6 +589,13 @@ class ChangeListTests(TestCase): response = m.changelist_view(request) self.assertEqual(response.context_data['cl'].list_filter, ('parent', 'name', 'age')) + def test_dynamic_search_fields(self): + child = self._create_superuser('child') + m = DynamicSearchFieldsChildAdmin(Child, admin.site) + request = self._mocked_authenticated_request('/child/', child) + response = m.changelist_view(request) + self.assertEqual(response.context_data['cl'].search_fields, ('name', 'age')) + def test_pagination_page_range(self): """ Regression tests for ticket #15653: ensure the number of pages diff --git a/tests/admin_custom_urls/tests.py b/tests/admin_custom_urls/tests.py index 31c93410f4..257638afb1 100644 --- a/tests/admin_custom_urls/tests.py +++ b/tests/admin_custom_urls/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import warnings from django.contrib.admin.util import quote diff --git a/tests/admin_docs/urls.py b/tests/admin_docs/urls.py index 3c3a8fe5d8..914b4836e0 100644 --- a/tests/admin_docs/urls.py +++ b/tests/admin_docs/urls.py @@ -1,6 +1,3 @@ -# coding: utf-8 -from __future__ import absolute_import - from django.conf.urls import patterns from . import views diff --git a/tests/admin_filters/tests.py b/tests/admin_filters/tests.py index f05e8e2011..5e6b122fec 100644 --- a/tests/admin_filters/tests.py +++ b/tests/admin_filters/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime diff --git a/tests/admin_inlines/admin.py b/tests/admin_inlines/admin.py index 62f9e04e5b..c69800851a 100644 --- a/tests/admin_inlines/admin.py +++ b/tests/admin_inlines/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib import admin from django import forms diff --git a/tests/admin_inlines/tests.py b/tests/admin_inlines/tests.py index 465b224d4f..f62a0c1e01 100644 --- a/tests/admin_inlines/tests.py +++ b/tests/admin_inlines/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.admin.helpers import InlineAdminForm diff --git a/tests/admin_inlines/urls.py b/tests/admin_inlines/urls.py index cf18ef97cf..a5d927e20b 100644 --- a/tests/admin_inlines/urls.py +++ b/tests/admin_inlines/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, include from . import admin diff --git a/tests/admin_ordering/tests.py b/tests/admin_ordering/tests.py index 6655ad37ad..763e97bd72 100644 --- a/tests/admin_ordering/tests.py +++ b/tests/admin_ordering/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.test import TestCase, RequestFactory from django.contrib import admin diff --git a/tests/admin_registration/tests.py b/tests/admin_registration/tests.py index 1b2d291691..0e444fd2af 100644 --- a/tests/admin_registration/tests.py +++ b/tests/admin_registration/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.contrib import admin from django.core.exceptions import ImproperlyConfigured diff --git a/tests/admin_scripts/complex_app/admin/foo.py b/tests/admin_scripts/complex_app/admin/foo.py index 1ed704a66b..09ceba05aa 100644 --- a/tests/admin_scripts/complex_app/admin/foo.py +++ b/tests/admin_scripts/complex_app/admin/foo.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib import admin from ..models.foo import Foo diff --git a/tests/admin_scripts/complex_app/models/bar.py b/tests/admin_scripts/complex_app/models/bar.py index 15956f7a50..92f1b98694 100644 --- a/tests/admin_scripts/complex_app/models/bar.py +++ b/tests/admin_scripts/complex_app/models/bar.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.db import models from ..admin import foo diff --git a/tests/admin_scripts/management/commands/color_command.py b/tests/admin_scripts/management/commands/color_command.py new file mode 100644 index 0000000000..02da0a14ab --- /dev/null +++ b/tests/admin_scripts/management/commands/color_command.py @@ -0,0 +1,9 @@ +from django.core.management.base import NoArgsCommand + + +class Command(NoArgsCommand): + help = "Test color output" + requires_model_validation = False + + def handle_noargs(self, **options): + return self.style.SQL_KEYWORD('BEGIN') diff --git a/tests/admin_scripts/simple_app/models.py b/tests/admin_scripts/simple_app/models.py index b89f4b898b..e5b9e297c5 100644 --- a/tests/admin_scripts/simple_app/models.py +++ b/tests/admin_scripts/simple_app/models.py @@ -1,3 +1 @@ -from __future__ import absolute_import - from ..complex_app.models.bar import Bar diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 28f2dcb841..810c90c53e 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -18,7 +18,7 @@ import unittest import django from django import conf, get_version from django.conf import settings -from django.core.management import BaseCommand, CommandError +from django.core.management import BaseCommand, CommandError, call_command from django.db import connection from django.test.runner import DiscoverRunner from django.utils.encoding import force_text @@ -45,6 +45,7 @@ class AdminScriptTestCase(unittest.TestCase): settings_file_path = os.path.join(test_dir, filename) with open(settings_file_path, 'w') as settings_file: + settings_file.write('# -*- coding: utf-8 -*\n') settings_file.write('# Settings file automatically generated by admin_scripts test case\n') exports = [ 'DATABASES', @@ -921,14 +922,21 @@ class ManageAlternateSettings(AdminScriptTestCase): "alternate: manage.py can execute user commands if settings are provided as argument" args = ['noargs_command', '--settings=alternate_settings'] out, err = self.run_manage(args) - self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") self.assertNoOutput(err) def test_custom_command_with_environment(self): "alternate: manage.py can execute user commands if settings are provided in environment" args = ['noargs_command'] out, err = self.run_manage(args, 'alternate_settings') - self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertNoOutput(err) + + def test_custom_command_output_color(self): + "alternate: manage.py output syntax color can be deactivated with the `--no-color` option" + args = ['noargs_command', '--no-color', '--settings=alternate_settings'] + out, err = self.run_manage(args) + self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', True), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") self.assertNoOutput(err) @@ -1271,40 +1279,47 @@ class CommandTypes(AdminScriptTestCase): self.assertNoOutput(err) self.assertOutput(out, "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s).") + def test_no_color(self): + "--no-color prevent colorization of the output" + out = StringIO() + + call_command('color_command', no_color=True, stdout=out) + self.assertEqual(out.getvalue(), 'BEGIN\n') + def test_base_command(self): "User BaseCommands can execute when a label is provided" args = ['base_command', 'testlabel'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_base_command_no_label(self): "User BaseCommands can execute when no labels are provided" args = ['base_command'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('no_color', False), ('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_base_command_multiple_label(self): "User BaseCommands can execute when no labels are provided" args = ['base_command', 'testlabel', 'anotherlabel'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('no_color', False), ('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_base_command_with_option(self): "User BaseCommands can execute with options when a label is provided" args = ['base_command', 'testlabel', '--option_a=x'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_base_command_with_options(self): "User BaseCommands can execute with multiple options when a label is provided" args = ['base_command', 'testlabel', '-a', 'x', '--option_b=y'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_base_run_from_argv(self): """ @@ -1350,7 +1365,7 @@ class CommandTypes(AdminScriptTestCase): args = ['noargs_command'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_noargs_with_args(self): "NoArg Commands raise an error if an argument is provided" @@ -1365,7 +1380,7 @@ class CommandTypes(AdminScriptTestCase): self.assertNoOutput(err) self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'") self.assertOutput(out, os.sep.join(['django', 'contrib', 'auth', 'models.py'])) - self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "'>, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_app_command_no_apps(self): "User AppCommands raise an error when no app name is provided" @@ -1380,10 +1395,10 @@ class CommandTypes(AdminScriptTestCase): self.assertNoOutput(err) self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'") self.assertOutput(out, os.sep.join(['django', 'contrib', 'auth', 'models.py'])) - self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "'>, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.contenttypes.models'") self.assertOutput(out, os.sep.join(['django', 'contrib', 'contenttypes', 'models.py'])) - self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "'>, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_app_command_invalid_appname(self): "User AppCommands can execute when a single app name is provided" @@ -1402,7 +1417,7 @@ class CommandTypes(AdminScriptTestCase): args = ['label_command', 'testlabel'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") def test_label_command_no_label(self): "User LabelCommands raise an error if no label is provided" @@ -1415,8 +1430,8 @@ class CommandTypes(AdminScriptTestCase): args = ['label_command', 'testlabel', 'anotherlabel'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") - self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('no_color', False), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]") class ArgumentOrder(AdminScriptTestCase): """Tests for 2-stage argument parsing scheme. @@ -1440,35 +1455,35 @@ class ArgumentOrder(AdminScriptTestCase): args = ['base_command', 'testlabel', '--settings=alternate_settings', '--option_a=x'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") def test_setting_then_short_option(self): "Short options passed after settings are correctly handled" args = ['base_command', 'testlabel', '--settings=alternate_settings', '--option_a=x'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") def test_option_then_setting(self): "Options passed before settings are correctly handled" args = ['base_command', 'testlabel', '--option_a=x', '--settings=alternate_settings'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") def test_short_option_then_setting(self): "Short options passed before settings are correctly handled" args = ['base_command', 'testlabel', '-a', 'x', '--settings=alternate_settings'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") def test_option_then_setting_then_option(self): "Options are correctly handled when they are passed before and after a setting" args = ['base_command', 'testlabel', '--option_a=x', '--settings=alternate_settings', '--option_b=y'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('no_color', False), ('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]") class StartProject(LiveServerTestCase, AdminScriptTestCase): diff --git a/tests/admin_util/tests.py b/tests/admin_util/tests.py index 637f643261..8c63e90ce1 100644 --- a/tests/admin_util/tests.py +++ b/tests/admin_util/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from datetime import datetime @@ -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_validation/tests.py b/tests/admin_validation/tests.py index 5eee3e7105..39e74a945c 100644 --- a/tests/admin_validation/tests.py +++ b/tests/admin_validation/tests.py @@ -1,9 +1,10 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django import forms from django.contrib import admin from django.core.exceptions import ImproperlyConfigured from django.test import TestCase +from django.test.utils import str_prefix from .models import Song, Book, Album, TwoAlbumFKAndAnE, State, City @@ -185,7 +186,8 @@ class ValidationTestCase(TestCase): readonly_fields = ("title", "nonexistant") self.assertRaisesMessage(ImproperlyConfigured, - "SongAdmin.readonly_fields[1], 'nonexistant' is not a callable or an attribute of 'SongAdmin' or found in the model 'Song'.", + str_prefix("SongAdmin.readonly_fields[1], %(_)s'nonexistant' is not a callable " + "or an attribute of 'SongAdmin' or found in the model 'Song'."), SongAdmin.validate, Song) @@ -195,7 +197,8 @@ class ValidationTestCase(TestCase): readonly_fields=['i_dont_exist'] # Missing attribute self.assertRaisesMessage(ImproperlyConfigured, - "CityInline.readonly_fields[0], 'i_dont_exist' is not a callable or an attribute of 'CityInline' or found in the model 'City'.", + str_prefix("CityInline.readonly_fields[0], %(_)s'i_dont_exist' is not a callable " + "or an attribute of 'CityInline' or found in the model 'City'."), CityInline.validate, City) diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index 039abb819b..df8ced949e 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import tempfile import os diff --git a/tests/admin_views/customadmin.py b/tests/admin_views/customadmin.py index c204b81edd..f964d6cffb 100644 --- a/tests/admin_views/customadmin.py +++ b/tests/admin_views/customadmin.py @@ -1,7 +1,7 @@ """ A second, custom AdminSite -- see tests.CustomAdminSiteTests. """ -from __future__ import absolute_import +from __future__ import unicode_literals from django.conf.urls import patterns from django.contrib import admin @@ -13,6 +13,7 @@ from . import models, forms, admin as base_admin class Admin2(admin.AdminSite): + app_index_template = 'custom_admin/app_index.html' login_form = forms.CustomAdminAuthenticationForm login_template = 'custom_admin/login.html' logout_template = 'custom_admin/logout.html' diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index 5ec4fbb544..a1131210d7 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -644,8 +644,8 @@ class MainPrepopulated(models.Model): max_length=20, choices=(('option one', 'Option One'), ('option two', 'Option Two'))) - slug1 = models.SlugField() - slug2 = models.SlugField() + slug1 = models.SlugField(blank=True) + slug2 = models.SlugField(blank=True) class RelatedPrepopulated(models.Model): parent = models.ForeignKey(MainPrepopulated) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 7decf6f471..3ee06751fb 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -1,5 +1,5 @@ # coding: utf-8 -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import os import re @@ -806,6 +806,12 @@ class CustomModelAdminTest(AdminViewBasicTestCase): self.assertTemplateUsed(response, 'custom_admin/index.html') self.assertContains(response, 'Hello from a custom index template *bar*') + def testCustomAdminSiteAppIndexViewandTemplate(self): + response = self.client.get('/test_admin/admin2/admin_views/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/app_index.html') + self.assertContains(response, 'Hello from a custom app_index template') + def testCustomAdminSitePasswordChangeTemplate(self): response = self.client.get('/test_admin/admin2/password_change/') self.assertIsInstance(response, TemplateResponse) @@ -1496,7 +1502,7 @@ class AdminViewStringPrimaryKeyTest(TestCase): response = self.client.get(prefix) # this URL now comes through reverse(), thus url quoting and iri_to_uri encoding pk_final_url = escape(iri_to_uri(urlquote(quote(self.pk)))) - should_contain = """<th><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, escape(self.pk)) + should_contain = """<th class="field-__str__"><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, escape(self.pk)) self.assertContains(response, should_contain) def test_recentactions_link(self): @@ -1527,6 +1533,17 @@ class AdminViewStringPrimaryKeyTest(TestCase): self.assertEqual(counted_presence_before - 1, counted_presence_after) + def test_logentry_get_admin_url(self): + "LogEntry.get_admin_url returns a URL to edit the entry's object or None for non-existent (possibly deleted) models" + log_entry_name = "Model with string primary key" # capitalized in Recent Actions + logentry = LogEntry.objects.get(content_type__name__iexact=log_entry_name) + model = "modelwithstringprimarykey" + desired_admin_url = "/test_admin/admin/admin_views/%s/%s/" % (model, escape(iri_to_uri(urlquote(quote(self.pk))))) + self.assertEqual(logentry.get_admin_url(), desired_admin_url) + + logentry.content_type.model = "non-existent" + self.assertEqual(logentry.get_admin_url(), None) + def test_deleteconfirmation_link(self): "The link from the delete confirmation page referring back to the changeform of the object should be quoted" response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/delete/' % quote(self.pk)) @@ -2151,8 +2168,8 @@ class AdminViewListEditable(TestCase): self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table. self.assertContains(response, 'id="id_form-1-id"', 1) self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True) - self.assertContains(response, '<td>%d</td>' % story1.id, 1) - self.assertContains(response, '<td>%d</td>' % story2.id, 1) + self.assertContains(response, '<td class="field-id">%d</td>' % story1.id, 1) + self.assertContains(response, '<td class="field-id">%d</td>' % story2.id, 1) def test_pk_hidden_fields_with_list_display_links(self): """ Similarly as test_pk_hidden_fields, but when the hidden pk fields are @@ -2167,8 +2184,8 @@ class AdminViewListEditable(TestCase): self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table. self.assertContains(response, 'id="id_form-1-id"', 1) self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True) - self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link1, story1.id), 1) - self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link2, story2.id), 1) + self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link1, story1.id), 1) + self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link2, story2.id), 1) @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) @@ -3438,6 +3455,50 @@ class SeleniumAdminViewsFirefoxTests(AdminSeleniumWebDriverTestCase): slug2='option-one-tabular-inline-ignored-characters', ) + def test_populate_existing_object(self): + """ + Ensure that the prepopulation works for existing objects too, as long + as the original field is empty. + Refs #19082. + """ + # Slugs are empty to start with. + item = MainPrepopulated.objects.create( + name=' this is the mAin nÀMë', + pubdate='2012-02-18', + status='option two', + slug1='', + slug2='', + ) + self.admin_login(username='super', + password='secret', + login_url='/test_admin/admin/') + + object_url = '%s%s' % ( + self.live_server_url, + '/test_admin/admin/admin_views/mainprepopulated/{}/'.format(item.id)) + + self.selenium.get(object_url) + self.selenium.find_element_by_css_selector('#id_name').send_keys(' the best') + + # The slugs got prepopulated since they were originally empty + slug1 = self.selenium.find_element_by_css_selector('#id_slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_slug2').get_attribute('value') + self.assertEqual(slug1, 'main-name-best-2012-02-18') + self.assertEqual(slug2, 'option-two-main-name-best') + + # Save the object + self.selenium.find_element_by_xpath('//input[@value="Save"]').click() + self.wait_page_loaded() + + self.selenium.get(object_url) + self.selenium.find_element_by_css_selector('#id_name').send_keys(' hello') + + # The slugs got prepopulated didn't change since they were originally not empty + slug1 = self.selenium.find_element_by_css_selector('#id_slug1').get_attribute('value') + slug2 = self.selenium.find_element_by_css_selector('#id_slug2').get_attribute('value') + self.assertEqual(slug1, 'main-name-best-2012-02-18') + self.assertEqual(slug2, 'option-two-main-name-best') + def test_collapsible_fieldset(self): """ Test that the 'collapse' class in fieldsets definition allows to @@ -3877,6 +3938,22 @@ class CSSTest(TestCase): self.assertContains(response, '<body class="app-admin_views model-section ') + def test_changelist_field_classes(self): + """ + Cells of the change list table should contain the field name in their class attribute + Refs #11195. + """ + Podcast.objects.create(name="Django Dose", + release_date=datetime.date.today()) + response = self.client.get('/test_admin/admin/admin_views/podcast/') + self.assertContains( + response, '<th class="field-name">') + self.assertContains( + response, '<td class="field-release_date nowrap">') + self.assertContains( + response, '<td class="action-checkbox">') + + try: import docutils except ImportError: diff --git a/tests/admin_views/urls.py b/tests/admin_views/urls.py index 763c83a450..d934173234 100644 --- a/tests/admin_views/urls.py +++ b/tests/admin_views/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, include from . import views, customadmin, admin diff --git a/tests/admin_widgets/models.py b/tests/admin_widgets/models.py index ae19d58cc4..cb97242e03 100644 --- a/tests/admin_widgets/models.py +++ b/tests/admin_widgets/models.py @@ -13,6 +13,7 @@ class Member(models.Model): name = models.CharField(max_length=100) birthdate = models.DateTimeField(blank=True, null=True) gender = models.CharField(max_length=1, blank=True, choices=[('M','Male'), ('F', 'Female')]) + email = models.EmailField(blank=True) def __str__(self): return self.name @@ -55,7 +56,8 @@ class Inventory(models.Model): return self.name class Event(models.Model): - band = models.ForeignKey(Band, limit_choices_to=models.Q(pk__gt=0)) + main_band = models.ForeignKey(Band, limit_choices_to=models.Q(pk__gt=0), related_name='events_main_band_at') + supporting_bands = models.ManyToManyField(Band, null=True, blank=True, related_name='events_supporting_band_at') start_date = models.DateField(blank=True, null=True) start_time = models.TimeField(blank=True, null=True) description = models.TextField(blank=True) diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 870aa6d455..5a88df1e57 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -1,5 +1,5 @@ # encoding: utf-8 -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from datetime import datetime, timedelta from unittest import TestCase @@ -83,19 +83,22 @@ class AdminFormfieldForDBFieldTests(TestCase): def testCharField(self): self.assertFormfield(models.Member, 'name', widgets.AdminTextInputWidget) + def testEmailField(self): + self.assertFormfield(models.Member, 'email', widgets.AdminEmailInputWidget) + def testFileField(self): self.assertFormfield(models.Album, 'cover_art', widgets.AdminFileWidget) def testForeignKey(self): - self.assertFormfield(models.Event, 'band', forms.Select) + self.assertFormfield(models.Event, 'main_band', forms.Select) def testRawIDForeignKey(self): - self.assertFormfield(models.Event, 'band', widgets.ForeignKeyRawIdWidget, - raw_id_fields=['band']) + self.assertFormfield(models.Event, 'main_band', widgets.ForeignKeyRawIdWidget, + raw_id_fields=['main_band']) def testRadioFieldsForeignKey(self): - ff = self.assertFormfield(models.Event, 'band', widgets.AdminRadioSelect, - radio_fields={'band':admin.VERTICAL}) + ff = self.assertFormfield(models.Event, 'main_band', widgets.AdminRadioSelect, + radio_fields={'main_band':admin.VERTICAL}) self.assertEqual(ff.empty_label, None) def testManyToMany(self): @@ -198,7 +201,7 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase): pk = band.pk band.delete() post_data = { - "band": '%s' % pk, + "main_band": '%s' % pk, } # Try posting with a non-existent pk in a raw id field: this # should result in an error message, not a server exception. @@ -212,7 +215,7 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase): for test_str in ('Iñtërnâtiônàlizætiøn', "1234'", -1234): # This should result in an error message, not a server exception. response = self.client.post('%s/admin_widgets/event/add/' % self.admin_root, - {"band": test_str}) + {"main_band": test_str}) self.assertContains(response, 'Select a valid choice. That choice is not one of the available choices.') @@ -223,6 +226,13 @@ class AdminForeignKeyRawIdWidget(DjangoTestCase): self.assertEqual(lookup1, {'color__in': 'red,blue'}) self.assertEqual(lookup1, lookup2) + def test_url_params_from_lookup_dict_callable(self): + def my_callable(): + return 'works' + lookup1 = widgets.url_params_from_lookup_dict({'myfield': my_callable}) + lookup2 = widgets.url_params_from_lookup_dict({'myfield': my_callable()}) + self.assertEqual(lookup1, lookup2) + class FilteredSelectMultipleWidgetTest(DjangoTestCase): def test_render(self): @@ -300,29 +310,29 @@ class AdminURLWidgetTest(DjangoTestCase): w = widgets.AdminURLFieldWidget() self.assertHTMLEqual( conditional_escape(w.render('test', '')), - '<input class="vURLField" name="test" type="text" />' + '<input class="vURLField" name="test" type="url" />' ) self.assertHTMLEqual( conditional_escape(w.render('test', 'http://example.com')), - '<p class="url">Currently:<a href="http://example.com">http://example.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com" /></p>' + '<p class="url">Currently:<a href="http://example.com">http://example.com</a><br />Change:<input class="vURLField" name="test" type="url" value="http://example.com" /></p>' ) def test_render_idn(self): w = widgets.AdminURLFieldWidget() self.assertHTMLEqual( conditional_escape(w.render('test', 'http://example-äüö.com')), - '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com" /></p>' + '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="url" value="http://example-äüö.com" /></p>' ) def test_render_quoting(self): w = widgets.AdminURLFieldWidget() self.assertHTMLEqual( conditional_escape(w.render('test', 'http://example.com/<sometag>some text</sometag>')), - '<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com/<sometag>some text</sometag>" /></p>' + '<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="url" value="http://example.com/<sometag>some text</sometag>" /></p>' ) self.assertHTMLEqual( conditional_escape(w.render('test', 'http://example-äüö.com/<sometag>some text</sometag>')), - '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>' + '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="url" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>' ) @@ -816,3 +826,100 @@ class HorizontalVerticalFilterSeleniumChromeTests(HorizontalVerticalFilterSeleni class HorizontalVerticalFilterSeleniumIETests(HorizontalVerticalFilterSeleniumFirefoxTests): webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver' + + +@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) +class AdminRawIdWidgetSeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): + 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): + models.Band.objects.create(id=42, name='Bogey Blues') + models.Band.objects.create(id=98, name='Green Potatoes') + super(AdminRawIdWidgetSeleniumFirefoxTests, self).setUp() + + def test_foreignkey(self): + self.admin_login(username='super', password='secret', login_url='/') + self.selenium.get( + '%s%s' % (self.live_server_url, '/admin_widgets/event/add/')) + main_window = self.selenium.current_window_handle + + # No value has been selected yet + self.assertEqual( + self.selenium.find_element_by_id('id_main_band').get_attribute('value'), + '') + + # Open the popup window and click on a band + self.selenium.find_element_by_id('lookup_id_main_band').click() + self.selenium.switch_to_window('id_main_band') + self.wait_page_loaded() + link = self.selenium.find_element_by_link_text('Bogey Blues') + self.assertTrue('/band/42/' in link.get_attribute('href')) + link.click() + + # The field now contains the selected band's id + self.selenium.switch_to_window(main_window) + self.assertEqual( + self.selenium.find_element_by_id('id_main_band').get_attribute('value'), + '42') + + # Reopen the popup window and click on another band + self.selenium.find_element_by_id('lookup_id_main_band').click() + self.selenium.switch_to_window('id_main_band') + self.wait_page_loaded() + link = self.selenium.find_element_by_link_text('Green Potatoes') + self.assertTrue('/band/98/' in link.get_attribute('href')) + link.click() + + # The field now contains the other selected band's id + self.selenium.switch_to_window(main_window) + self.assertEqual( + self.selenium.find_element_by_id('id_main_band').get_attribute('value'), + '98') + + def test_many_to_many(self): + self.admin_login(username='super', password='secret', login_url='/') + self.selenium.get( + '%s%s' % (self.live_server_url, '/admin_widgets/event/add/')) + main_window = self.selenium.current_window_handle + + # No value has been selected yet + self.assertEqual( + self.selenium.find_element_by_id('id_supporting_bands').get_attribute('value'), + '') + + # Open the popup window and click on a band + self.selenium.find_element_by_id('lookup_id_supporting_bands').click() + self.selenium.switch_to_window('id_supporting_bands') + self.wait_page_loaded() + link = self.selenium.find_element_by_link_text('Bogey Blues') + self.assertTrue('/band/42/' in link.get_attribute('href')) + link.click() + + # The field now contains the selected band's id + self.selenium.switch_to_window(main_window) + self.assertEqual( + self.selenium.find_element_by_id('id_supporting_bands').get_attribute('value'), + '42') + + # Reopen the popup window and click on another band + self.selenium.find_element_by_id('lookup_id_supporting_bands').click() + self.selenium.switch_to_window('id_supporting_bands') + self.wait_page_loaded() + link = self.selenium.find_element_by_link_text('Green Potatoes') + self.assertTrue('/band/98/' in link.get_attribute('href')) + link.click() + + # The field now contains the two selected bands' ids + self.selenium.switch_to_window(main_window) + self.assertEqual( + self.selenium.find_element_by_id('id_supporting_bands').get_attribute('value'), + '42,98') + +class AdminRawIdWidgetSeleniumChromeTests(AdminRawIdWidgetSeleniumFirefoxTests): + webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver' + +class AdminRawIdWidgetSeleniumIETests(AdminRawIdWidgetSeleniumFirefoxTests): + webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver' diff --git a/tests/admin_widgets/urls.py b/tests/admin_widgets/urls.py index aecee90b7f..3da5d25bc8 100644 --- a/tests/admin_widgets/urls.py +++ b/tests/admin_widgets/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, include from . import widgetadmin diff --git a/tests/admin_widgets/widgetadmin.py b/tests/admin_widgets/widgetadmin.py index 1cdeeb9f67..4894f92afb 100644 --- a/tests/admin_widgets/widgetadmin.py +++ b/tests/admin_widgets/widgetadmin.py @@ -1,8 +1,3 @@ -""" - -""" -from __future__ import absolute_import - from django.contrib import admin from . import models @@ -23,7 +18,7 @@ class CarTireAdmin(admin.ModelAdmin): return super(CarTireAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs) class EventAdmin(admin.ModelAdmin): - raw_id_fields = ['band'] + raw_id_fields = ['main_band', 'supporting_bands'] class SchoolAdmin(admin.ModelAdmin): @@ -47,4 +42,4 @@ site.register(models.Bee) site.register(models.Advisor) -site.register(models.School, SchoolAdmin)
\ No newline at end of file +site.register(models.School, SchoolAdmin) diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index c635e6ebb6..4d46cae766 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import datetime from decimal import Decimal @@ -585,3 +585,35 @@ class BaseAggregateTestCase(TestCase): "datetime.date(2008, 1, 1)" ] ) + + def test_values_aggregation(self): + # Refs #20782 + max_rating = Book.objects.values('rating').aggregate(max_rating=Max('rating')) + self.assertEqual(max_rating['max_rating'], 5) + max_books_per_rating = Book.objects.values('rating').annotate( + books_per_rating=Count('id') + ).aggregate(Max('books_per_rating')) + self.assertEqual( + max_books_per_rating, + {'books_per_rating__max': 3}) + + def test_ticket17424(self): + """ + Check that doing exclude() on a foreign model after annotate() + doesn't crash. + """ + all_books = list(Book.objects.values_list('pk', flat=True).order_by('pk')) + annotated_books = Book.objects.order_by('pk').annotate(one=Count("id")) + + # The value doesn't matter, we just need any negative + # constraint on a related model that's a noop. + excluded_books = annotated_books.exclude(publisher__name="__UNLIKELY_VALUE__") + + # Try to generate query tree + str(excluded_books.query) + + self.assertQuerysetEqual(excluded_books, all_books, lambda x: x.pk) + + # Check internal state + self.assertIsNone(annotated_books.query.alias_map["aggregation_book"].join_type) + self.assertIsNone(excluded_books.query.alias_map["aggregation_book"].join_type) diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py index 35ac57d9be..8388e78dbe 100644 --- a/tests/aggregation_regress/tests.py +++ b/tests/aggregation_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime import pickle @@ -250,6 +250,13 @@ class AggregationTests(TestCase): 'price__max': Decimal("82.80") }) + # Regression for #15624 - Missing SELECT columns when using values, annotate + # and aggregate in a single query + self.assertEqual( + Book.objects.annotate(c=Count('authors')).values('c').aggregate(Max('c')), + {'c__max': 3} + ) + def test_field_error(self): # Bad field requests in aggregates are caught and reported self.assertRaises( diff --git a/tests/app_loading/tests.py b/tests/app_loading/tests.py index e0c7cbd78c..dc25899646 100644 --- a/tests/app_loading/tests.py +++ b/tests/app_loading/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import copy import os @@ -7,7 +7,8 @@ import time from unittest import TestCase from django.conf import Settings -from django.db.models.loading import cache, load_app, get_model, get_models +from django.db.models.loading import cache, load_app, get_model, get_models, AppCache +from django.test.utils import override_settings from django.utils._os import upath @@ -61,12 +62,33 @@ class EggLoadingTest(TestCase): egg_name = '%s/brokenapp.egg' % self.egg_dir sys.path.append(egg_name) self.assertRaises(ImportError, load_app, 'broken_app') + raised = None try: load_app('broken_app') except ImportError as e: - # Make sure the message is indicating the actual - # problem in the broken app. - self.assertTrue("modelz" in e.args[0]) + raised = e + + # Make sure the message is indicating the actual + # problem in the broken app. + self.assertTrue(raised is not None) + self.assertTrue("modelz" in raised.args[0]) + + def test_missing_app(self): + """ + Test that repeated app loading doesn't succeed in case there is an + error. Refs #17667. + """ + # AppCache is a Borg, so we can instantiate one and change its + # loaded to False to force the following code to actually try to + # populate the cache. + a = AppCache() + a.loaded = False + try: + with override_settings(INSTALLED_APPS=('notexists',)): + self.assertRaises(ImportError, get_model, 'notexists', 'nomodel', seed_cache=True) + self.assertRaises(ImportError, get_model, 'notexists', 'nomodel', seed_cache=True) + finally: + a.loaded = True class GetModelsTest(TestCase): diff --git a/tests/backends/models.py b/tests/backends/models.py index 4f03ddeacc..1508af4354 100644 --- a/tests/backends/models.py +++ b/tests/backends/models.py @@ -68,11 +68,18 @@ class Reporter(models.Model): return "%s %s" % (self.first_name, self.last_name) +class ReporterProxy(Reporter): + class Meta: + proxy = True + + @python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() reporter = models.ForeignKey(Reporter) + reporter_proxy = models.ForeignKey(ReporterProxy, null=True, + related_name='reporter_proxy') def __str__(self): return self.headline diff --git a/tests/backends/tests.py b/tests/backends/tests.py index ec592f0188..d6e3f5c78e 100644 --- a/tests/backends/tests.py +++ b/tests/backends/tests.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Unit and doctests for specific database backends. -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime from decimal import Decimal @@ -614,12 +614,19 @@ class FkConstraintsTests(TransactionTestCase): Try to create a model instance that violates a FK constraint. If it fails it should fail with IntegrityError. """ - a = models.Article(headline="This is a test", pub_date=datetime.datetime(2005, 7, 27), reporter_id=30) + a1 = models.Article(headline="This is a test", pub_date=datetime.datetime(2005, 7, 27), reporter_id=30) try: - a.save() + a1.save() except IntegrityError: - return - self.skipTest("This backend does not support integrity checks.") + pass + else: + self.skipTest("This backend does not support integrity checks.") + # Now that we know this backend supports integrity checks we make sure + # constraints are also enforced for proxy models. Refs #17519 + a2 = models.Article(headline='This is another test', reporter=self.r, + pub_date=datetime.datetime(2012, 8, 3), + reporter_proxy_id=30) + self.assertRaises(IntegrityError, a2.save) def test_integrity_checks_on_update(self): """ @@ -628,14 +635,26 @@ class FkConstraintsTests(TransactionTestCase): """ # Create an Article. models.Article.objects.create(headline="Test article", pub_date=datetime.datetime(2010, 9, 4), reporter=self.r) - # Retrive it from the DB - a = models.Article.objects.get(headline="Test article") - a.reporter_id = 30 + # Retrieve it from the DB + a1 = models.Article.objects.get(headline="Test article") + a1.reporter_id = 30 try: - a.save() + a1.save() except IntegrityError: - return - self.skipTest("This backend does not support integrity checks.") + pass + else: + self.skipTest("This backend does not support integrity checks.") + # Now that we know this backend supports integrity checks we make sure + # constraints are also enforced for proxy models. Refs #17519 + # Create another article + r_proxy = models.ReporterProxy.objects.get(pk=self.r.pk) + models.Article.objects.create(headline='Another article', + pub_date=datetime.datetime(1988, 5, 15), + reporter=self.r, reporter_proxy=r_proxy) + # Retreive the second article from the DB + a2 = models.Article.objects.get(headline='Another article') + a2.reporter_proxy_id = 30 + self.assertRaises(IntegrityError, a2.save) def test_disable_constraint_checks_manually(self): """ diff --git a/tests/basic/tests.py b/tests/basic/tests.py index fb21b11279..55ed6a436f 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from datetime import datetime import threading @@ -6,6 +6,7 @@ import threading from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.db import connections, DEFAULT_DB_ALIAS from django.db.models.fields import Field, FieldDoesNotExist +from django.db.models.manager import BaseManager from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet, MAX_GET_RESULTS from django.test import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature from django.utils import six @@ -734,3 +735,59 @@ class ConcurrentSaveTests(TransactionTestCase): t.join() a.save() self.assertEqual(Article.objects.get(pk=a.pk).headline, 'foo') + + +class ManagerTest(TestCase): + QUERYSET_PROXY_METHODS = [ + 'none', + 'count', + 'dates', + 'datetimes', + 'distinct', + 'extra', + 'get', + 'get_or_create', + 'update_or_create', + 'create', + 'bulk_create', + 'filter', + 'aggregate', + 'annotate', + 'complex_filter', + 'exclude', + 'in_bulk', + 'iterator', + 'earliest', + 'latest', + 'first', + 'last', + 'order_by', + 'select_for_update', + 'select_related', + 'prefetch_related', + 'values', + 'values_list', + 'update', + 'reverse', + 'defer', + 'only', + 'using', + 'exists', + '_insert', + '_update', + 'raw', + ] + + def test_manager_methods(self): + """ + This test ensures that the correct set of methods from `QuerySet` + are copied onto `Manager`. + + It's particularly useful to prevent accidentally leaking new methods + into `Manager`. New `QuerySet` methods that should also be copied onto + `Manager` will need to be added to `ManagerTest.QUERYSET_PROXY_METHODS`. + """ + self.assertEqual( + sorted(BaseManager._get_queryset_methods(QuerySet).keys()), + sorted(self.QUERYSET_PROXY_METHODS), + ) diff --git a/tests/bug639/tests.py b/tests/bug639/tests.py index 71c50e14aa..769d27d2f0 100644 --- a/tests/bug639/tests.py +++ b/tests/bug639/tests.py @@ -4,8 +4,6 @@ gets called *again* for each FileField. This test will fail if calling a ModelForm's save() method causes Model.save() to be called more than once. """ -from __future__ import absolute_import - import os import shutil import unittest diff --git a/tests/bug8245/admin.py b/tests/bug8245/admin.py index d821190763..e7d1a080c2 100644 --- a/tests/bug8245/admin.py +++ b/tests/bug8245/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib import admin from .models import Story diff --git a/tests/bulk_create/tests.py b/tests/bulk_create/tests.py index d4772934a1..94258ca711 100644 --- a/tests/bulk_create/tests.py +++ b/tests/bulk_create/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from operator import attrgetter diff --git a/tests/cache/tests.py b/tests/cache/tests.py index 5cf199794c..287a078617 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -2,7 +2,7 @@ # Unit tests for cache framework # Uses whatever cache backend is set in the test settings file. -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import hashlib import os diff --git a/tests/choices/tests.py b/tests/choices/tests.py index 03a7d3340d..2d612626c2 100644 --- a/tests/choices/tests.py +++ b/tests/choices/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.test import TestCase from .models import Person diff --git a/tests/comment_tests/tests/__init__.py b/tests/comment_tests/tests/__init__.py index ae4585e187..6cbddbe82b 100644 --- a/tests/comment_tests/tests/__init__.py +++ b/tests/comment_tests/tests/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib.auth.models import User from django.contrib.comments.forms import CommentForm from django.contrib.comments.models import Comment diff --git a/tests/comment_tests/tests/test_app_api.py b/tests/comment_tests/tests/test_app_api.py index ed23ba39cc..83ee868a02 100644 --- a/tests/comment_tests/tests/test_app_api.py +++ b/tests/comment_tests/tests/test_app_api.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf import settings from django.contrib import comments from django.contrib.comments.models import Comment diff --git a/tests/comment_tests/tests/test_comment_form.py b/tests/comment_tests/tests/test_comment_form.py index a30f13a073..bca339fd3d 100644 --- a/tests/comment_tests/tests/test_comment_form.py +++ b/tests/comment_tests/tests/test_comment_form.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import time from django.conf import settings diff --git a/tests/comment_tests/tests/test_comment_utils_moderators.py b/tests/comment_tests/tests/test_comment_utils_moderators.py index 6c7a882e25..e61be48d8b 100644 --- a/tests/comment_tests/tests/test_comment_utils_moderators.py +++ b/tests/comment_tests/tests/test_comment_utils_moderators.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib.comments.models import Comment from django.contrib.comments.moderation import (moderator, CommentModerator, AlreadyModerated) diff --git a/tests/comment_tests/tests/test_comment_view.py b/tests/comment_tests/tests/test_comment_view.py index 0d994d3af8..19d7c1d16b 100644 --- a/tests/comment_tests/tests/test_comment_view.py +++ b/tests/comment_tests/tests/test_comment_view.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import re diff --git a/tests/comment_tests/tests/test_feeds.py b/tests/comment_tests/tests/test_feeds.py index 941ffb6bf2..e6652afa28 100644 --- a/tests/comment_tests/tests/test_feeds.py +++ b/tests/comment_tests/tests/test_feeds.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from xml.etree import ElementTree as ET from django.conf import settings diff --git a/tests/comment_tests/tests/test_models.py b/tests/comment_tests/tests/test_models.py index 69c1a8118f..4303fda852 100644 --- a/tests/comment_tests/tests/test_models.py +++ b/tests/comment_tests/tests/test_models.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib.comments.models import Comment from . import CommentTestCase diff --git a/tests/comment_tests/tests/test_moderation_views.py b/tests/comment_tests/tests/test_moderation_views.py index 02af35cfe4..9f826f5866 100644 --- a/tests/comment_tests/tests/test_moderation_views.py +++ b/tests/comment_tests/tests/test_moderation_views.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.contrib.auth.models import User, Permission from django.contrib.comments import signals diff --git a/tests/comment_tests/tests/test_templatetags.py b/tests/comment_tests/tests/test_templatetags.py index 1971c21a58..d24859eb55 100644 --- a/tests/comment_tests/tests/test_templatetags.py +++ b/tests/comment_tests/tests/test_templatetags.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib.comments.forms import CommentForm from django.contrib.comments.models import Comment from django.contrib.contenttypes.models import ContentType diff --git a/tests/comment_tests/urls.py b/tests/comment_tests/urls.py index 0a7e8b5fdf..32106106b5 100644 --- a/tests/comment_tests/urls.py +++ b/tests/comment_tests/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url from django.contrib.comments.feeds import LatestCommentFeed diff --git a/tests/conditional_processing/views.py b/tests/conditional_processing/views.py index 14a5a83a45..d4dc4b8cf6 100644 --- a/tests/conditional_processing/views.py +++ b/tests/conditional_processing/views.py @@ -1,6 +1,3 @@ -# -*- coding:utf-8 -*- -from __future__ import absolute_import - from django.views.decorators.http import condition, etag, last_modified from django.http import HttpResponse diff --git a/tests/contenttypes_tests/models.py b/tests/contenttypes_tests/models.py index 3c6685687a..5d21ad5b96 100644 --- a/tests/contenttypes_tests/models.py +++ b/tests/contenttypes_tests/models.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible diff --git a/tests/contenttypes_tests/tests.py b/tests/contenttypes_tests/tests.py index b39e118ec6..63f02697df 100644 --- a/tests/contenttypes_tests/tests.py +++ b/tests/contenttypes_tests/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.contrib.contenttypes.models import ContentType from django.test import TestCase diff --git a/tests/contenttypes_tests/urls.py b/tests/contenttypes_tests/urls.py index 2cfc90b024..626b3ae8f7 100644 --- a/tests/contenttypes_tests/urls.py +++ b/tests/contenttypes_tests/urls.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.conf.urls import patterns diff --git a/tests/context_processors/urls.py b/tests/context_processors/urls.py index 757017c9ca..9340cdfc38 100644 --- a/tests/context_processors/urls.py +++ b/tests/context_processors/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url from . import views diff --git a/tests/custom_columns/tests.py b/tests/custom_columns/tests.py index 51028b0c46..155279297f 100644 --- a/tests/custom_columns/tests.py +++ b/tests/custom_columns/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.core.exceptions import FieldError from django.test import TestCase diff --git a/tests/custom_columns_regress/tests.py b/tests/custom_columns_regress/tests.py index 7cc66ca2a6..034de08d45 100644 --- a/tests/custom_columns_regress/tests.py +++ b/tests/custom_columns_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.core.exceptions import FieldError from django.test import TestCase diff --git a/tests/custom_managers/models.py b/tests/custom_managers/models.py index 2f5e62fc7a..44d5eb70da 100644 --- a/tests/custom_managers/models.py +++ b/tests/custom_managers/models.py @@ -20,6 +20,49 @@ class PersonManager(models.Manager): def get_fun_people(self): return self.filter(fun=True) +# An example of a custom manager that sets get_queryset(). + +class PublishedBookManager(models.Manager): + def get_queryset(self): + return super(PublishedBookManager, self).get_queryset().filter(is_published=True) + +# An example of a custom queryset that copies its methods onto the manager. + +class CustomQuerySet(models.QuerySet): + def filter(self, *args, **kwargs): + queryset = super(CustomQuerySet, self).filter(fun=True) + queryset._filter_CustomQuerySet = True + return queryset + + def public_method(self, *args, **kwargs): + return self.all() + + def _private_method(self, *args, **kwargs): + return self.all() + + def optout_public_method(self, *args, **kwargs): + return self.all() + optout_public_method.queryset_only = True + + def _optin_private_method(self, *args, **kwargs): + return self.all() + _optin_private_method.queryset_only = False + +class BaseCustomManager(models.Manager): + def __init__(self, arg): + super(BaseCustomManager, self).__init__() + self.init_arg = arg + + def filter(self, *args, **kwargs): + queryset = super(BaseCustomManager, self).filter(fun=True) + queryset._filter_CustomManager = True + return queryset + + def manager_only(self): + return self.all() + +CustomManager = BaseCustomManager.from_queryset(CustomQuerySet) + @python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=30) @@ -27,15 +70,12 @@ class Person(models.Model): fun = models.BooleanField() objects = PersonManager() + custom_queryset_default_manager = CustomQuerySet.as_manager() + custom_queryset_custom_manager = CustomManager('hello') + def __str__(self): return "%s %s" % (self.first_name, self.last_name) -# An example of a custom manager that sets get_queryset(). - -class PublishedBookManager(models.Manager): - def get_queryset(self): - return super(PublishedBookManager, self).get_queryset().filter(is_published=True) - @python_2_unicode_compatible class Book(models.Model): title = models.CharField(max_length=50) diff --git a/tests/custom_managers/tests.py b/tests/custom_managers/tests.py index 4fe79fe3fb..87e5a721bc 100644 --- a/tests/custom_managers/tests.py +++ b/tests/custom_managers/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase from django.utils import six @@ -11,12 +11,54 @@ class CustomManagerTests(TestCase): p1 = Person.objects.create(first_name="Bugs", last_name="Bunny", fun=True) p2 = Person.objects.create(first_name="Droopy", last_name="Dog", fun=False) + # Test a custom `Manager` method. self.assertQuerysetEqual( Person.objects.get_fun_people(), [ "Bugs Bunny" ], six.text_type ) + + # Test that the methods of a custom `QuerySet` are properly + # copied onto the default `Manager`. + for manager in ['custom_queryset_default_manager', + 'custom_queryset_custom_manager']: + manager = getattr(Person, manager) + + # Copy public methods. + manager.public_method() + # Don't copy private methods. + with self.assertRaises(AttributeError): + manager._private_method() + # Copy methods with `manager=True` even if they are private. + manager._optin_private_method() + # Don't copy methods with `manager=False` even if they are public. + with self.assertRaises(AttributeError): + manager.optout_public_method() + + # Test that the overridden method is called. + queryset = manager.filter() + self.assertQuerysetEqual(queryset, ["Bugs Bunny"], six.text_type) + self.assertEqual(queryset._filter_CustomQuerySet, True) + + # Test that specialized querysets inherit from our custom queryset. + queryset = manager.values_list('first_name', flat=True).filter() + self.assertEqual(list(queryset), [six.text_type("Bugs")]) + self.assertEqual(queryset._filter_CustomQuerySet, True) + + # Test that the custom manager `__init__()` argument has been set. + self.assertEqual(Person.custom_queryset_custom_manager.init_arg, 'hello') + + # Test that the custom manager method is only available on the manager. + Person.custom_queryset_custom_manager.manager_only() + with self.assertRaises(AttributeError): + Person.custom_queryset_custom_manager.all().manager_only() + + # Test that the queryset method doesn't override the custom manager method. + queryset = Person.custom_queryset_custom_manager.filter() + self.assertQuerysetEqual(queryset, ["Bugs Bunny"], six.text_type) + self.assertEqual(queryset._filter_CustomManager, True) + # The RelatedManager used on the 'books' descriptor extends the default # manager self.assertIsInstance(p2.books, PublishedBookManager) diff --git a/tests/custom_managers_regress/tests.py b/tests/custom_managers_regress/tests.py index 5a9cd91d59..8462e526c9 100644 --- a/tests/custom_managers_regress/tests.py +++ b/tests/custom_managers_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/custom_methods/tests.py b/tests/custom_methods/tests.py index 9d7444ba62..addb61d4c1 100644 --- a/tests/custom_methods/tests.py +++ b/tests/custom_methods/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import date diff --git a/tests/custom_pk/models.py b/tests/custom_pk/models.py index 5ef9b69f0c..8d5065aed8 100644 --- a/tests/custom_pk/models.py +++ b/tests/custom_pk/models.py @@ -6,7 +6,7 @@ By default, Django adds an ``"id"`` field to each model. But you can override this behavior by explicitly adding ``primary_key=True`` to a field. """ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.db import models diff --git a/tests/custom_pk/tests.py b/tests/custom_pk/tests.py index 3f562f0bed..59bc64754f 100644 --- a/tests/custom_pk/tests.py +++ b/tests/custom_pk/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.db import transaction, IntegrityError from django.test import TestCase, skipIfDBFeature diff --git a/tests/datatypes/tests.py b/tests/datatypes/tests.py index b6b52dedf2..a2bb0c110a 100644 --- a/tests/datatypes/tests.py +++ b/tests/datatypes/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime diff --git a/tests/dates/tests.py b/tests/dates/tests.py index 6c02d597de..c2a8d82e37 100644 --- a/tests/dates/tests.py +++ b/tests/dates/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import datetime diff --git a/tests/datetimes/tests.py b/tests/datetimes/tests.py index 58cb060f6b..f54b30d967 100644 --- a/tests/datetimes/tests.py +++ b/tests/datetimes/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import datetime diff --git a/tests/defaultfilters/tests.py b/tests/defaultfilters/tests.py index 15b96be881..725b673903 100644 --- a/tests/defaultfilters/tests.py +++ b/tests/defaultfilters/tests.py @@ -249,9 +249,10 @@ class DefaultFiltersTests(TestCase): '<a href="https://google.com" rel="nofollow">https://google.com</a>') # Check urlize doesn't overquote already quoted urls - see #9655 - self.assertEqual(urlize('http://hi.baidu.com/%D6%D8%D0%C2%BF'), - '<a href="http://hi.baidu.com/%D6%D8%D0%C2%BF" rel="nofollow">' - 'http://hi.baidu.com/%D6%D8%D0%C2%BF</a>') + # The teststring is the urlquoted version of 'http://hi.baidu.com/重新开始' + self.assertEqual(urlize('http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B'), + '<a href="http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B" rel="nofollow">' + 'http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B</a>') self.assertEqual(urlize('www.mystore.com/30%OffCoupons!'), '<a href="http://www.mystore.com/30%25OffCoupons!" rel="nofollow">' 'www.mystore.com/30%OffCoupons!</a>') diff --git a/tests/defer/tests.py b/tests/defer/tests.py index 50db5a76b4..1df312e1bf 100644 --- a/tests/defer/tests.py +++ b/tests/defer/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db.models.query_utils import DeferredAttribute, InvalidQuery from django.test import TestCase diff --git a/tests/defer_regress/tests.py b/tests/defer_regress/tests.py index ad2546794c..619f65163c 100644 --- a/tests/defer_regress/tests.py +++ b/tests/defer_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from operator import attrgetter diff --git a/tests/delete/tests.py b/tests/delete/tests.py index 66173078a0..8fb16f5a2d 100644 --- a/tests/delete/tests.py +++ b/tests/delete/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db import models, IntegrityError, connection from django.test import TestCase, skipUnlessDBFeature, skipIfDBFeature diff --git a/tests/delete_regress/tests.py b/tests/delete_regress/tests.py index a4908b2121..22e15baf8e 100644 --- a/tests/delete_regress/tests.py +++ b/tests/delete_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import datetime diff --git a/tests/dispatch/tests/__init__.py b/tests/dispatch/tests/__init__.py index b6d26217e1..4c4d4fea8a 100644 --- a/tests/dispatch/tests/__init__.py +++ b/tests/dispatch/tests/__init__.py @@ -2,7 +2,5 @@ Unit-tests for the dispatch project """ -from __future__ import absolute_import - from .test_dispatcher import DispatcherTests, ReceiverTestCase from .test_saferef import SaferefTests diff --git a/tests/distinct_on_fields/tests.py b/tests/distinct_on_fields/tests.py index f62a32e58d..7bc06f0da2 100644 --- a/tests/distinct_on_fields/tests.py +++ b/tests/distinct_on_fields/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db.models import Max from django.test import TestCase, skipUnlessDBFeature diff --git a/tests/empty/tests.py b/tests/empty/tests.py index 6dd9f7c75d..007d04c363 100644 --- a/tests/empty/tests.py +++ b/tests/empty/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.core.exceptions import ImproperlyConfigured from django.db.models.loading import get_app from django.test import TestCase diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index a351496442..9801d0acbb 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.core.exceptions import FieldError from django.db.models import F diff --git a/tests/expressions_regress/tests.py b/tests/expressions_regress/tests.py index ddb2c83b4f..c4f8085804 100644 --- a/tests/expressions_regress/tests.py +++ b/tests/expressions_regress/tests.py @@ -1,7 +1,7 @@ """ Spanning tests for all the operations that F() expressions can perform. """ -from __future__ import absolute_import +from __future__ import unicode_literals import datetime diff --git a/tests/extra_regress/tests.py b/tests/extra_regress/tests.py index 194b250c99..ae8f1b4423 100644 --- a/tests/extra_regress/tests.py +++ b/tests/extra_regress/tests.py @@ -1,10 +1,10 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals +from collections import OrderedDict import datetime from django.contrib.auth.models import User from django.test import TestCase -from django.utils.datastructures import SortedDict from .models import TestObject, Order, RevisionableModel @@ -74,7 +74,7 @@ class ExtraRegressTests(TestCase): # select portions. Applies when portions are updated or otherwise # moved around. qs = User.objects.extra( - select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), + select=OrderedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select_params=(1, 3) ) qs = qs.extra(select={"beta": 4}) @@ -180,100 +180,100 @@ class ExtraRegressTests(TestCase): obj.save() self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()), [{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}] ) # Extra clauses after an empty values clause are still included self.assertEqual( - list(TestObject.objects.values().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), + list(TestObject.objects.values().extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}] ) # Extra columns are ignored if not mentioned in the values() clause self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')), [{'second': 'second', 'first': 'first'}] ) # Extra columns after a non-empty values() clause are ignored self.assertEqual( - list(TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), + list(TestObject.objects.values('first', 'second').extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [{'second': 'second', 'first': 'first'}] ) # Extra columns can be partially returned self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')), [{'second': 'second', 'foo': 'first', 'first': 'first'}] ) # Also works if only extra columns are included self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')), [{'foo': 'first', 'whiz': 'third'}] ) # Values list works the same way # All columns are returned for an empty values_list() self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), [('first', 'second', 'third', obj.pk, 'first', 'second', 'third')] ) # Extra columns after an empty values_list() are still included self.assertEqual( - list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), + list(TestObject.objects.values_list().extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [('first', 'second', 'third', obj.pk, 'first', 'second', 'third')] ) # Extra columns ignored completely if not mentioned in values_list() self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')), [('first', 'second')] ) # Extra columns after a non-empty values_list() clause are ignored completely self.assertEqual( - list(TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), + list(TestObject.objects.values_list('first', 'second').extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [('first', 'second')] ) self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)), ['second'] ) # Only the extra columns specified in the values_list() are returned self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')), [('first', 'second', 'third')] ) # ...also works if only extra columns are included self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')), [('first', 'third')] ) self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)), ['third'] ) # ... and values are returned in the order they are specified self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')), [('third', 'first')] ) self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')), [('first', obj.pk)] ) self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')), [('third', 'first', 'second', obj.pk)] ) diff --git a/tests/field_defaults/tests.py b/tests/field_defaults/tests.py index 69dabb5168..d9f28d8c5c 100644 --- a/tests/field_defaults/tests.py +++ b/tests/field_defaults/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from datetime import datetime from django.test import TestCase diff --git a/tests/field_subclassing/models.py b/tests/field_subclassing/models.py index 642573cc83..67a95b02f2 100644 --- a/tests/field_subclassing/models.py +++ b/tests/field_subclassing/models.py @@ -2,8 +2,6 @@ Tests for field subclassing. """ -from __future__ import absolute_import - from django.db import models from django.utils.encoding import force_text diff --git a/tests/field_subclassing/tests.py b/tests/field_subclassing/tests.py index 4945cff1bf..d3b4d9e527 100644 --- a/tests/field_subclassing/tests.py +++ b/tests/field_subclassing/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.core import serializers from django.test import TestCase diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index 8cf4c33091..cdd9720374 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import errno import os diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py index f5a9c10be4..b0f00236d2 100644 --- a/tests/file_uploads/tests.py +++ b/tests/file_uploads/tests.py @@ -1,5 +1,5 @@ #! -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import base64 import errno diff --git a/tests/file_uploads/urls.py b/tests/file_uploads/urls.py index 96efaaa5d8..4782a0dab9 100644 --- a/tests/file_uploads/urls.py +++ b/tests/file_uploads/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns from . import views diff --git a/tests/file_uploads/views.py b/tests/file_uploads/views.py index d1bd2cf44e..8d20a9cb6e 100644 --- a/tests/file_uploads/views.py +++ b/tests/file_uploads/views.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import hashlib import json diff --git a/tests/files/tests.py b/tests/files/tests.py index 54eeee13e4..b353c1a213 100644 --- a/tests/files/tests.py +++ b/tests/files/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import os import gzip diff --git a/tests/fixtures/tests.py b/tests/fixtures/tests.py index 6f2218b19e..78653ffe5e 100644 --- a/tests/fixtures/tests.py +++ b/tests/fixtures/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import warnings diff --git a/tests/fixtures_model_package/models/sql/book.sql b/tests/fixtures_model_package/models/sql/book.sql new file mode 100644 index 0000000000..9b3918f4d7 --- /dev/null +++ b/tests/fixtures_model_package/models/sql/book.sql @@ -0,0 +1,2 @@ +-- Deprecated search path for custom SQL -- remove in Django 1.9 +INSERT INTO fixtures_model_package_book (name) VALUES ('My Deprecated Book'); diff --git a/tests/fixtures_model_package/sql/book.sql b/tests/fixtures_model_package/sql/book.sql new file mode 100644 index 0000000000..21b1d9465b --- /dev/null +++ b/tests/fixtures_model_package/sql/book.sql @@ -0,0 +1 @@ +INSERT INTO fixtures_model_package_book (name) VALUES ('My Book'); diff --git a/tests/fixtures_model_package/tests.py b/tests/fixtures_model_package/tests.py index ad82267da3..1e22ac9833 100644 --- a/tests/fixtures_model_package/tests.py +++ b/tests/fixtures_model_package/tests.py @@ -5,6 +5,7 @@ import warnings from django.core import management from django.db import transaction from django.test import TestCase, TransactionTestCase +from django.utils.six import StringIO from .models import Article, Book @@ -110,3 +111,19 @@ class FixtureTestCase(TestCase): ], lambda a: a.headline, ) + + +class InitialSQLTests(TestCase): + + def test_custom_sql(self): + """ + #14300 -- Verify that custom_sql_for_model searches `app/sql` and not + `app/models/sql` (the old location will work until Django 1.9) + """ + out = StringIO() + management.call_command("sqlcustom", "fixtures_model_package", stdout=out) + output = out.getvalue() + self.assertTrue("INSERT INTO fixtures_model_package_book (name) " + "VALUES ('My Book')" in output) + # value from deprecated search path models/sql (remove in Django 1.9) + self.assertTrue("Deprecated Book" in output) diff --git a/tests/fixtures_regress/models.py b/tests/fixtures_regress/models.py index 1cc30d2e51..99096728a7 100644 --- a/tests/fixtures_regress/models.py +++ b/tests/fixtures_regress/models.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.contrib.auth.models import User from django.db import models diff --git a/tests/fixtures_regress/tests.py b/tests/fixtures_regress/tests.py index a6bad5716d..f917b21642 100644 --- a/tests/fixtures_regress/tests.py +++ b/tests/fixtures_regress/tests.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Unittests for fixtures. -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import os import re @@ -181,55 +181,68 @@ class TestFixtures(TestCase): """ Test for ticket #4371 -- Loading a fixture file with invalid data using explicit filename. - Validate that error conditions are caught correctly + Test for ticket #18213 -- warning conditions are caught correctly """ - with six.assertRaisesRegex(self, management.CommandError, - "No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)"): + with warnings.catch_warnings(record=True) as warning_list: + warnings.simplefilter("always") management.call_command( 'loaddata', 'bad_fixture2.xml', verbosity=0, ) + warning = warning_list.pop() + self.assertEqual(warning.category, RuntimeWarning) + self.assertEqual(str(warning.message), "No fixture data found for 'bad_fixture2'. (File format may be invalid.)") def test_invalid_data_no_ext(self): """ Test for ticket #4371 -- Loading a fixture file with invalid data without file extension. - Validate that error conditions are caught correctly + Test for ticket #18213 -- warning conditions are caught correctly """ - with six.assertRaisesRegex(self, management.CommandError, - "No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)"): + with warnings.catch_warnings(record=True) as warning_list: + warnings.simplefilter("always") management.call_command( 'loaddata', 'bad_fixture2', verbosity=0, ) + warning = warning_list.pop() + self.assertEqual(warning.category, RuntimeWarning) + self.assertEqual(str(warning.message), "No fixture data found for 'bad_fixture2'. (File format may be invalid.)") def test_empty(self): """ - Test for ticket #4371 -- Loading a fixture file with no data returns an error. - Validate that error conditions are caught correctly + Test for ticket #18213 -- Loading a fixture file with no data output a warning. + Previously empty fixture raises an error exception, see ticket #4371. """ - with six.assertRaisesRegex(self, management.CommandError, - "No fixture data found for 'empty'. \(File format may be invalid.\)"): + with warnings.catch_warnings(record=True) as warning_list: + warnings.simplefilter("always") management.call_command( 'loaddata', 'empty', verbosity=0, ) + warning = warning_list.pop() + self.assertEqual(warning.category, RuntimeWarning) + self.assertEqual(str(warning.message), "No fixture data found for 'empty'. (File format may be invalid.)") def test_error_message(self): """ - (Regression for #9011 - error message is correct) + Regression for #9011 - error message is correct. + Change from error to warning for ticket #18213. """ - with six.assertRaisesRegex(self, management.CommandError, - "^No fixture data found for 'bad_fixture2'. \(File format may be invalid.\)$"): + with warnings.catch_warnings(record=True) as warning_list: + warnings.simplefilter("always") management.call_command( 'loaddata', 'bad_fixture2', 'animal', verbosity=0, ) + warning = warning_list.pop() + self.assertEqual(warning.category, RuntimeWarning) + self.assertEqual(str(warning.message), "No fixture data found for 'bad_fixture2'. (File format may be invalid.)") def test_pg_sequence_resetting_checks(self): """ diff --git a/tests/force_insert_update/tests.py b/tests/force_insert_update/tests.py index a5b2dcebb5..706a099872 100644 --- a/tests/force_insert_update/tests.py +++ b/tests/force_insert_update/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db import transaction, IntegrityError, DatabaseError from django.test import TestCase diff --git a/tests/foreign_object/models.py b/tests/foreign_object/models.py index eee8091a15..d8c3bc10d4 100644 --- a/tests/foreign_object/models.py +++ b/tests/foreign_object/models.py @@ -5,14 +5,15 @@ from django.db.models.fields.related import ReverseSingleRelatedObjectDescriptor from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import get_language +@python_2_unicode_compatible class Country(models.Model): # Table Column Fields name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name - +@python_2_unicode_compatible class Person(models.Model): # Table Column Fields name = models.CharField(max_length=128) @@ -26,9 +27,10 @@ class Person(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Group(models.Model): # Table Column Fields name = models.CharField(max_length=128) @@ -38,10 +40,11 @@ class Group(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Membership(models.Model): # Table Column Fields membership_country = models.ForeignKey(Country) @@ -51,17 +54,19 @@ class Membership(models.Model): group_id = models.IntegerField() # Relation Fields - person = models.ForeignObject(Person, + person = models.ForeignObject( + Person, from_fields=['membership_country', 'person_id'], to_fields=['person_country_id', 'id']) - group = models.ForeignObject(Group, + group = models.ForeignObject( + Group, from_fields=['membership_country', 'group_id'], to_fields=['group_country', 'id']) class Meta: ordering = ('date_joined', 'invite_reason') - def __unicode__(self): + def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) @@ -73,17 +78,20 @@ class Friendship(models.Model): to_friend_id = models.IntegerField() # Relation Fields - from_friend = models.ForeignObject(Person, + from_friend = models.ForeignObject( + Person, from_fields=['from_friend_country', 'from_friend_id'], to_fields=['person_country_id', 'id'], related_name='from_friend') - to_friend_country = models.ForeignObject(Country, + to_friend_country = models.ForeignObject( + Country, from_fields=['to_friend_country_id'], to_fields=['id'], related_name='to_friend_country') - to_friend = models.ForeignObject(Person, + to_friend = models.ForeignObject( + Person, from_fields=['to_friend_country_id', 'to_friend_id'], to_fields=['person_country_id', 'id'], related_name='to_friend') @@ -140,6 +148,9 @@ class Article(models.Model): except ArticleTranslation.DoesNotExist: return '[No translation found]' +class NewsArticle(Article): + pass + class ArticleTranslation(models.Model): article = models.ForeignKey(Article) lang = models.CharField(max_length='2') @@ -156,5 +167,6 @@ class ArticleTag(models.Model): name = models.CharField(max_length=255) class ArticleIdea(models.Model): - articles = models.ManyToManyField(Article, related_name="ideas", related_query_name="idea_things") + articles = models.ManyToManyField(Article, related_name="ideas", + related_query_name="idea_things") name = models.CharField(max_length=255) diff --git a/tests/foreign_object/tests.py b/tests/foreign_object/tests.py index 670fc94dc5..cd81cc68a2 100644 --- a/tests/foreign_object/tests.py +++ b/tests/foreign_object/tests.py @@ -1,12 +1,17 @@ import datetime from operator import attrgetter -from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation, ArticleTag, ArticleIdea +from .models import ( + Country, Person, Group, Membership, Friendship, Article, + ArticleTranslation, ArticleTag, ArticleIdea, NewsArticle) from django.test import TestCase from django.utils.translation import activate from django.core.exceptions import FieldError from django import forms +# Note that these tests are testing internal implementation details. +# ForeignObject is not part of public API. + class MultiColumnFKTests(TestCase): def setUp(self): # Creating countries @@ -140,9 +145,9 @@ class MultiColumnFKTests(TestCase): Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat) with self.assertNumQueries(1): - people = [m.person for m in Membership.objects.select_related('person')] + people = [m.person for m in Membership.objects.select_related('person').order_by('pk')] - normal_people = [m.person for m in Membership.objects.all()] + normal_people = [m.person for m in Membership.objects.all().order_by('pk')] self.assertEqual(people, normal_people) def test_prefetch_foreignkey_forward_works(self): @@ -150,19 +155,22 @@ class MultiColumnFKTests(TestCase): Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat) with self.assertNumQueries(2): - people = [m.person for m in Membership.objects.prefetch_related('person')] + people = [ + m.person for m in Membership.objects.prefetch_related('person').order_by('pk')] - normal_people = [m.person for m in Membership.objects.all()] + normal_people = [m.person for m in Membership.objects.order_by('pk')] self.assertEqual(people, normal_people) def test_prefetch_foreignkey_reverse_works(self): Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat) with self.assertNumQueries(2): - membership_sets = [list(p.membership_set.all()) - for p in Person.objects.prefetch_related('membership_set')] + membership_sets = [ + list(p.membership_set.all()) + for p in Person.objects.prefetch_related('membership_set').order_by('pk')] - normal_membership_sets = [list(p.membership_set.all()) for p in Person.objects.all()] + normal_membership_sets = [list(p.membership_set.all()) + for p in Person.objects.order_by('pk')] self.assertEqual(membership_sets, normal_membership_sets) def test_m2m_through_forward_returns_valid_members(self): @@ -339,6 +347,20 @@ class MultiColumnFKTests(TestCase): with self.assertRaises(FieldError): Article.objects.filter(ideas__name="idea1") + def test_inheritance(self): + activate("fi") + na = NewsArticle.objects.create(pub_date=datetime.date.today()) + ArticleTranslation.objects.create( + article=na, lang="fi", title="foo", body="bar") + self.assertQuerysetEqual( + NewsArticle.objects.select_related('active_translation'), + [na], lambda x: x + ) + with self.assertNumQueries(1): + self.assertEqual( + NewsArticle.objects.select_related( + 'active_translation')[0].active_translation.title, + "foo") class FormsTests(TestCase): # ForeignObjects should not have any form fields, currently the user needs diff --git a/tests/forms_tests/models.py b/tests/forms_tests/models.py index bec31d12d7..33898ffbb8 100644 --- a/tests/forms_tests/models.py +++ b/tests/forms_tests/models.py @@ -34,7 +34,30 @@ class Defaults(models.Model): class ChoiceModel(models.Model): """For ModelChoiceField and ModelMultipleChoiceField tests.""" + CHOICES = [ + ('', 'No Preference'), + ('f', 'Foo'), + ('b', 'Bar'), + ] + + INTEGER_CHOICES = [ + (None, 'No Preference'), + (1, 'Foo'), + (2, 'Bar'), + ] + + STRING_CHOICES_WITH_NONE = [ + (None, 'No Preference'), + ('f', 'Foo'), + ('b', 'Bar'), + ] + name = models.CharField(max_length=10) + choice = models.CharField(max_length=2, blank=True, choices=CHOICES) + choice_string_w_none = models.CharField( + max_length=2, blank=True, null=True, choices=STRING_CHOICES_WITH_NONE) + choice_integer = models.IntegerField(choices=INTEGER_CHOICES, blank=True, + null=True) @python_2_unicode_compatible diff --git a/tests/forms_tests/tests/__init__.py b/tests/forms_tests/tests/__init__.py index aea9418c05..39219f82be 100644 --- a/tests/forms_tests/tests/__init__.py +++ b/tests/forms_tests/tests/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from .test_error_messages import (FormsErrorMessagesTestCase, ModelChoiceFieldErrorMessagesTestCase) from .test_extra import FormsExtraTestCase, FormsExtraL10NTestCase diff --git a/tests/forms_tests/tests/test_error_messages.py b/tests/forms_tests/tests/test_error_messages.py index 9d45a46a3d..f0638298e9 100644 --- a/tests/forms_tests/tests/test_error_messages.py +++ b/tests/forms_tests/tests/test_error_messages.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import * diff --git a/tests/forms_tests/tests/test_extra.py b/tests/forms_tests/tests/test_extra.py index d439e2223c..3bc22243fb 100644 --- a/tests/forms_tests/tests/test_extra.py +++ b/tests/forms_tests/tests/test_extra.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime @@ -620,6 +620,37 @@ class FormsExtraTestCase(TestCase, AssertFormErrorsMixin): self.assertTrue(f.is_valid()) self.assertEqual(f.cleaned_data['username'], 'sirrobin') + def test_changing_cleaned_data_nothing_returned(self): + class UserForm(Form): + username = CharField(max_length=10) + password = CharField(widget=PasswordInput) + + def clean(self): + self.cleaned_data['username'] = self.cleaned_data['username'].lower() + # don't return anything + + f = UserForm({'username': 'SirRobin', 'password': 'blue'}) + self.assertTrue(f.is_valid()) + self.assertEqual(f.cleaned_data['username'], 'sirrobin') + + def test_changing_cleaned_data_in_clean(self): + class UserForm(Form): + username = CharField(max_length=10) + password = CharField(widget=PasswordInput) + + def clean(self): + data = self.cleaned_data + + # Return a different dict. We have not changed self.cleaned_data. + return { + 'username': data['username'].lower(), + 'password': 'this_is_not_a_secret', + } + + f = UserForm({'username': 'SirRobin', 'password': 'blue'}) + self.assertTrue(f.is_valid()) + self.assertEqual(f.cleaned_data['username'], 'sirrobin') + def test_overriding_errorlist(self): @python_2_unicode_compatible class DivErrorList(ErrorList): diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index 633fde5026..1c72d17abe 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import datetime from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.validators import RegexValidator from django.forms import * from django.http import QueryDict from django.template import Template, Context @@ -1792,6 +1793,75 @@ class FormsTestCase(TestCase): self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data, {'name' : 'fname lname'}) + def test_multivalue_optional_subfields(self): + class PhoneField(MultiValueField): + def __init__(self, *args, **kwargs): + fields = ( + CharField(label='Country Code', validators=[ + RegexValidator(r'^\+\d{1,2}$', message='Enter a valid country code.')]), + CharField(label='Phone Number'), + CharField(label='Extension', error_messages={'incomplete': 'Enter an extension.'}), + CharField(label='Label', required=False, help_text='E.g. home, work.'), + ) + super(PhoneField, self).__init__(fields, *args, **kwargs) + + def compress(self, data_list): + if data_list: + return '%s.%s ext. %s (label: %s)' % tuple(data_list) + return None + + # An empty value for any field will raise a `required` error on a + # required `MultiValueField`. + f = PhoneField() + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, []) + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, ['+61']) + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, ['+61', '287654321', '123']) + self.assertEqual('+61.287654321 ext. 123 (label: Home)', f.clean(['+61', '287654321', '123', 'Home'])) + self.assertRaisesMessage(ValidationError, + "'Enter a valid country code.'", f.clean, ['61', '287654321', '123', 'Home']) + + # Empty values for fields will NOT raise a `required` error on an + # optional `MultiValueField` + f = PhoneField(required=False) + self.assertEqual(None, f.clean('')) + self.assertEqual(None, f.clean(None)) + self.assertEqual(None, f.clean([])) + self.assertEqual('+61. ext. (label: )', f.clean(['+61'])) + self.assertEqual('+61.287654321 ext. 123 (label: )', f.clean(['+61', '287654321', '123'])) + self.assertEqual('+61.287654321 ext. 123 (label: Home)', f.clean(['+61', '287654321', '123', 'Home'])) + self.assertRaisesMessage(ValidationError, + "'Enter a valid country code.'", f.clean, ['61', '287654321', '123', 'Home']) + + # For a required `MultiValueField` with `require_all_fields=False`, a + # `required` error will only be raised if all fields are empty. Fields + # can individually be required or optional. An empty value for any + # required field will raise an `incomplete` error. + f = PhoneField(require_all_fields=False) + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '') + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None) + self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, []) + self.assertRaisesMessage(ValidationError, "'Enter a complete value.'", f.clean, ['+61']) + self.assertEqual('+61.287654321 ext. 123 (label: )', f.clean(['+61', '287654321', '123'])) + six.assertRaisesRegex(self, ValidationError, + "'Enter a complete value\.', u?'Enter an extension\.'", f.clean, ['', '', '', 'Home']) + self.assertRaisesMessage(ValidationError, + "'Enter a valid country code.'", f.clean, ['61', '287654321', '123', 'Home']) + + # For an optional `MultiValueField` with `require_all_fields=False`, we + # don't get any `required` error but we still get `incomplete` errors. + f = PhoneField(required=False, require_all_fields=False) + self.assertEqual(None, f.clean('')) + self.assertEqual(None, f.clean(None)) + self.assertEqual(None, f.clean([])) + self.assertRaisesMessage(ValidationError, "'Enter a complete value.'", f.clean, ['+61']) + self.assertEqual('+61.287654321 ext. 123 (label: )', f.clean(['+61', '287654321', '123'])) + six.assertRaisesRegex(self, ValidationError, + "'Enter a complete value\.', u?'Enter an extension\.'", f.clean, ['', '', '', 'Home']) + self.assertRaisesMessage(ValidationError, + "'Enter a valid country code.'", f.clean, ['61', '287654321', '123', 'Home']) + def test_custom_empty_values(self): """ Test that form fields can customize what is considered as an empty value @@ -1824,7 +1894,7 @@ class FormsTestCase(TestCase): # passing just one argument: overrides the field's label (('custom',), {}, '<label for="id_field">custom:</label>'), - # the overriden label is escaped + # the overridden label is escaped (('custom&',), {}, '<label for="id_field">custom&:</label>'), ((mark_safe('custom&'),), {}, '<label for="id_field">custom&:</label>'), @@ -1870,3 +1940,13 @@ class FormsTestCase(TestCase): boundfield = SomeForm()['field'] self.assertHTMLEqual(boundfield.label_tag(), '<label for="id_field"></label>') + + def test_label_tag_override(self): + """ + BoundField label_suffix (if provided) overrides Form label_suffix + """ + class SomeForm(Form): + field = CharField() + boundfield = SomeForm(label_suffix='!')['field'] + + self.assertHTMLEqual(boundfield.label_tag(label_suffix='$'), '<label for="id_field">Field$</label>') diff --git a/tests/forms_tests/tests/test_widgets.py b/tests/forms_tests/tests/test_widgets.py index 4c566dc8e4..06ee2d6c53 100644 --- a/tests/forms_tests/tests/test_widgets.py +++ b/tests/forms_tests/tests/test_widgets.py @@ -849,6 +849,14 @@ beatle J R Ringo False""") w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'}) self.assertHTMLEqual(w.render('name', ['john', 'lennon']), '<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />') + # Test needs_multipart_form=True if any widget needs it + w = MyMultiWidget(widgets=(TextInput(), FileInput())) + self.assertTrue(w.needs_multipart_form) + + # Test needs_multipart_form=False if no widget needs it + w = MyMultiWidget(widgets=(TextInput(), TextInput())) + self.assertFalse(w.needs_multipart_form) + def test_splitdatetime(self): w = SplitDateTimeWidget() self.assertHTMLEqual(w.render('date', ''), '<input type="text" name="date_0" /><input type="text" name="date_1" />') diff --git a/tests/forms_tests/tests/tests.py b/tests/forms_tests/tests/tests.py index 99a67c320c..618ab8e07c 100644 --- a/tests/forms_tests/tests/tests.py +++ b/tests/forms_tests/tests/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime @@ -10,8 +10,8 @@ from django.forms.models import ModelFormMetaclass from django.test import TestCase from django.utils import six -from ..models import (ChoiceOptionModel, ChoiceFieldModel, FileModel, Group, - BoundaryModel, Defaults, OptionalMultiChoiceModel) +from ..models import (ChoiceModel, ChoiceOptionModel, ChoiceFieldModel, + FileModel, Group, BoundaryModel, Defaults, OptionalMultiChoiceModel) class ChoiceFieldForm(ModelForm): @@ -34,6 +34,24 @@ class ChoiceFieldExclusionForm(ModelForm): model = ChoiceFieldModel +class EmptyCharLabelChoiceForm(ModelForm): + class Meta: + model = ChoiceModel + fields = ['name', 'choice'] + + +class EmptyIntegerLabelChoiceForm(ModelForm): + class Meta: + model = ChoiceModel + fields = ['name', 'choice_integer'] + + +class EmptyCharLabelNoneChoiceForm(ModelForm): + class Meta: + model = ChoiceModel + fields = ['name', 'choice_string_w_none'] + + class FileForm(Form): file1 = FileField() @@ -259,3 +277,78 @@ class ManyToManyExclusionTestCase(TestCase): self.assertEqual(form.instance.choice_int.pk, data['choice_int']) self.assertEqual(list(form.instance.multi_choice.all()), [opt2, opt3]) self.assertEqual([obj.pk for obj in form.instance.multi_choice_int.all()], data['multi_choice_int']) + + +class EmptyLabelTestCase(TestCase): + def test_empty_field_char(self): + f = EmptyCharLabelChoiceForm() + self.assertHTMLEqual(f.as_p(), + """<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p> +<p><label for="id_choice">Choice:</label> <select id="id_choice" name="choice"> +<option value="" selected="selected">No Preference</option> +<option value="f">Foo</option> +<option value="b">Bar</option> +</select></p>""") + + def test_empty_field_char_none(self): + f = EmptyCharLabelNoneChoiceForm() + self.assertHTMLEqual(f.as_p(), + """<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p> +<p><label for="id_choice_string_w_none">Choice string w none:</label> <select id="id_choice_string_w_none" name="choice_string_w_none"> +<option value="" selected="selected">No Preference</option> +<option value="f">Foo</option> +<option value="b">Bar</option> +</select></p>""") + + def test_save_empty_label_forms(self): + # Test that saving a form with a blank choice results in the expected + # value being stored in the database. + tests = [ + (EmptyCharLabelNoneChoiceForm, 'choice_string_w_none', None), + (EmptyIntegerLabelChoiceForm, 'choice_integer', None), + (EmptyCharLabelChoiceForm, 'choice', ''), + ] + + for form, key, expected in tests: + f = form({'name': 'some-key', key: ''}) + self.assertTrue(f.is_valid()) + m = f.save() + self.assertEqual(expected, getattr(m, key)) + self.assertEqual('No Preference', + getattr(m, 'get_{0}_display'.format(key))()) + + def test_empty_field_integer(self): + f = EmptyIntegerLabelChoiceForm() + self.assertHTMLEqual(f.as_p(), + """<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" /></p> +<p><label for="id_choice_integer">Choice integer:</label> <select id="id_choice_integer" name="choice_integer"> +<option value="" selected="selected">No Preference</option> +<option value="1">Foo</option> +<option value="2">Bar</option> +</select></p>""") + + def test_get_display_value_on_none(self): + m = ChoiceModel.objects.create(name='test', choice='', choice_integer=None) + self.assertEqual(None, m.choice_integer) + self.assertEqual('No Preference', m.get_choice_integer_display()) + + def test_html_rendering_of_prepopulated_models(self): + none_model = ChoiceModel(name='none-test', choice_integer=None) + f = EmptyIntegerLabelChoiceForm(instance=none_model) + self.assertHTMLEqual(f.as_p(), + """<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" value="none-test"/></p> +<p><label for="id_choice_integer">Choice integer:</label> <select id="id_choice_integer" name="choice_integer"> +<option value="" selected="selected">No Preference</option> +<option value="1">Foo</option> +<option value="2">Bar</option> +</select></p>""") + + foo_model = ChoiceModel(name='foo-test', choice_integer=1) + f = EmptyIntegerLabelChoiceForm(instance=foo_model) + self.assertHTMLEqual(f.as_p(), + """<p><label for="id_name">Name:</label> <input id="id_name" maxlength="10" name="name" type="text" value="foo-test"/></p> +<p><label for="id_choice_integer">Choice integer:</label> <select id="id_choice_integer" name="choice_integer"> +<option value="">No Preference</option> +<option value="1" selected="selected">Foo</option> +<option value="2">Bar</option> +</select></p>""") diff --git a/tests/generic_inline_admin/admin.py b/tests/generic_inline_admin/admin.py index 73cac7f7c5..1917024fa5 100644 --- a/tests/generic_inline_admin/admin.py +++ b/tests/generic_inline_admin/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib import admin from django.contrib.contenttypes import generic diff --git a/tests/generic_inline_admin/tests.py b/tests/generic_inline_admin/tests.py index ac2c191183..83bee2f4e0 100644 --- a/tests/generic_inline_admin/tests.py +++ b/tests/generic_inline_admin/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.conf import settings from django.contrib import admin @@ -325,3 +325,23 @@ class GenericInlineModelAdminTest(TestCase): self.assertEqual( list(list(ma.get_formsets(request))[0]().forms[0].fields), ['description', 'keywords', 'id', 'DELETE']) + + def test_get_fieldsets(self): + # Test that get_fieldsets is called when figuring out form fields. + # Refs #18681. + class MediaForm(ModelForm): + class Meta: + model = Media + fields = '__all__' + + class MediaInline(GenericTabularInline): + form = MediaForm + model = Media + can_delete = False + + def get_fieldsets(self, request, obj=None): + return [(None, {'fields': ['url', 'description']})] + + ma = MediaInline(Media, self.site) + form = ma.get_formset(None).form + self.assertEqual(form._meta.fields, ['url', 'description']) diff --git a/tests/generic_inline_admin/urls.py b/tests/generic_inline_admin/urls.py index 88d7b574d4..8d68d6c922 100644 --- a/tests/generic_inline_admin/urls.py +++ b/tests/generic_inline_admin/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, include from . import admin diff --git a/tests/generic_relations/tests.py b/tests/generic_relations/tests.py index 734b2e5143..2b52ebac56 100644 --- a/tests/generic_relations/tests.py +++ b/tests/generic_relations/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django import forms from django.contrib.contenttypes.generic import generic_inlineformset_factory diff --git a/tests/generic_views/test_base.py b/tests/generic_views/test_base.py index 013a8f282c..b1dd7a3040 100644 --- a/tests/generic_views/test_base.py +++ b/tests/generic_views/test_base.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import time import unittest diff --git a/tests/generic_views/test_dates.py b/tests/generic_views/test_dates.py index bff909f7d0..03c6d02e5e 100644 --- a/tests/generic_views/test_dates.py +++ b/tests/generic_views/test_dates.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import time import datetime diff --git a/tests/generic_views/test_detail.py b/tests/generic_views/test_detail.py index 3a97d27995..8a487a7c3d 100644 --- a/tests/generic_views/test_detail.py +++ b/tests/generic_views/test_detail.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.core.exceptions import ImproperlyConfigured from django.test import TestCase diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py index 9ed18833e4..9b1ba0f865 100644 --- a/tests/generic_views/test_edit.py +++ b/tests/generic_views/test_edit.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import warnings from unittest import expectedFailure diff --git a/tests/generic_views/test_forms.py b/tests/generic_views/test_forms.py index 8c118e32a6..1ee26afc8f 100644 --- a/tests/generic_views/test_forms.py +++ b/tests/generic_views/test_forms.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django import forms diff --git a/tests/generic_views/test_list.py b/tests/generic_views/test_list.py index a77a6418a3..e572af8e32 100644 --- a/tests/generic_views/test_list.py +++ b/tests/generic_views/test_list.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.core.exceptions import ImproperlyConfigured from django.test import TestCase diff --git a/tests/generic_views/urls.py b/tests/generic_views/urls.py index 695b50279a..4ef8f7a97f 100644 --- a/tests/generic_views/urls.py +++ b/tests/generic_views/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url from django.views.decorators.cache import cache_page from django.views.generic import TemplateView diff --git a/tests/generic_views/views.py b/tests/generic_views/views.py index fd331f14b7..f839b53753 100644 --- a/tests/generic_views/views.py +++ b/tests/generic_views/views.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator diff --git a/tests/get_earliest_or_latest/tests.py b/tests/get_earliest_or_latest/tests.py index 8d16af9587..eeb95c0818 100644 --- a/tests/get_earliest_or_latest/tests.py +++ b/tests/get_earliest_or_latest/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import datetime diff --git a/tests/get_object_or_404/tests.py b/tests/get_object_or_404/tests.py index 38ebeb4f8c..1a0b41bc4d 100644 --- a/tests/get_object_or_404/tests.py +++ b/tests/get_object_or_404/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.http import Http404 from django.shortcuts import get_object_or_404, get_list_or_404 @@ -87,7 +87,7 @@ class GetObjectOr404Tests(TestCase): self.assertRaisesMessage(ValueError, "Object is of type 'str', but must be a Django Model, Manager, " "or QuerySet", - get_object_or_404, "Article", title__icontains="Run" + get_object_or_404, str("Article"), title__icontains="Run" ) class CustomClass(object): diff --git a/tests/get_or_create/tests.py b/tests/get_or_create/tests.py index 0f766ab128..a612ea60a0 100644 --- a/tests/get_or_create/tests.py +++ b/tests/get_or_create/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import date import traceback @@ -65,7 +65,7 @@ class GetOrCreateTests(TestCase): ManualPrimaryKeyTest.objects.get_or_create(id=1, data="Different") except IntegrityError as e: formatted_traceback = traceback.format_exc() - self.assertIn('obj.save', formatted_traceback) + self.assertIn(str('obj.save'), formatted_traceback) def test_savepoint_rollback(self): # Regression test for #20463: the database connection should still be diff --git a/tests/get_or_create_regress/tests.py b/tests/get_or_create_regress/tests.py index 92c371b6f8..54dafc85fb 100644 --- a/tests/get_or_create_regress/tests.py +++ b/tests/get_or_create_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/httpwrappers/tests.py b/tests/httpwrappers/tests.py index 3a2ec23864..1679b6e036 100644 --- a/tests/httpwrappers/tests.py +++ b/tests/httpwrappers/tests.py @@ -16,10 +16,13 @@ from django.http import (QueryDict, HttpResponse, HttpResponseRedirect, SimpleCookie, BadHeaderError, parse_cookie) from django.test import TestCase -from django.utils.encoding import smart_str +from django.utils.encoding import smart_str, force_text +from django.utils.functional import lazy from django.utils._os import upath from django.utils import six +lazystr = lazy(force_text, six.text_type) + class QueryDictTests(unittest.TestCase): def test_missing_key(self): @@ -366,6 +369,10 @@ class HttpResponseTests(unittest.TestCase): self.assertEqual(list(i), [b'abc']) self.assertEqual(list(i), []) + def test_lazy_content(self): + r = HttpResponse(lazystr('helloworld')) + self.assertEqual(r.content, b'helloworld') + def test_file_interface(self): r = HttpResponse() r.write(b"hello") @@ -402,6 +409,11 @@ class HttpResponseSubclassesTests(TestCase): # Test that url attribute is right self.assertEqual(response.url, response['Location']) + def test_redirect_lazy(self): + """Make sure HttpResponseRedirect works with lazy strings.""" + r = HttpResponseRedirect(lazystr('/redirected/')) + self.assertEqual(r.url, '/redirected/') + def test_not_modified(self): response = HttpResponseNotModified() self.assertEqual(response.status_code, 304) diff --git a/tests/i18n/forms.py b/tests/i18n/forms.py index 6e4def9c5e..07b015d590 100644 --- a/tests/i18n/forms.py +++ b/tests/i18n/forms.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django import forms from django.forms.extras import SelectDateWidget diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index 019ed88cdb..05d1065a75 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -1,8 +1,9 @@ # -*- encoding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime import decimal +from importlib import import_module import os import pickle from threading import local @@ -17,7 +18,6 @@ from django.utils import translation from django.utils.formats import (get_format, date_format, time_format, localize, localize_input, iter_format_modules, get_format_modules, number_format, reset_format_cache, sanitize_separators) -from django.utils.importlib import import_module from django.utils.numberformat import format as nformat from django.utils._os import upath from django.utils.safestring import mark_safe, SafeBytes, SafeString, SafeText @@ -774,6 +774,39 @@ class FormattingTests(TransRealMixin, TestCase): self.assertEqual(template2.render(context), output2) self.assertEqual(template3.render(context), output3) + def test_localized_as_text_as_hidden_input(self): + """ + Tests if form input with 'as_hidden' or 'as_text' is correctly localized. Ticket #18777 + """ + self.maxDiff = 1200 + + with translation.override('de-at', deactivate=True): + template = Template('{% load l10n %}{{ form.date_added }}; {{ form.cents_paid }}') + template_as_text = Template('{% load l10n %}{{ form.date_added.as_text }}; {{ form.cents_paid.as_text }}') + template_as_hidden = Template('{% load l10n %}{{ form.date_added.as_hidden }}; {{ form.cents_paid.as_hidden }}') + form = CompanyForm({ + 'name': 'acme', + 'date_added': datetime.datetime(2009, 12, 31, 6, 0, 0), + 'cents_paid': decimal.Decimal('59.47'), + 'products_delivered': 12000, + }) + context = Context({'form': form }) + self.assertTrue(form.is_valid()) + + self.assertHTMLEqual( + template.render(context), + '<input id="id_date_added" name="date_added" type="text" value="31.12.2009 06:00:00" />; <input id="id_cents_paid" name="cents_paid" type="text" value="59,47" />' + ) + self.assertHTMLEqual( + template_as_text.render(context), + '<input id="id_date_added" name="date_added" type="text" value="31.12.2009 06:00:00" />; <input id="id_cents_paid" name="cents_paid" type="text" value="59,47" />' + ) + self.assertHTMLEqual( + template_as_hidden.render(context), + '<input id="id_date_added" name="date_added" type="hidden" value="31.12.2009 06:00:00" />; <input id="id_cents_paid" name="cents_paid" type="hidden" value="59,47" />' + ) + + class MiscTests(TransRealMixin, TestCase): def setUp(self): diff --git a/tests/inline_formsets/tests.py b/tests/inline_formsets/tests.py index ad8a666cb5..a16488dc79 100644 --- a/tests/inline_formsets/tests.py +++ b/tests/inline_formsets/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.forms.models import inlineformset_factory from django.test import TestCase diff --git a/tests/introspection/tests.py b/tests/introspection/tests.py index f1c87bbf14..6f38439945 100644 --- a/tests/introspection/tests.py +++ b/tests/introspection/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import unittest diff --git a/tests/known_related_objects/tests.py b/tests/known_related_objects/tests.py index d28d266557..6fd507cbdc 100644 --- a/tests/known_related_objects/tests.py +++ b/tests/known_related_objects/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index ee9c5afe1d..2beb43929b 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from datetime import datetime from operator import attrgetter @@ -470,7 +470,8 @@ class LookupTests(TestCase): 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") + "into field. Choices are: author, author_id, headline, " + "id, pub_date, tag") try: Article.objects.filter(headline__starts='Article') self.fail('FieldError not raised') diff --git a/tests/m2m_and_m2o/tests.py b/tests/m2m_and_m2o/tests.py index 77f2eb3b09..0380ad4b08 100644 --- a/tests/m2m_and_m2o/tests.py +++ b/tests/m2m_and_m2o/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.db.models import Q from django.test import TestCase diff --git a/tests/m2m_intermediary/tests.py b/tests/m2m_intermediary/tests.py index f261f23546..d9c77ecb7c 100644 --- a/tests/m2m_intermediary/tests.py +++ b/tests/m2m_intermediary/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import datetime diff --git a/tests/m2m_multiple/tests.py b/tests/m2m_multiple/tests.py index 7bf88f99bb..2122517ce4 100644 --- a/tests/m2m_multiple/tests.py +++ b/tests/m2m_multiple/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import datetime diff --git a/tests/m2m_recursive/tests.py b/tests/m2m_recursive/tests.py index a3f2c670d6..3beafc9692 100644 --- a/tests/m2m_recursive/tests.py +++ b/tests/m2m_recursive/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from operator import attrgetter diff --git a/tests/m2m_regress/tests.py b/tests/m2m_regress/tests.py index 884fc097a1..610f01694a 100644 --- a/tests/m2m_regress/tests.py +++ b/tests/m2m_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.core.exceptions import FieldError from django.test import TestCase diff --git a/tests/m2m_signals/tests.py b/tests/m2m_signals/tests.py index d3d2a74c70..569a2dc12d 100644 --- a/tests/m2m_signals/tests.py +++ b/tests/m2m_signals/tests.py @@ -2,8 +2,6 @@ Testing signals emitted on changing m2m relations. """ -from .models import Person - from django.db import models from django.test import TestCase diff --git a/tests/m2m_through/tests.py b/tests/m2m_through/tests.py index 259dc68a0b..69a7ec99bf 100644 --- a/tests/m2m_through/tests.py +++ b/tests/m2m_through/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import datetime from operator import attrgetter diff --git a/tests/m2m_through_regress/tests.py b/tests/m2m_through_regress/tests.py index de4d52a2db..c1b229b30b 100644 --- a/tests/m2m_through_regress/tests.py +++ b/tests/m2m_through_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.core import management from django.contrib.auth.models import User diff --git a/tests/m2o_recursive/tests.py b/tests/m2o_recursive/tests.py index fa04c74cca..f5e8938706 100644 --- a/tests/m2o_recursive/tests.py +++ b/tests/m2o_recursive/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/mail/tests.py b/tests/mail/tests.py index 822c572b0f..0f85cc0c76 100644 --- a/tests/mail/tests.py +++ b/tests/mail/tests.py @@ -397,6 +397,34 @@ class BaseEmailBackendTests(object): self.assertEqual(message.get_payload(), "Content") self.assertEqual(message["from"], "=?utf-8?q?Firstname_S=C3=BCrname?= <from@example.com>") + def test_plaintext_send_mail(self): + """ + Test send_mail without the html_message + regression test for adding html_message parameter to send_mail() + """ + send_mail('Subject', 'Content', 'sender@example.com', ['nobody@example.com']) + message = self.get_the_message() + + self.assertEqual(message.get('subject'), 'Subject') + self.assertEqual(message.get_all('to'), ['nobody@example.com']) + self.assertFalse(message.is_multipart()) + self.assertEqual(message.get_payload(), 'Content') + self.assertEqual(message.get_content_type(), 'text/plain') + + def test_html_send_mail(self): + """Test html_message argument to send_mail""" + send_mail('Subject', 'Content', 'sender@example.com', ['nobody@example.com'], html_message='HTML Content') + message = self.get_the_message() + + self.assertEqual(message.get('subject'), 'Subject') + self.assertEqual(message.get_all('to'), ['nobody@example.com']) + self.assertTrue(message.is_multipart()) + self.assertEqual(len(message.get_payload()), 2) + self.assertEqual(message.get_payload(0).get_payload(), 'Content') + self.assertEqual(message.get_payload(0).get_content_type(), 'text/plain') + self.assertEqual(message.get_payload(1).get_payload(), 'HTML Content') + self.assertEqual(message.get_payload(1).get_content_type(), 'text/html') + @override_settings(MANAGERS=[('nobody', 'nobody@example.com')]) def test_html_mail_managers(self): """Test html_message argument to mail_managers""" diff --git a/tests/managers_regress/tests.py b/tests/managers_regress/tests.py index 45059be4e5..e5dce53122 100644 --- a/tests/managers_regress/tests.py +++ b/tests/managers_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import copy from django.conf import settings diff --git a/tests/many_to_many/tests.py b/tests/many_to_many/tests.py index 7d30379b94..e6fed191fc 100644 --- a/tests/many_to_many/tests.py +++ b/tests/many_to_many/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase from django.utils import six diff --git a/tests/many_to_one/tests.py b/tests/many_to_one/tests.py index a4f87a3283..ae629288b1 100644 --- a/tests/many_to_one/tests.py +++ b/tests/many_to_one/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from copy import deepcopy import datetime diff --git a/tests/many_to_one_null/tests.py b/tests/many_to_one_null/tests.py index 4de44b5e64..cd1800d77c 100644 --- a/tests/many_to_one_null/tests.py +++ b/tests/many_to_one_null/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/many_to_one_regress/tests.py b/tests/many_to_one_regress/tests.py index 035ba53bff..19cfaad097 100644 --- a/tests/many_to_one_regress/tests.py +++ b/tests/many_to_one_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db import models from django.test import TestCase diff --git a/tests/max_lengths/tests.py b/tests/max_lengths/tests.py index 0f525864d5..43f9e217c8 100644 --- a/tests/max_lengths/tests.py +++ b/tests/max_lengths/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import unittest diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py index 5ceab2e594..0897ec06be 100644 --- a/tests/middleware/tests.py +++ b/tests/middleware/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import gzip from io import BytesIO diff --git a/tests/middleware_exceptions/urls.py b/tests/middleware_exceptions/urls.py index 042607fdc8..fcef638553 100644 --- a/tests/middleware_exceptions/urls.py +++ b/tests/middleware_exceptions/urls.py @@ -1,6 +1,3 @@ -# coding: utf-8 -from __future__ import absolute_import - from django.conf.urls import patterns from . import views diff --git a/tests/model_fields/test_imagefield.py b/tests/model_fields/test_imagefield.py index f6019bd77f..ce7d33eb32 100644 --- a/tests/model_fields/test_imagefield.py +++ b/tests/model_fields/test_imagefield.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import os import shutil diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index 1ce5eba3e9..378e6280cc 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime from decimal import Decimal @@ -8,11 +8,21 @@ from django import test from django import forms from django.core.exceptions import ValidationError from django.db import connection, models, IntegrityError -from django.db.models.fields.files import FieldFile +from django.db.models.fields import ( + AutoField, BigIntegerField, BinaryField, BooleanField, CharField, + CommaSeparatedIntegerField, DateField, DateTimeField, DecimalField, + EmailField, FilePathField, FloatField, IntegerField, IPAddressField, + GenericIPAddressField, NullBooleanField, PositiveIntegerField, + PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField, + TimeField, URLField) +from django.db.models.fields.files import FileField, ImageField from django.utils import six +from django.utils.functional import lazy +from django.utils.unittest import skipIf -from .models import (Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post, - NullBooleanModel, BooleanModel, DataModel, Document, RenamedField, +from .models import ( + Foo, Bar, Whiz, BigD, BigS, BigInt, Post, NullBooleanModel, + BooleanModel, DataModel, Document, RenamedField, VerboseNameField, FksToBooleans) @@ -64,7 +74,7 @@ class BasicFieldTests(test.TestCase): m = VerboseNameField for i in range(1, 23): self.assertEqual(m._meta.get_field('field%d' % i).verbose_name, - 'verbose field%d' % i) + 'verbose field%d' % i) self.assertEqual(m._meta.get_field('id').verbose_name, 'verbose pk') @@ -290,9 +300,9 @@ class SlugFieldTests(test.TestCase): """ Make sure SlugField honors max_length (#9706) """ - bs = BigS.objects.create(s = 'slug'*50) + bs = BigS.objects.create(s='slug' * 50) bs = BigS.objects.get(pk=bs.pk) - self.assertEqual(bs.s, 'slug'*50) + self.assertEqual(bs.s, 'slug' * 50) class ValidationTest(test.TestCase): @@ -313,15 +323,21 @@ class ValidationTest(test.TestCase): self.assertRaises(ValidationError, f.clean, "a", None) def test_charfield_with_choices_cleans_valid_choice(self): - f = models.CharField(max_length=1, choices=[('a','A'), ('b','B')]) + f = models.CharField(max_length=1, + choices=[('a', 'A'), ('b', 'B')]) self.assertEqual('a', f.clean('a', None)) def test_charfield_with_choices_raises_error_on_invalid_choice(self): - f = models.CharField(choices=[('a','A'), ('b','B')]) + f = models.CharField(choices=[('a', 'A'), ('b', 'B')]) self.assertRaises(ValidationError, f.clean, "not a", None) + def test_charfield_get_choices_with_blank_defined(self): + f = models.CharField(choices=[('', '<><>'), ('a', 'A')]) + self.assertEqual(f.get_choices(True), [('', '<><>'), ('a', 'A')]) + def test_choices_validation_supports_named_groups(self): - f = models.IntegerField(choices=(('group',((10,'A'),(20,'B'))),(30,'C'))) + f = models.IntegerField( + choices=(('group', ((10, 'A'), (20, 'B'))), (30, 'C'))) self.assertEqual(10, f.clean(10, None)) def test_nullable_integerfield_raises_error_with_blank_false(self): @@ -370,7 +386,7 @@ class BigIntegerFieldTests(test.TestCase): self.assertEqual(qs[0].value, minval) def test_types(self): - b = BigInt(value = 0) + b = BigInt(value=0) self.assertIsInstance(b.value, six.integer_types) b.save() self.assertIsInstance(b.value, six.integer_types) @@ -378,8 +394,8 @@ class BigIntegerFieldTests(test.TestCase): self.assertIsInstance(b.value, six.integer_types) def test_coercing(self): - BigInt.objects.create(value ='10') - b = BigInt.objects.get(value = '10') + BigInt.objects.create(value='10') + b = BigInt.objects.get(value='10') self.assertEqual(b.value, 10) class TypeCoercionTests(test.TestCase): @@ -466,7 +482,7 @@ class BinaryFieldTests(test.TestCase): test_set_and_retrieve = unittest.expectedFailure(test_set_and_retrieve) def test_max_length(self): - dm = DataModel(short_data=self.binary_data*4) + dm = DataModel(short_data=self.binary_data * 4) self.assertRaises(ValidationError, dm.full_clean) class GenericIPAddressFieldTests(test.TestCase): @@ -481,3 +497,156 @@ class GenericIPAddressFieldTests(test.TestCase): model_field = models.GenericIPAddressField(protocol='IPv6') form_field = model_field.formfield() self.assertRaises(ValidationError, form_field.clean, '127.0.0.1') + + +class PromiseTest(test.TestCase): + def test_AutoField(self): + lazy_func = lazy(lambda: 1, int) + self.assertIsInstance( + AutoField(primary_key=True).get_prep_value(lazy_func()), + int) + + @skipIf(six.PY3, "Python 3 has no `long` type.") + def test_BigIntegerField(self): + lazy_func = lazy(lambda: long(9999999999999999999), long) + self.assertIsInstance( + BigIntegerField().get_prep_value(lazy_func()), + long) + + def test_BinaryField(self): + lazy_func = lazy(lambda: b'', bytes) + self.assertIsInstance( + BinaryField().get_prep_value(lazy_func()), + bytes) + + def test_BooleanField(self): + lazy_func = lazy(lambda: True, bool) + self.assertIsInstance( + BooleanField().get_prep_value(lazy_func()), + bool) + + def test_CharField(self): + lazy_func = lazy(lambda: '', six.text_type) + self.assertIsInstance( + CharField().get_prep_value(lazy_func()), + six.text_type) + + def test_CommaSeparatedIntegerField(self): + lazy_func = lazy(lambda: '1,2', six.text_type) + self.assertIsInstance( + CommaSeparatedIntegerField().get_prep_value(lazy_func()), + six.text_type) + + def test_DateField(self): + lazy_func = lazy(lambda: datetime.date.today(), datetime.date) + self.assertIsInstance( + DateField().get_prep_value(lazy_func()), + datetime.date) + + def test_DateTimeField(self): + lazy_func = lazy(lambda: datetime.datetime.now(), datetime.datetime) + self.assertIsInstance( + DateTimeField().get_prep_value(lazy_func()), + datetime.datetime) + + def test_DecimalField(self): + lazy_func = lazy(lambda: Decimal('1.2'), Decimal) + self.assertIsInstance( + DecimalField().get_prep_value(lazy_func()), + Decimal) + + def test_EmailField(self): + lazy_func = lazy(lambda: 'mailbox@domain.com', six.text_type) + self.assertIsInstance( + EmailField().get_prep_value(lazy_func()), + six.text_type) + + def test_FileField(self): + lazy_func = lazy(lambda: 'filename.ext', six.text_type) + self.assertIsInstance( + FileField().get_prep_value(lazy_func()), + six.text_type) + + def test_FilePathField(self): + lazy_func = lazy(lambda: 'tests.py', six.text_type) + self.assertIsInstance( + FilePathField().get_prep_value(lazy_func()), + six.text_type) + + def test_FloatField(self): + lazy_func = lazy(lambda: 1.2, float) + self.assertIsInstance( + FloatField().get_prep_value(lazy_func()), + float) + + def test_ImageField(self): + lazy_func = lazy(lambda: 'filename.ext', six.text_type) + self.assertIsInstance( + ImageField().get_prep_value(lazy_func()), + six.text_type) + + def test_IntegerField(self): + lazy_func = lazy(lambda: 1, int) + self.assertIsInstance( + IntegerField().get_prep_value(lazy_func()), + int) + + def test_IPAddressField(self): + lazy_func = lazy(lambda: '127.0.0.1', six.text_type) + self.assertIsInstance( + IPAddressField().get_prep_value(lazy_func()), + six.text_type) + + def test_GenericIPAddressField(self): + lazy_func = lazy(lambda: '127.0.0.1', six.text_type) + self.assertIsInstance( + GenericIPAddressField().get_prep_value(lazy_func()), + six.text_type) + + def test_NullBooleanField(self): + lazy_func = lazy(lambda: True, bool) + self.assertIsInstance( + NullBooleanField().get_prep_value(lazy_func()), + bool) + + def test_PositiveIntegerField(self): + lazy_func = lazy(lambda: 1, int) + self.assertIsInstance( + PositiveIntegerField().get_prep_value(lazy_func()), + int) + + def test_PositiveSmallIntegerField(self): + lazy_func = lazy(lambda: 1, int) + self.assertIsInstance( + PositiveSmallIntegerField().get_prep_value(lazy_func()), + int) + + def test_SlugField(self): + lazy_func = lazy(lambda: 'slug', six.text_type) + self.assertIsInstance( + SlugField().get_prep_value(lazy_func()), + six.text_type) + + def test_SmallIntegerField(self): + lazy_func = lazy(lambda: 1, int) + self.assertIsInstance( + SmallIntegerField().get_prep_value(lazy_func()), + int) + + def test_TextField(self): + lazy_func = lazy(lambda: 'Abc', six.text_type) + self.assertIsInstance( + TextField().get_prep_value(lazy_func()), + six.text_type) + + def test_TimeField(self): + lazy_func = lazy(lambda: datetime.datetime.now().time(), datetime.time) + self.assertIsInstance( + TimeField().get_prep_value(lazy_func()), + datetime.time) + + def test_URLField(self): + lazy_func = lazy(lambda: 'http://domain.com', six.text_type) + self.assertIsInstance( + URLField().get_prep_value(lazy_func()), + six.text_type) diff --git a/tests/model_forms/models.py b/tests/model_forms/models.py index a4cf9471de..17aec29055 100644 --- a/tests/model_forms/models.py +++ b/tests/model_forms/models.py @@ -12,7 +12,7 @@ import os import tempfile from django.core import validators -from django.core.exceptions import ImproperlyConfigured +from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.files.storage import FileSystemStorage from django.db import models from django.utils import six @@ -296,3 +296,7 @@ class CustomErrorMessage(models.Model): name2 = models.CharField(max_length=50, validators=[validators.validate_slug], error_messages={'invalid': 'Model custom error message.'}) + + def clean(self): + if self.name1 == 'FORBIDDEN_VALUE': + raise ValidationError({'name1': [ValidationError('Model.clean() error messages.')]}) diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 09c62c5205..1404725484 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime import os @@ -1782,6 +1782,14 @@ class OldFormForXTests(TestCase): '<ul class="errorlist"><li>Model custom error message.</li></ul>' ) + def test_model_clean_error_messages(self) : + data = {'name1': 'FORBIDDEN_VALUE', 'name2': 'ABC'} + errors = CustomErrorMessageForm(data).errors + self.assertHTMLEqual( + str(errors['name1']), + '<ul class="errorlist"><li>Model.clean() error messages.</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 35e706ac4c..ffe1123b99 100644 --- a/tests/model_forms_regress/tests.py +++ b/tests/model_forms_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from datetime import date import unittest diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index 4411bbf59a..053448181a 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime import re @@ -32,6 +32,9 @@ class DeletionTests(TestCase): 'form-0-DELETE': 'on', } formset = PoetFormSet(data, queryset=Poet.objects.all()) + formset.save(commit=False) + self.assertEqual(Poet.objects.count(), 1) + formset.save() self.assertTrue(formset.is_valid()) self.assertEqual(Poet.objects.count(), 0) @@ -388,7 +391,7 @@ class ModelFormsetTest(TestCase): def test_custom_queryset_init(self): """ - Test that a queryset can be overriden in the __init__ method. + Test that a queryset can be overridden in the __init__ method. https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset """ author1 = Author.objects.create(name='Charles Baudelaire') diff --git a/tests/model_formsets_regress/tests.py b/tests/model_formsets_regress/tests.py index 179f79fbcb..782c7d6fbc 100644 --- a/tests/model_formsets_regress/tests.py +++ b/tests/model_formsets_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django import forms from django.forms.formsets import BaseFormSet, DELETION_FIELD_NAME diff --git a/tests/model_inheritance/tests.py b/tests/model_inheritance/tests.py index dc40d2d2e0..eb50d30cf8 100644 --- a/tests/model_inheritance/tests.py +++ b/tests/model_inheritance/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from operator import attrgetter diff --git a/tests/model_inheritance_regress/tests.py b/tests/model_inheritance_regress/tests.py index 7ca16647fa..10a1230685 100644 --- a/tests/model_inheritance_regress/tests.py +++ b/tests/model_inheritance_regress/tests.py @@ -1,7 +1,7 @@ """ Regression tests for Model inheritance behavior. """ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime from operator import attrgetter diff --git a/tests/model_inheritance_same_model_name/models.py b/tests/model_inheritance_same_model_name/models.py index b4a6b930df..8b02b08668 100644 --- a/tests/model_inheritance_same_model_name/models.py +++ b/tests/model_inheritance_same_model_name/models.py @@ -6,8 +6,6 @@ in the need for an %(app_label)s format string. This app specifically tests this feature by redefining the Copy model from model_inheritance/models.py """ -from __future__ import absolute_import - from django.db import models from model_inheritance.models import NamedURL diff --git a/tests/model_inheritance_same_model_name/tests.py b/tests/model_inheritance_same_model_name/tests.py index 8f22578013..fe1fb0cdd2 100644 --- a/tests/model_inheritance_same_model_name/tests.py +++ b/tests/model_inheritance_same_model_name/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/model_inheritance_select_related/tests.py b/tests/model_inheritance_select_related/tests.py index 078b466d0e..68f9897e25 100644 --- a/tests/model_inheritance_select_related/tests.py +++ b/tests/model_inheritance_select_related/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from operator import attrgetter diff --git a/tests/model_package/models/__init__.py b/tests/model_package/models/__init__.py index 3c261aa444..ec29d667f9 100644 --- a/tests/model_package/models/__init__.py +++ b/tests/model_package/models/__init__.py @@ -1,5 +1,3 @@ # Import all the models from subpackages -from __future__ import absolute_import - from .article import Article from .publication import Publication diff --git a/tests/model_package/tests.py b/tests/model_package/tests.py index 5d856a9608..a352b57478 100644 --- a/tests/model_package/tests.py +++ b/tests/model_package/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.contrib.sites.models import Site from django.db import models diff --git a/tests/model_permalink/tests.py b/tests/model_permalink/tests.py index 257648ca5f..ef682ed0e8 100644 --- a/tests/model_permalink/tests.py +++ b/tests/model_permalink/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.test import TestCase from .models import Guitarist diff --git a/tests/model_regress/tests.py b/tests/model_regress/tests.py index 2924b220e6..f84a40b05b 100644 --- a/tests/model_regress/tests.py +++ b/tests/model_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime from operator import attrgetter diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index 0d0fed394a..424588cf49 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from datetime import date import unittest @@ -51,6 +51,12 @@ class ModelAdminTests(TestCase): self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date']) + self.assertEqual(list(ma.get_fields(request)), + ['name', 'bio', 'sign_date']) + + self.assertEqual(list(ma.get_fields(request, self.band)), + ['name', 'bio', 'sign_date']) + def test_default_fieldsets(self): # fieldsets_add and fieldsets_change should return a special data structure that # is used in the templates. They should generate the "right thing" whether we @@ -97,6 +103,10 @@ class ModelAdminTests(TestCase): ma = BandAdmin(Band, self.site) + self.assertEqual(list(ma.get_fields(request)), ['name']) + + self.assertEqual(list(ma.get_fields(request, self.band)), ['name']) + self.assertEqual(ma.get_fieldsets(request), [(None, {'fields': ['name']})]) diff --git a/tests/multiple_database/models.py b/tests/multiple_database/models.py index e46438649b..00534c870c 100644 --- a/tests/multiple_database/models.py +++ b/tests/multiple_database/models.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index e65fa4e19d..629cb1237c 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime import pickle diff --git a/tests/mutually_referential/tests.py b/tests/mutually_referential/tests.py index b3deb0e75c..6006465843 100644 --- a/tests/mutually_referential/tests.py +++ b/tests/mutually_referential/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.test import TestCase from .models import Parent diff --git a/tests/nested_foreign_keys/tests.py b/tests/nested_foreign_keys/tests.py index 5cb23cfb9c..e8e20762fe 100644 --- a/tests/nested_foreign_keys/tests.py +++ b/tests/nested_foreign_keys/tests.py @@ -1,4 +1,5 @@ -from __future__ import absolute_import +from __future__ import unicode_literals + from django.test import TestCase from .models import Person, Movie, Event, Screening, ScreeningNullFK, Package, PackageNullFK diff --git a/tests/null_fk/tests.py b/tests/null_fk/tests.py index 96a06b6769..29e1fcb4bb 100644 --- a/tests/null_fk/tests.py +++ b/tests/null_fk/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.db.models import Q from django.test import TestCase diff --git a/tests/null_fk_ordering/tests.py b/tests/null_fk_ordering/tests.py index aea969de4f..70873244ac 100644 --- a/tests/null_fk_ordering/tests.py +++ b/tests/null_fk_ordering/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/null_queries/tests.py b/tests/null_queries/tests.py index 93e72d55d8..d08c9979d7 100644 --- a/tests/null_queries/tests.py +++ b/tests/null_queries/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase from django.core.exceptions import FieldError diff --git a/tests/one_to_one/tests.py b/tests/one_to_one/tests.py index a36764b788..6e16d81cea 100644 --- a/tests/one_to_one/tests.py +++ b/tests/one_to_one/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db import transaction, IntegrityError from django.test import TestCase diff --git a/tests/one_to_one_regress/tests.py b/tests/one_to_one_regress/tests.py index 615536ba38..7d82194abb 100644 --- a/tests/one_to_one_regress/tests.py +++ b/tests/one_to_one_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/or_lookups/tests.py b/tests/or_lookups/tests.py index e1c6fcb32a..264d999575 100644 --- a/tests/or_lookups/tests.py +++ b/tests/or_lookups/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import datetime from operator import attrgetter diff --git a/tests/order_with_respect_to/tests.py b/tests/order_with_respect_to/tests.py index 559cb1d996..155c238617 100644 --- a/tests/order_with_respect_to/tests.py +++ b/tests/order_with_respect_to/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from operator import attrgetter diff --git a/tests/ordering/tests.py b/tests/ordering/tests.py index b1b5253682..63161a3273 100644 --- a/tests/ordering/tests.py +++ b/tests/ordering/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import datetime from operator import attrgetter diff --git a/tests/pagination/tests.py b/tests/pagination/tests.py index 76799bec46..46ea428d17 100644 --- a/tests/pagination/tests.py +++ b/tests/pagination/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from datetime import datetime import unittest diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py index 2e3fee6be6..9d5a5290da 100644 --- a/tests/prefetch_related/tests.py +++ b/tests/prefetch_related/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.contrib.contenttypes.models import ContentType from django.db import connection diff --git a/tests/properties/tests.py b/tests/properties/tests.py index 8a40d06e98..c471c414a1 100644 --- a/tests/properties/tests.py +++ b/tests/properties/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/proxy_model_inheritance/app1/models.py b/tests/proxy_model_inheritance/app1/models.py index affcf140ac..a7a99fe46b 100644 --- a/tests/proxy_model_inheritance/app1/models.py +++ b/tests/proxy_model_inheritance/app1/models.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - # TODO: why can't I make this ..app2 from app2.models import NiceModel diff --git a/tests/proxy_model_inheritance/tests.py b/tests/proxy_model_inheritance/tests.py index 85bf74a4e9..dda09a080f 100644 --- a/tests/proxy_model_inheritance/tests.py +++ b/tests/proxy_model_inheritance/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import os import sys diff --git a/tests/proxy_models/tests.py b/tests/proxy_models/tests.py index 5cc5ef5478..240198cc39 100644 --- a/tests/proxy_models/tests.py +++ b/tests/proxy_models/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import copy from django.conf import settings diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 87d54b637e..6e03b0d7f6 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -1,5 +1,6 @@ -from __future__ import absolute_import,unicode_literals +from __future__ import unicode_literals +from collections import OrderedDict import datetime from operator import attrgetter import pickle @@ -14,7 +15,6 @@ from django.db.models.sql.where import WhereNode, EverythingNode, NothingNode from django.db.models.sql.datastructures import EmptyResultSet from django.test import TestCase, skipUnlessDBFeature from django.test.utils import str_prefix -from django.utils.datastructures import SortedDict from .models import ( Annotation, Article, Author, Celebrity, Child, Cover, Detail, DumbCategory, @@ -499,7 +499,7 @@ class Queries1Tests(BaseQuerysetTest): ) def test_ticket2902(self): - # Parameters can be given to extra_select, *if* you use a SortedDict. + # Parameters can be given to extra_select, *if* you use an OrderedDict. # (First we need to know which order the keys fall in "naturally" on # your system, so we can put things in the wrong way around from @@ -513,7 +513,7 @@ class Queries1Tests(BaseQuerysetTest): # This slightly odd comparison works around the fact that PostgreSQL will # return 'one' and 'two' as strings, not Unicode objects. It's a side-effect of # using constants here and not a real concern. - d = Item.objects.extra(select=SortedDict(s), select_params=params).values('a', 'b')[0] + d = Item.objects.extra(select=OrderedDict(s), select_params=params).values('a', 'b')[0] self.assertEqual(d, {'a': 'one', 'b': 'two'}) # Order by the number of tags attached to an item. @@ -789,7 +789,7 @@ class Queries1Tests(BaseQuerysetTest): ) def test_ticket7181(self): - # Ordering by related tables should accomodate nullable fields (this + # Ordering by related tables should accommodate nullable fields (this # test is a little tricky, since NULL ordering is database dependent. # Instead, we just count the number of results). self.assertEqual(len(Tag.objects.order_by('parent__name')), 5) @@ -1987,7 +1987,7 @@ class ValuesQuerysetTests(BaseQuerysetTest): def test_extra_values(self): # testing for ticket 14930 issues - qs = Number.objects.extra(select=SortedDict([('value_plus_x', 'num+%s'), + qs = Number.objects.extra(select=OrderedDict([('value_plus_x', 'num+%s'), ('value_minus_x', 'num-%s')]), select_params=(1, 2)) qs = qs.order_by('value_minus_x') @@ -2910,7 +2910,7 @@ class DoubleInSubqueryTests(TestCase): self.assertQuerysetEqual( qs, [lfb1], lambda x: x) -class Ticket18785Tests(unittest.TestCase): +class Ticket18785Tests(TestCase): def test_ticket_18785(self): # Test join trimming from ticket18785 qs = Item.objects.exclude( @@ -2920,3 +2920,38 @@ class Ticket18785Tests(unittest.TestCase): ).order_by() self.assertEqual(1, str(qs.query).count('INNER JOIN')) self.assertEqual(0, str(qs.query).count('OUTER JOIN')) + + +class Ticket20788Tests(TestCase): + def test_ticket_20788(self): + Paragraph.objects.create() + paragraph = Paragraph.objects.create() + page = paragraph.page.create() + chapter = Chapter.objects.create(paragraph=paragraph) + Book.objects.create(chapter=chapter) + + paragraph2 = Paragraph.objects.create() + Page.objects.create() + chapter2 = Chapter.objects.create(paragraph=paragraph2) + book2 = Book.objects.create(chapter=chapter2) + + sentences_not_in_pub = Book.objects.exclude( + chapter__paragraph__page=page) + self.assertQuerysetEqual( + sentences_not_in_pub, [book2], lambda x: x) + +class RelatedLookupTypeTests(TestCase): + def test_wrong_type_lookup(self): + oa = ObjectA.objects.create(name="oa") + wrong_type = Order.objects.create(id=oa.pk) + ob = ObjectB.objects.create(name="ob", objecta=oa, num=1) + # Currently Django doesn't care if the object is of correct + # type, it will just use the objecta's related fields attribute + # (id) for model lookup. Making things more restrictive could + # be a good idea... + self.assertQuerysetEqual( + ObjectB.objects.filter(objecta=wrong_type), + [ob], lambda x: x) + self.assertQuerysetEqual( + ObjectB.objects.filter(objecta__in=[wrong_type]), + [ob], lambda x: x) diff --git a/tests/queryset_pickle/models.py b/tests/queryset_pickle/models.py index 3a8973505c..dacd5018da 100644 --- a/tests/queryset_pickle/models.py +++ b/tests/queryset_pickle/models.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import datetime from django.db import models diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py index b4b540c80d..384073ad56 100644 --- a/tests/queryset_pickle/tests.py +++ b/tests/queryset_pickle/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import pickle import datetime @@ -83,10 +83,14 @@ class PickleabilityTestCase(TestCase): def test_model_pickle_dynamic(self): class Meta: proxy = True - dynclass = type("DynamicEventSubclass", (Event, ), + dynclass = type(str("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) + + def test_specialized_queryset(self): + self.assert_pickles(Happening.objects.values('name')) + self.assert_pickles(Happening.objects.values('name').dates('when', 'year')) diff --git a/tests/raw_query/tests.py b/tests/raw_query/tests.py index 7242b8309b..06c9b11230 100644 --- a/tests/raw_query/tests.py +++ b/tests/raw_query/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from datetime import date diff --git a/tests/requests/tests.py b/tests/requests/tests.py index 8c56e48f58..5bd3e5141f 100644 --- a/tests/requests/tests.py +++ b/tests/requests/tests.py @@ -620,12 +620,20 @@ class HostValidationTests(SimpleTestCase): } self.assertEqual(request.get_host(), 'example.com') + # Invalid hostnames would normally raise a SuspiciousOperation, + # but we have DEBUG=True, so this check is disabled. + request = HttpRequest() + request.META = { + 'HTTP_HOST': "invalid_hostname.com", + } + self.assertEqual(request.get_host(), "invalid_hostname.com") @override_settings(ALLOWED_HOSTS=[]) def test_get_host_suggestion_of_allowed_host(self): """get_host() makes helpful suggestions if a valid-looking host is not in ALLOWED_HOSTS.""" msg_invalid_host = "Invalid HTTP_HOST header: %r." msg_suggestion = msg_invalid_host + "You may need to add %r to ALLOWED_HOSTS." + msg_suggestion2 = msg_invalid_host + "The domain name provided is not valid according to RFC 1034/1035" for host in [ # Valid-looking hosts 'example.com', @@ -664,6 +672,14 @@ class HostValidationTests(SimpleTestCase): request.get_host ) + request = HttpRequest() + request.META = {'HTTP_HOST': "invalid_hostname.com"} + self.assertRaisesMessage( + SuspiciousOperation, + msg_suggestion2 % "invalid_hostname.com", + request.get_host + ) + @skipIf(connection.vendor == 'sqlite' and connection.settings_dict['TEST_NAME'] in (None, '', ':memory:'), diff --git a/tests/reserved_names/tests.py b/tests/reserved_names/tests.py index ddffe08d34..cdf81b8477 100644 --- a/tests/reserved_names/tests.py +++ b/tests/reserved_names/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import datetime diff --git a/tests/reverse_lookup/tests.py b/tests/reverse_lookup/tests.py index 549ee66392..ca16db0d31 100644 --- a/tests/reverse_lookup/tests.py +++ b/tests/reverse_lookup/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.core.exceptions import FieldError from django.test import TestCase diff --git a/tests/reverse_single_related/tests.py b/tests/reverse_single_related/tests.py index 0c755c4db6..472a3026b6 100644 --- a/tests/reverse_single_related/tests.py +++ b/tests/reverse_single_related/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.test import TestCase from .models import Source, Item diff --git a/tests/runtests.py b/tests/runtests.py index 8d5b375fb3..53318a7461 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -7,6 +7,20 @@ import sys import tempfile import warnings +def upath(path): + """ + Separate version of django.utils._os.upath. The django.utils version isn't + usable here, as upath is needed for RUNTESTS_DIR which is needed before + django can be imported. + """ + if sys.version_info[0] != 3 and not isinstance(path, bytes): + fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() + return path.decode(fs_encoding) + return path + +RUNTESTS_DIR = os.path.abspath(os.path.dirname(upath(__file__))) +sys.path.insert(0, os.path.dirname(RUNTESTS_DIR)) # 'tests/../' + from django import contrib from django.utils._os import upath from django.utils import six @@ -15,7 +29,6 @@ CONTRIB_MODULE_PATH = 'django.contrib' TEST_TEMPLATE_DIR = 'templates' -RUNTESTS_DIR = os.path.abspath(os.path.dirname(upath(__file__))) CONTRIB_DIR = os.path.dirname(upath(contrib.__file__)) TEMP_DIR = tempfile.mkdtemp(prefix='django_') @@ -331,10 +344,9 @@ if __name__ == "__main__": options, args = parser.parse_args() if options.settings: os.environ['DJANGO_SETTINGS_MODULE'] = options.settings - elif "DJANGO_SETTINGS_MODULE" not in os.environ: - parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. " - "Set it or use --settings.") else: + if "DJANGO_SETTINGS_MODULE" not in os.environ: + os.environ['DJANGO_SETTINGS_MODULE'] = 'test_sqlite' options.settings = os.environ['DJANGO_SETTINGS_MODULE'] if options.liveserver is not None: diff --git a/tests/save_delete_hooks/tests.py b/tests/save_delete_hooks/tests.py index 42e0d4a80e..0fd1ed4e03 100644 --- a/tests/save_delete_hooks/tests.py +++ b/tests/save_delete_hooks/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.test import TestCase from django.utils import six diff --git a/tests/select_for_update/tests.py b/tests/select_for_update/tests.py index 3204d74224..f9e7e96ac1 100644 --- a/tests/select_for_update/tests.py +++ b/tests/select_for_update/tests.py @@ -1,15 +1,17 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import sys import time import unittest from django.conf import settings -from django.db import transaction, connection +from django.db import transaction, connection, router from django.db.utils import ConnectionHandler, DEFAULT_DB_ALIAS, DatabaseError from django.test import (TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature) +from multiple_database.tests import TestRouter + from .models import Person # Some tests require threading, which might not be available. So create a @@ -254,3 +256,13 @@ class SelectForUpdateTests(TransactionTestCase): """ people = list(Person.objects.select_for_update()) self.assertTrue(transaction.is_dirty()) + + @skipUnlessDBFeature('has_select_for_update') + def test_select_for_update_on_multidb(self): + old_routers = router.routers + try: + router.routers = [TestRouter()] + query = Person.objects.select_for_update() + self.assertEqual(router.db_for_write(Person), query.db) + finally: + router.routers = old_routers diff --git a/tests/select_related/tests.py b/tests/select_related/tests.py index e6723eac9b..f07e28df99 100644 --- a/tests/select_related/tests.py +++ b/tests/select_related/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.test import TestCase diff --git a/tests/select_related_onetoone/tests.py b/tests/select_related_onetoone/tests.py index d8ba4d0484..3942b2d221 100644 --- a/tests/select_related_onetoone/tests.py +++ b/tests/select_related_onetoone/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import unittest diff --git a/tests/select_related_regress/tests.py b/tests/select_related_regress/tests.py index f6d21b2dd9..da47cb771f 100644 --- a/tests/select_related_regress/tests.py +++ b/tests/select_related_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.test import TestCase from django.utils import six diff --git a/tests/serializers/tests.py b/tests/serializers/tests.py index bff7c53249..07c220c52e 100644 --- a/tests/serializers/tests.py +++ b/tests/serializers/tests.py @@ -1,6 +1,6 @@ -from __future__ import absolute_import, unicode_literals - # -*- coding: utf-8 -*- +from __future__ import unicode_literals + import json from datetime import datetime import unittest diff --git a/tests/serializers_regress/tests.py b/tests/serializers_regress/tests.py index 1751816cee..3173f73985 100644 --- a/tests/serializers_regress/tests.py +++ b/tests/serializers_regress/tests.py @@ -6,7 +6,7 @@ test case that is capable of testing the capabilities of the serializers. This includes all valid data values, plus forward, backwards and self references. """ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime import decimal diff --git a/tests/servers/tests.py b/tests/servers/tests.py index be74afe820..1f02a88d5a 100644 --- a/tests/servers/tests.py +++ b/tests/servers/tests.py @@ -113,7 +113,7 @@ class LiveServerAddress(LiveServerBase): def test_test_test(self): # Intentionally empty method so that the test is picked up by the - # test runner and the overriden setUpClass() method is executed. + # test runner and the overridden setUpClass() method is executed. pass class LiveServerViews(LiveServerBase): diff --git a/tests/servers/urls.py b/tests/servers/urls.py index a857c45f95..03393c30ec 100644 --- a/tests/servers/urls.py +++ b/tests/servers/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url from . import views diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index 87ed4d4f5e..4031d09a58 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -163,7 +163,7 @@ class SettingsTests(TestCase): def test_override_settings_delete(self): """ - Allow deletion of a setting in an overriden settings set (#18824) + Allow deletion of a setting in an overridden settings set (#18824) """ previous_i18n = settings.USE_I18N with self.settings(USE_I18N=False): @@ -226,6 +226,27 @@ class TestComplexSettingOverride(TestCase): self.assertEqual('Overriding setting TEST_WARN can lead to unexpected behaviour.', str(w[-1].message)) +class UniqueSettngsTests(TestCase): + """ + Tests for the INSTALLED_APPS setting. + """ + settings_module = settings + + def setUp(self): + self._installed_apps = self.settings_module.INSTALLED_APPS + + def tearDown(self): + self.settings_module.INSTALLED_APPS = self._installed_apps + + def test_unique(self): + """ + An ImproperlyConfigured exception is raised if the INSTALLED_APPS contains + any duplicate strings. + """ + with self.assertRaises(ImproperlyConfigured): + self.settings_module.INSTALLED_APPS = ("myApp1", "myApp1", "myApp2", "myApp3") + + class TrailingSlashURLTests(TestCase): """ Tests for the MEDIA_URL and STATIC_URL settings. diff --git a/tests/signals/tests.py b/tests/signals/tests.py index 58f25c2868..1dfe72bf5f 100644 --- a/tests/signals/tests.py +++ b/tests/signals/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db.models import signals from django.dispatch import receiver diff --git a/tests/signals_regress/tests.py b/tests/signals_regress/tests.py index 8fb3ad5a48..2c9858efda 100644 --- a/tests/signals_regress/tests.py +++ b/tests/signals_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db import models from django.test import TestCase diff --git a/tests/sites_framework/tests.py b/tests/sites_framework/tests.py index 8e664fd501..7860394aa2 100644 --- a/tests/sites_framework/tests.py +++ b/tests/sites_framework/tests.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf import settings from django.contrib.sites.models import Site from django.test import TestCase diff --git a/tests/staticfiles_tests/tests.py b/tests/staticfiles_tests/tests.py index 912dcffe83..7200037050 100644 --- a/tests/staticfiles_tests/tests.py +++ b/tests/staticfiles_tests/tests.py @@ -650,8 +650,7 @@ class TestServeDisabled(TestServeStatic): settings.DEBUG = False def test_disabled_serving(self): - six.assertRaisesRegex(self, ImproperlyConfigured, 'The staticfiles view ' - 'can only be used in debug mode ', self._response, 'test.txt') + self.assertFileNotFound('test.txt') class TestServeStaticWithDefaultURL(TestServeStatic, TestDefaults): @@ -774,3 +773,33 @@ class TestTemplateTag(StaticFilesTestCase): self.assertStaticRenders("does/not/exist.png", "/static/does/not/exist.png") self.assertStaticRenders("testfile.txt", "/static/testfile.txt") + + +class TestAppStaticStorage(TestCase): + def setUp(self): + # Creates a python module foo_module in a directory with non ascii + # characters + self.search_path = 'search_path_\xc3\xbc' + os.mkdir(self.search_path) + module_path = os.path.join(self.search_path, 'foo_module') + os.mkdir(module_path) + open(os.path.join(module_path, '__init__.py'), 'w') + sys.path.append(os.path.abspath(self.search_path)) + + def tearDown(self): + sys.path.remove(os.path.abspath(self.search_path)) + shutil.rmtree(self.search_path) + + def test_app_with_non_ascii_characters_in_path(self): + """ + Regression test for #18404 - Tests AppStaticStorage with a module that + has non ascii characters in path and a non utf8 file system encoding + """ + # set file system encoding to a non unicode encoding + old_enc_func = sys.getfilesystemencoding + sys.getfilesystemencoding = lambda: 'ISO-8859-1' + try: + st = storage.AppStaticStorage('foo_module') + st.path('bar') + finally: + sys.getfilesystemencoding = old_enc_func diff --git a/tests/str/tests.py b/tests/str/tests.py index d82908a0ee..3c0bc079c8 100644 --- a/tests/str/tests.py +++ b/tests/str/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime from unittest import skipIf diff --git a/tests/string_lookup/tests.py b/tests/string_lookup/tests.py index b011720ddf..5a17e55560 100644 --- a/tests/string_lookup/tests.py +++ b/tests/string_lookup/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.test import TestCase from .models import Foo, Whiz, Bar, Article, Base, Child @@ -80,4 +80,4 @@ class StringLookupTests(TestCase): self.assertEqual(repr(Article.objects.filter(submitted_from__contains='192.0.2')), repr([a])) # Test that the searches do not match the subnet mask (/32 in this case) - self.assertEqual(Article.objects.filter(submitted_from__contains='32').count(), 0)
\ No newline at end of file + self.assertEqual(Article.objects.filter(submitted_from__contains='32').count(), 0) diff --git a/tests/swappable_models/tests.py b/tests/swappable_models/tests.py index 858061db23..2e2d544cea 100644 --- a/tests/swappable_models/tests.py +++ b/tests/swappable_models/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.utils.six import StringIO diff --git a/tests/syndication/feeds.py b/tests/syndication/feeds.py index 1cd5c3d988..f8ffb4b2e6 100644 --- a/tests/syndication/feeds.py +++ b/tests/syndication/feeds.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.contrib.syndication import views from django.core.exceptions import ObjectDoesNotExist diff --git a/tests/syndication/tests.py b/tests/syndication/tests.py index d3b0058d53..8bc6b04939 100644 --- a/tests/syndication/tests.py +++ b/tests/syndication/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from xml.dom import minidom diff --git a/tests/syndication/urls.py b/tests/syndication/urls.py index 06a75a4e68..1b5d77f2e1 100644 --- a/tests/syndication/urls.py +++ b/tests/syndication/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns from . import feeds diff --git a/tests/tablespaces/tests.py b/tests/tablespaces/tests.py index 1eaddef079..088938ca1d 100644 --- a/tests/tablespaces/tests.py +++ b/tests/tablespaces/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import copy diff --git a/tests/template_tests/alternate_urls.py b/tests/template_tests/alternate_urls.py index fa4985a9dc..3c6a4e62f9 100644 --- a/tests/template_tests/alternate_urls.py +++ b/tests/template_tests/alternate_urls.py @@ -1,7 +1,3 @@ -# coding: utf-8 - -from __future__ import absolute_import - from django.conf.urls import patterns, url from . import views diff --git a/tests/template_tests/test_custom.py b/tests/template_tests/test_custom.py index e941bc223e..c2dff03b10 100644 --- a/tests/template_tests/test_custom.py +++ b/tests/template_tests/test_custom.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from unittest import TestCase diff --git a/tests/template_tests/loaders.py b/tests/template_tests/test_loaders.py index 497b422e7f..6fd17fd04b 100644 --- a/tests/template_tests/loaders.py +++ b/tests/template_tests/test_loaders.py @@ -11,10 +11,15 @@ if __name__ == '__main__': import imp import os.path -import pkg_resources import sys import unittest +try: + import pkg_resources +except ImportError: + pkg_resources = None + + from django.template import TemplateDoesNotExist, Context from django.template.loaders.eggs import Loader as EggLoader from django.template import loader @@ -24,23 +29,6 @@ from django.utils.six import StringIO # Mock classes and objects for pkg_resources functions. -class MockProvider(pkg_resources.NullProvider): - def __init__(self, module): - pkg_resources.NullProvider.__init__(self, module) - self.module = module - - def _has(self, path): - return path in self.module._resources - - def _isdir(self, path): - return False - - def get_resource_stream(self, manager, resource_name): - return self.module._resources[resource_name] - - def _get(self, path): - return self.module._resources[path].read() - class MockLoader(object): pass @@ -57,8 +45,27 @@ def create_egg(name, resources): sys.modules[name] = egg +@unittest.skipUnless(pkg_resources, 'setuptools is not installed') class EggLoaderTest(unittest.TestCase): def setUp(self): + # Defined here b/c at module scope we may not have pkg_resources + class MockProvider(pkg_resources.NullProvider): + def __init__(self, module): + pkg_resources.NullProvider.__init__(self, module) + self.module = module + + def _has(self, path): + return path in self.module._resources + + def _isdir(self, path): + return False + + def get_resource_stream(self, manager, resource_name): + return self.module._resources[resource_name] + + def _get(self, path): + return self.module._resources[path].read() + pkg_resources._provider_factories[MockLoader] = MockProvider self.empty_egg = create_egg("egg_empty", {}) diff --git a/tests/template_tests/test_response.py b/tests/template_tests/test_response.py index 6acc45626f..65f7531361 100644 --- a/tests/template_tests/test_response.py +++ b/tests/template_tests/test_response.py @@ -95,7 +95,7 @@ class SimpleTemplateResponseTest(TestCase): self.assertEqual(response.content, b'foo') def test_set_content(self): - # content can be overriden + # content can be overridden response = self._response() self.assertFalse(response.is_rendered) response.content = 'spam' diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index 6b8a106623..aa0283be79 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.conf import settings @@ -8,8 +8,7 @@ if __name__ == '__main__': # before importing 'template'. settings.configure() -from datetime import date, datetime, timedelta -import time +from datetime import date, datetime import os import sys import traceback @@ -31,21 +30,12 @@ from django.test.utils import (setup_test_template_loader, from django.utils.encoding import python_2_unicode_compatible from django.utils.formats import date_format from django.utils._os import upath -from django.utils.translation import activate, deactivate, ugettext as _ +from django.utils.translation import activate, deactivate from django.utils.safestring import mark_safe from django.utils import six -from django.utils.tzinfo import LocalTimezone from i18n import TransRealMixin -try: - from .loaders import RenderToStringTest, EggLoaderTest -except ImportError as e: - if "pkg_resources" in e.args[0]: - pass # If setuptools isn't installed, that's fine. Just move on. - else: - raise - # NumPy installed? try: import numpy diff --git a/tests/template_tests/urls.py b/tests/template_tests/urls.py index b5498fade1..f3720bbda7 100644 --- a/tests/template_tests/urls.py +++ b/tests/template_tests/urls.py @@ -1,5 +1,5 @@ # coding: utf-8 -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.conf.urls import patterns, url from . import views diff --git a/tests/templates/custom_admin/app_index.html b/tests/templates/custom_admin/app_index.html new file mode 100644 index 0000000000..3dfae814a7 --- /dev/null +++ b/tests/templates/custom_admin/app_index.html @@ -0,0 +1,6 @@ +{% extends "admin/app_index.html" %} + +{% block content %} +Hello from a custom app_index template +{{ block.super }} +{% endblock %} diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index 0f3cba7e88..85637b982e 100644 --- a/tests/test_client/tests.py +++ b/tests/test_client/tests.py @@ -20,7 +20,7 @@ testing against the contexts and templates produced by a view, rather than the HTML rendered to the end-user. """ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.conf import settings from django.core import mail diff --git a/tests/test_client/urls.py b/tests/test_client/urls.py index bd395ca552..4d2f4fb86e 100644 --- a/tests/test_client/urls.py +++ b/tests/test_client/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns from django.views.generic import RedirectView diff --git a/tests/test_client_regress/urls.py b/tests/test_client_regress/urls.py index 1332537d57..6a0b330e02 100644 --- a/tests/test_client_regress/urls.py +++ b/tests/test_client_regress/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url from django.views.generic import RedirectView diff --git a/tests/test_runner/tests.py b/tests/test_runner/tests.py index 72200e40d3..7aefa4e077 100644 --- a/tests/test_runner/tests.py +++ b/tests/test_runner/tests.py @@ -1,8 +1,9 @@ """ Tests for django test runner """ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals +from importlib import import_module from optparse import make_option import sys import unittest @@ -13,7 +14,6 @@ from django import db from django.test import runner, TestCase, TransactionTestCase, skipUnlessDBFeature from django.test.testcases import connections_support_transactions from django.test.utils import IgnoreAllDeprecationWarningsMixin -from django.utils.importlib import import_module from admin_scripts.tests import AdminScriptTestCase from .models import Person diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 24aa433dd3..eb850cc3be 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import unittest diff --git a/tests/test_utils/urls.py b/tests/test_utils/urls.py index 31fc5cc7fc..65e8631735 100644 --- a/tests/test_utils/urls.py +++ b/tests/test_utils/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns from . import views diff --git a/tests/test_utils/views.py b/tests/test_utils/views.py index 5495488e2c..77e598b72c 100644 --- a/tests/test_utils/views.py +++ b/tests/test_utils/views.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.http import HttpResponse from django.shortcuts import get_object_or_404 @@ -8,4 +6,4 @@ from .models import Person def get_person(request, pk): person = get_object_or_404(Person, pk=pk) - return HttpResponse(person.name)
\ No newline at end of file + return HttpResponse(person.name) diff --git a/tests/timezones/admin.py b/tests/timezones/admin.py index 4c199813e2..81b49a4ab6 100644 --- a/tests/timezones/admin.py +++ b/tests/timezones/admin.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.contrib import admin from .models import Event, Timestamp diff --git a/tests/timezones/urls.py b/tests/timezones/urls.py index e4f42972db..e9a7a90df9 100644 --- a/tests/timezones/urls.py +++ b/tests/timezones/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, include from django.contrib import admin diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py index afb573f366..9cf8b4d742 100644 --- a/tests/transactions/tests.py +++ b/tests/transactions/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import sys from unittest import skipIf, skipUnless diff --git a/tests/transactions_regress/tests.py b/tests/transactions_regress/tests.py index 01f8fc4186..2376bcd277 100644 --- a/tests/transactions_regress/tests.py +++ b/tests/transactions_regress/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from unittest import skipIf, skipUnless diff --git a/tests/unmanaged_models/tests.py b/tests/unmanaged_models/tests.py index d7cf961a37..7b23a46096 100644 --- a/tests/unmanaged_models/tests.py +++ b/tests/unmanaged_models/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db import connection from django.test import TestCase diff --git a/tests/update/tests.py b/tests/update/tests.py index 7a1177c1fd..95e9069972 100644 --- a/tests/update/tests.py +++ b/tests/update/tests.py @@ -1,8 +1,8 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.test import TestCase -from .models import A, B, C, D, DataPoint, RelatedPoint +from .models import A, B, D, DataPoint, RelatedPoint class SimpleTest(TestCase): @@ -51,6 +51,15 @@ class SimpleTest(TestCase): cnt = D.objects.filter(y=100).count() self.assertEqual(cnt, 0) + def test_foreign_key_update_with_id(self): + """ + Test that update works using <field>_id for foreign keys + """ + num_updated = self.a1.d_set.update(a_id=self.a2) + self.assertEqual(num_updated, 20) + self.assertEqual(self.a2.d_set.count(), 20) + + class AdvancedTests(TestCase): def setUp(self): @@ -115,4 +124,4 @@ class AdvancedTests(TestCase): """ method = DataPoint.objects.all()[:2].update self.assertRaises(AssertionError, method, - another_value='another thing') + another_value='another thing') diff --git a/tests/update_only_fields/tests.py b/tests/update_only_fields/tests.py index 97c05ddc79..1f85c3bbb2 100644 --- a/tests/update_only_fields/tests.py +++ b/tests/update_only_fields/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals from django.db.models.signals import pre_save, post_save from django.test import TestCase diff --git a/tests/urlpatterns_reverse/extra_urls.py b/tests/urlpatterns_reverse/extra_urls.py index d3a04e6b31..94e225fb46 100644 --- a/tests/urlpatterns_reverse/extra_urls.py +++ b/tests/urlpatterns_reverse/extra_urls.py @@ -2,8 +2,6 @@ Some extra URL patterns that are included at the top level. """ -from __future__ import absolute_import - from django.conf.urls import patterns, url, include from .views import empty_view diff --git a/tests/urlpatterns_reverse/included_named_urls.py b/tests/urlpatterns_reverse/included_named_urls.py index 353aed255e..366fe9b57a 100644 --- a/tests/urlpatterns_reverse/included_named_urls.py +++ b/tests/urlpatterns_reverse/included_named_urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url, include from .views import empty_view diff --git a/tests/urlpatterns_reverse/included_named_urls2.py b/tests/urlpatterns_reverse/included_named_urls2.py index b31bdb1f7e..b8e4c531fa 100644 --- a/tests/urlpatterns_reverse/included_named_urls2.py +++ b/tests/urlpatterns_reverse/included_named_urls2.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url from .views import empty_view diff --git a/tests/urlpatterns_reverse/included_namespace_urls.py b/tests/urlpatterns_reverse/included_namespace_urls.py index 7a2096ecd9..ae098a64dc 100644 --- a/tests/urlpatterns_reverse/included_namespace_urls.py +++ b/tests/urlpatterns_reverse/included_namespace_urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url, include from .namespace_urls import URLObject diff --git a/tests/urlpatterns_reverse/included_urls.py b/tests/urlpatterns_reverse/included_urls.py index c8c9001843..af6b6882e0 100644 --- a/tests/urlpatterns_reverse/included_urls.py +++ b/tests/urlpatterns_reverse/included_urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url from .views import empty_view diff --git a/tests/urlpatterns_reverse/included_urls2.py b/tests/urlpatterns_reverse/included_urls2.py index 98605047f4..9dcafc8535 100644 --- a/tests/urlpatterns_reverse/included_urls2.py +++ b/tests/urlpatterns_reverse/included_urls2.py @@ -5,8 +5,6 @@ each name to resolve and Django must distinguish the possibilities based on the argument list. """ -from __future__ import absolute_import - from django.conf.urls import patterns, url from .views import empty_view diff --git a/tests/urlpatterns_reverse/middleware.py b/tests/urlpatterns_reverse/middleware.py index fbf577786e..0de692835f 100644 --- a/tests/urlpatterns_reverse/middleware.py +++ b/tests/urlpatterns_reverse/middleware.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.core.urlresolvers import reverse from django.http import HttpResponse, StreamingHttpResponse diff --git a/tests/urlpatterns_reverse/named_urls.py b/tests/urlpatterns_reverse/named_urls.py index 3290cab29c..d4d35bac33 100644 --- a/tests/urlpatterns_reverse/named_urls.py +++ b/tests/urlpatterns_reverse/named_urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url, include from .views import empty_view diff --git a/tests/urlpatterns_reverse/namespace_urls.py b/tests/urlpatterns_reverse/namespace_urls.py index cf960070ec..014bc65990 100644 --- a/tests/urlpatterns_reverse/namespace_urls.py +++ b/tests/urlpatterns_reverse/namespace_urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url, include from .views import view_class_instance diff --git a/tests/urlpatterns_reverse/reverse_lazy_urls.py b/tests/urlpatterns_reverse/reverse_lazy_urls.py index 693c6e1b38..0ef0a1f313 100644 --- a/tests/urlpatterns_reverse/reverse_lazy_urls.py +++ b/tests/urlpatterns_reverse/reverse_lazy_urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url from .views import empty_view, LazyRedirectView, login_required_view diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index 222ebe053b..aef0fa0514 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -1,7 +1,7 @@ """ Unit tests for reverse URL lookups. """ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import unittest diff --git a/tests/urlpatterns_reverse/urlconf_outer.py b/tests/urlpatterns_reverse/urlconf_outer.py index 0cdebf83ff..20b9b09f98 100644 --- a/tests/urlpatterns_reverse/urlconf_outer.py +++ b/tests/urlpatterns_reverse/urlconf_outer.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url, include from . import urlconf_inner @@ -8,4 +6,4 @@ from . import urlconf_inner urlpatterns = patterns('', url(r'^test/me/$', urlconf_inner.inner_view, name='outer'), url(r'^inner_urlconf/', include(urlconf_inner.__name__)) -)
\ No newline at end of file +) diff --git a/tests/urlpatterns_reverse/urls.py b/tests/urlpatterns_reverse/urls.py index 1dbc8d889f..cb08e2e664 100644 --- a/tests/urlpatterns_reverse/urls.py +++ b/tests/urlpatterns_reverse/urls.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django.conf.urls import patterns, url, include from .views import empty_view, absolute_kwargs_view diff --git a/tests/urlpatterns_reverse/urls_error_handlers_callables.py b/tests/urlpatterns_reverse/urls_error_handlers_callables.py index befeccaf45..0900ec94ba 100644 --- a/tests/urlpatterns_reverse/urls_error_handlers_callables.py +++ b/tests/urlpatterns_reverse/urls_error_handlers_callables.py @@ -1,7 +1,5 @@ # Used by the ErrorHandlerResolutionTests test case. -from __future__ import absolute_import - from django.conf.urls import patterns from .views import empty_view diff --git a/tests/urlpatterns_reverse/urls_without_full_import.py b/tests/urlpatterns_reverse/urls_without_full_import.py index ca3e424f23..c1dce5549c 100644 --- a/tests/urlpatterns_reverse/urls_without_full_import.py +++ b/tests/urlpatterns_reverse/urls_without_full_import.py @@ -1,8 +1,6 @@ # A URLs file that doesn't use the default # from django.conf.urls import * # import pattern. -from __future__ import absolute_import - from django.conf.urls import patterns, url from .views import empty_view, bad_view diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py index 1f2e19d8d5..74d94ea19d 100644 --- a/tests/utils_tests/test_html.py +++ b/tests/utils_tests/test_html.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from __future__ import unicode_literals from datetime import datetime @@ -181,3 +182,13 @@ class TestUtilsHtml(TestCase): ) for value, tags, output in items: self.assertEqual(f(value, tags), output) + + def test_smart_urlquote(self): + quote = html.smart_urlquote + # Ensure that IDNs are properly quoted + self.assertEqual(quote('http://öäü.com/'), 'http://xn--4ca9at.com/') + self.assertEqual(quote('http://öäü.com/öäü/'), 'http://xn--4ca9at.com/%C3%B6%C3%A4%C3%BC/') + # Ensure that everything unsafe is quoted, !*'();:@&=+$,/?#[]~ is considered safe as per RFC + self.assertEqual(quote('http://example.com/path/öäü/'), 'http://example.com/path/%C3%B6%C3%A4%C3%BC/') + self.assertEqual(quote('http://example.com/%C3%B6/ä/'), 'http://example.com/%C3%B6/%C3%A4/') + self.assertEqual(quote('http://example.com/?x=1&y=2'), 'http://example.com/?x=1&y=2') diff --git a/tests/utils_tests/test_module_loading.py b/tests/utils_tests/test_module_loading.py index 5a43a927bf..2423215778 100644 --- a/tests/utils_tests/test_module_loading.py +++ b/tests/utils_tests/test_module_loading.py @@ -1,11 +1,11 @@ import imp +from importlib import import_module import os import sys import unittest from zipimport import zipimporter from django.core.exceptions import ImproperlyConfigured -from django.utils.importlib import import_module from django.utils.module_loading import import_by_path, module_has_submodule from django.utils._os import upath diff --git a/tests/utils_tests/test_safestring.py b/tests/utils_tests/test_safestring.py index a6f0c0a01c..5d4528a9a8 100644 --- a/tests/utils_tests/test_safestring.py +++ b/tests/utils_tests/test_safestring.py @@ -1,5 +1,4 @@ -from __future__ import absolute_import, unicode_literals - +from __future__ import unicode_literals from django.template import Template, Context from django.test import TestCase diff --git a/tests/validation/test_custom_messages.py b/tests/validation/test_custom_messages.py index c5a1ee744f..2e259b7aef 100644 --- a/tests/validation/test_custom_messages.py +++ b/tests/validation/test_custom_messages.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from . import ValidationTestCase from .models import CustomMessagesModel diff --git a/tests/validation/test_unique.py b/tests/validation/test_unique.py index a481fcb1c4..bca1b36c93 100644 --- a/tests/validation/test_unique.py +++ b/tests/validation/test_unique.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import datetime import unittest diff --git a/tests/validation/test_validators.py b/tests/validation/test_validators.py index e58d9fd4a6..c3875d4601 100644 --- a/tests/validation/test_validators.py +++ b/tests/validation/test_validators.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from . import ValidationTestCase from .models import ModelToValidate diff --git a/tests/validation/tests.py b/tests/validation/tests.py index c8b679541a..34cde3fc8a 100644 --- a/tests/validation/tests.py +++ b/tests/validation/tests.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django import forms from django.core.exceptions import NON_FIELD_ERRORS diff --git a/tests/view_tests/generic_urls.py b/tests/view_tests/generic_urls.py index c3ac1fcafa..10e7601eb6 100644 --- a/tests/view_tests/generic_urls.py +++ b/tests/view_tests/generic_urls.py @@ -1,5 +1,5 @@ # -*- coding:utf-8 -*- -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.conf.urls import patterns, url from django.views.generic import RedirectView diff --git a/tests/view_tests/templatetags/debugtags.py b/tests/view_tests/templatetags/debugtags.py index cd2d2d9ad2..9e1945cb23 100644 --- a/tests/view_tests/templatetags/debugtags.py +++ b/tests/view_tests/templatetags/debugtags.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from django import template from ..views import BrokenException diff --git a/tests/view_tests/tests/__init__.py b/tests/view_tests/tests/__init__.py index bfb2ed7376..dae149a8ef 100644 --- a/tests/view_tests/tests/__init__.py +++ b/tests/view_tests/tests/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from .test_debug import (DebugViewTests, ExceptionReporterTests, ExceptionReporterTests, PlainTextReportTests, ExceptionReporterFilterTests, AjaxResponseExceptionReporterFilter) diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index bc77fc351a..0159886918 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # This coding header is significant for tests, as the debug view is parsing # files to search for such a header to decode the source file content -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals import inspect import os diff --git a/tests/view_tests/tests/test_defaults.py b/tests/view_tests/tests/test_defaults.py index 5efd338d34..d55ed53454 100644 --- a/tests/view_tests/tests/test_defaults.py +++ b/tests/view_tests/tests/test_defaults.py @@ -1,8 +1,9 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import unicode_literals from django.contrib.contenttypes.models import ContentType from django.test import TestCase -from django.test.utils import setup_test_template_loader, restore_template_loaders +from django.test.utils import (setup_test_template_loader, + restore_template_loaders, override_settings) from ..models import Author, Article, UrlArticle @@ -59,3 +60,20 @@ class DefaultsTests(TestCase): article = UrlArticle.objects.get(pk=1) self.assertTrue(getattr(article.get_absolute_url, 'purge', False), 'The attributes of the original get_absolute_url must be added.') + + @override_settings(DEFAULT_CONTENT_TYPE="text/xml") + def test_default_content_type_is_text_html(self): + """ + Content-Type of the default error responses is text/html. Refs #20822. + """ + response = self.client.get('/views/raises400/') + self.assertEqual(response['Content-Type'], 'text/html') + + response = self.client.get('/views/raises403/') + self.assertEqual(response['Content-Type'], 'text/html') + + response = self.client.get('/views/non_existing_url/') + self.assertEqual(response['Content-Type'], 'text/html') + + response = self.client.get('/views/server_error/') + self.assertEqual(response['Content-Type'], 'text/html') diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index c1852ee71f..cfee4f5093 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -1,6 +1,4 @@ # -*- coding:utf-8 -*- -from __future__ import absolute_import - import gettext import os from os import path @@ -200,26 +198,22 @@ class JavascriptI18nTests(LiveServerTestCase): @override_settings(LANGUAGE_CODE='de') def test_javascript_gettext(self): - extended_apps = list(settings.INSTALLED_APPS) + ['view_tests'] - with self.settings(INSTALLED_APPS=extended_apps): - self.selenium.get('%s%s' % (self.live_server_url, '/jsi18n_template/')) + self.selenium.get('%s%s' % (self.live_server_url, '/jsi18n_template/')) - elem = self.selenium.find_element_by_id("gettext") - self.assertEqual(elem.text, "Entfernen") - elem = self.selenium.find_element_by_id("ngettext_sing") - self.assertEqual(elem.text, "1 Element") - elem = self.selenium.find_element_by_id("ngettext_plur") - self.assertEqual(elem.text, "455 Elemente") - elem = self.selenium.find_element_by_id("pgettext") - self.assertEqual(elem.text, "Kann") - elem = self.selenium.find_element_by_id("npgettext_sing") - self.assertEqual(elem.text, "1 Resultat") - elem = self.selenium.find_element_by_id("npgettext_plur") - self.assertEqual(elem.text, "455 Resultate") + elem = self.selenium.find_element_by_id("gettext") + self.assertEqual(elem.text, "Entfernen") + elem = self.selenium.find_element_by_id("ngettext_sing") + self.assertEqual(elem.text, "1 Element") + elem = self.selenium.find_element_by_id("ngettext_plur") + self.assertEqual(elem.text, "455 Elemente") + elem = self.selenium.find_element_by_id("pgettext") + self.assertEqual(elem.text, "Kann") + elem = self.selenium.find_element_by_id("npgettext_sing") + self.assertEqual(elem.text, "1 Resultat") + elem = self.selenium.find_element_by_id("npgettext_plur") + self.assertEqual(elem.text, "455 Resultate") def test_escaping(self): - extended_apps = list(settings.INSTALLED_APPS) + ['view_tests'] - with self.settings(INSTALLED_APPS=extended_apps): - # Force a language via GET otherwise the gettext functions are a noop! - response = self.client.get('/jsi18n_admin/?language=de') - self.assertContains(response, '\\x04') + # Force a language via GET otherwise the gettext functions are a noop! + response = self.client.get('/jsi18n_admin/?language=de') + self.assertContains(response, '\\x04') diff --git a/tests/view_tests/tests/test_static.py b/tests/view_tests/tests/test_static.py index 6104ad063e..d2f3f47fa7 100644 --- a/tests/view_tests/tests/test_static.py +++ b/tests/view_tests/tests/test_static.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import mimetypes from os import path diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py index d792e47ddf..f2c910368d 100644 --- a/tests/view_tests/urls.py +++ b/tests/view_tests/urls.py @@ -1,6 +1,4 @@ # coding: utf-8 -from __future__ import absolute_import - from os import path from django.conf.urls import patterns, url, include @@ -47,8 +45,10 @@ urlpatterns = patterns('', # a view that raises an exception for the debug view (r'raises/$', views.raises), - (r'raises404/$', views.raises404), + + (r'raises400/$', views.raises400), (r'raises403/$', views.raises403), + (r'raises404/$', views.raises404), # i18n views (r'^i18n/', include('django.conf.urls.i18n')), diff --git a/tests/view_tests/views.py b/tests/view_tests/views.py index 1cfafa4333..0bac7d9321 100644 --- a/tests/view_tests/views.py +++ b/tests/view_tests/views.py @@ -1,8 +1,8 @@ -from __future__ import absolute_import +from __future__ import unicode_literals import sys -from django.core.exceptions import PermissionDenied +from django.core.exceptions import PermissionDenied, SuspiciousOperation from django.core.urlresolvers import get_resolver from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response, render @@ -31,13 +31,16 @@ def raises(request): except Exception: return technical_500_response(request, *sys.exc_info()) -def raises404(request): - resolver = get_resolver(None) - resolver.resolve('') +def raises400(request): + raise SuspiciousOperation def raises403(request): raise PermissionDenied +def raises404(request): + resolver = get_resolver(None) + resolver.resolve('') + def redirect(request): """ Forces an HTTP redirect. |
