summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2013-06-28 17:32:57 +0100
committerAndrew Godwin <andrew@aeracode.org>2013-06-28 17:32:57 +0100
commit7a47ba6f6aeca36b8b092dbafd36abb342d34d4b (patch)
treee959922f7d4d08057225459e31697f410e26df26 /tests
parente2d7e83256234251a81ad3388428f6579795a672 (diff)
parent48dd1e63bbb93479666208535a56f8c7c4aeab3a (diff)
Merge remote-tracking branch 'core/master' into schema-alteration
Conflicts: django/db/backends/__init__.py django/db/models/fields/related.py tests/field_deconstruction/tests.py
Diffstat (limited to 'tests')
-rw-r--r--tests/admin_views/models.py2
-rw-r--r--tests/admin_views/tests.py26
-rw-r--r--tests/backends/tests.py81
-rw-r--r--tests/check/__init__.py (renamed from tests/compat_checks/__init__.py)0
-rw-r--r--tests/check/models.py (renamed from tests/compat_checks/models.py)0
-rw-r--r--tests/check/tests.py (renamed from tests/compat_checks/tests.py)22
-rw-r--r--tests/field_deconstruction/tests.py2
-rw-r--r--tests/files/tests.py14
-rw-r--r--tests/foreign_object/models.py8
-rw-r--r--tests/foreign_object/tests.py21
-rw-r--r--tests/forms_tests/tests/test_fields.py4
-rw-r--r--tests/forms_tests/tests/test_formsets.py3
-rw-r--r--tests/generic_views/test_edit.py21
-rw-r--r--tests/i18n/commands/__init__.py6
-rw-r--r--tests/i18n/commands/extraction.py19
-rw-r--r--tests/i18n/commands/templates/plural.djtpl6
-rw-r--r--tests/lookup/tests.py22
-rw-r--r--tests/model_formsets/tests.py21
-rw-r--r--tests/raw_query/tests.py21
-rw-r--r--tests/servers/tests.py5
-rw-r--r--tests/settings_tests/tests.py27
-rw-r--r--tests/template_tests/tests.py4
-rw-r--r--tests/transactions/tests.py26
23 files changed, 329 insertions, 32 deletions
diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py
index 5cc6f6251a..5ec4fbb544 100644
--- a/tests/admin_views/models.py
+++ b/tests/admin_views/models.py
@@ -137,7 +137,7 @@ class Thing(models.Model):
class Actor(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
- title = models.CharField(max_length=50, null=True)
+ title = models.CharField(max_length=50, null=True, blank=True)
def __str__(self):
return self.name
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 528c728069..235fe0cb54 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -57,7 +57,7 @@ for a staff account. Note that both fields may be case-sensitive."
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
-class AdminViewBasicTest(TestCase):
+class AdminViewBasicTestCase(TestCase):
fixtures = ['admin-views-users.xml', 'admin-views-colors.xml',
'admin-views-fabrics.xml', 'admin-views-books.xml']
@@ -92,6 +92,7 @@ class AdminViewBasicTest(TestCase):
failing_msg
)
+class AdminViewBasicTest(AdminViewBasicTestCase):
def testTrailingSlashRequired(self):
"""
If you leave off the trailing slash, app should redirect and add it.
@@ -583,6 +584,14 @@ class AdminViewBasicTest(TestCase):
response = self.client.get("/test_admin/admin/admin_views/inquisition/?leader__name=Palin&leader__age=27")
self.assertEqual(response.status_code, 200)
+ def test_popup_dismiss_related(self):
+ """
+ Regression test for ticket 20664 - ensure the pk is properly quoted.
+ """
+ actor = Actor.objects.create(name="Palin", age=27)
+ response = self.client.get("/test_admin/admin/admin_views/actor/?%s" % IS_POPUP_VAR)
+ self.assertContains(response, "opener.dismissRelatedLookupPopup(window, &#39;%s&#39;)" % actor.pk)
+
def test_hide_change_password(self):
"""
Tests if the "change password" link in the admin is hidden if the User
@@ -753,7 +762,7 @@ class SaveAsTests(TestCase):
self.assertEqual(response.context['form_url'], '/test_admin/admin/admin_views/person/add/')
-class CustomModelAdminTest(AdminViewBasicTest):
+class CustomModelAdminTest(AdminViewBasicTestCase):
urls = "admin_views.urls"
urlbit = "admin2"
@@ -2554,6 +2563,17 @@ action)</option>
'/test_admin/admin/admin_views/subscriber/?%s' % IS_POPUP_VAR)
self.assertEqual(response.context["action_form"], None)
+ def test_popup_template_response(self):
+ """
+ Success on popups shall be rendered from template in order to allow
+ easy customization.
+ """
+ response = self.client.post(
+ '/test_admin/admin/admin_views/actor/add/?%s=1' % IS_POPUP_VAR,
+ {'name': 'Troy McClure', 'age': '55', IS_POPUP_VAR: '1'})
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.template_name, 'admin/popup_response.html')
+
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class TestCustomChangeList(TestCase):
@@ -4275,7 +4295,7 @@ class AdminKeepChangeListFiltersTests(TestCase):
# Get the `add_view`.
response = self.client.get(self.get_add_url())
self.assertEqual(response.status_code, 200)
-
+
# Check the form action.
form_action = """<form enctype="multipart/form-data" action="?%s" method="post" id="user_form">""" % self.get_preserved_filters_querystring()
self.assertContains(response, form_action, count=1)
diff --git a/tests/backends/tests.py b/tests/backends/tests.py
index b6e3e9e36f..c1a26df7fc 100644
--- a/tests/backends/tests.py
+++ b/tests/backends/tests.py
@@ -11,9 +11,12 @@ from django.core.management.color import no_style
from django.db import (connection, connections, DEFAULT_DB_ALIAS,
DatabaseError, IntegrityError, transaction)
from django.db.backends.signals import connection_created
+from django.db.backends.sqlite3.base import DatabaseOperations
from django.db.backends.postgresql_psycopg2 import version as pg_version
from django.db.backends.util import format_number
from django.db.models import Sum, Avg, Variance, StdDev
+from django.db.models.fields import (AutoField, DateField, DateTimeField,
+ DecimalField, IntegerField, TimeField)
from django.db.utils import ConnectionHandler
from django.test import (TestCase, skipUnlessDBFeature, skipIfDBFeature,
TransactionTestCase)
@@ -402,7 +405,7 @@ class EscapingChecksDebug(EscapingChecks):
pass
-class SqlliteAggregationTests(TestCase):
+class SqliteAggregationTests(TestCase):
"""
#19360: Raise NotImplementedError when aggregating on date/time fields.
"""
@@ -418,16 +421,59 @@ class SqlliteAggregationTests(TestCase):
models.Item.objects.all().aggregate, aggregate('last_modified'))
+class SqliteChecks(TestCase):
+
+ @unittest.skipUnless(connection.vendor == 'sqlite',
+ "No need to do SQLite checks")
+ def test_convert_values_to_handle_null_value(self):
+ database_operations = DatabaseOperations(connection)
+ self.assertEqual(
+ None,
+ database_operations.convert_values(None, AutoField(primary_key=True))
+ )
+ self.assertEqual(
+ None,
+ database_operations.convert_values(None, DateField())
+ )
+ self.assertEqual(
+ None,
+ database_operations.convert_values(None, DateTimeField())
+ )
+ self.assertEqual(
+ None,
+ database_operations.convert_values(None, DecimalField())
+ )
+ self.assertEqual(
+ None,
+ database_operations.convert_values(None, IntegerField())
+ )
+ self.assertEqual(
+ None,
+ database_operations.convert_values(None, TimeField())
+ )
+
+
class BackendTestCase(TestCase):
def create_squares_with_executemany(self, args):
+ self.create_squares(args, 'format', True)
+
+ def create_squares(self, args, paramstyle, multiple):
cursor = connection.cursor()
opts = models.Square._meta
tbl = connection.introspection.table_name_converter(opts.db_table)
f1 = connection.ops.quote_name(opts.get_field('root').column)
f2 = connection.ops.quote_name(opts.get_field('square').column)
- query = 'INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % (tbl, f1, f2)
- cursor.executemany(query, args)
+ if paramstyle=='format':
+ query = 'INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % (tbl, f1, f2)
+ elif paramstyle=='pyformat':
+ query = 'INSERT INTO %s (%s, %s) VALUES (%%(root)s, %%(square)s)' % (tbl, f1, f2)
+ else:
+ raise ValueError("unsupported paramstyle in test")
+ if multiple:
+ cursor.executemany(query, args)
+ else:
+ cursor.execute(query, args)
def test_cursor_executemany(self):
#4896: Test cursor.executemany
@@ -456,6 +502,35 @@ class BackendTestCase(TestCase):
self.create_squares_with_executemany(args)
self.assertEqual(models.Square.objects.count(), 9)
+ @skipUnlessDBFeature('supports_paramstyle_pyformat')
+ def test_cursor_execute_with_pyformat(self):
+ #10070: Support pyformat style passing of paramters
+ args = {'root': 3, 'square': 9}
+ self.create_squares(args, 'pyformat', multiple=False)
+ self.assertEqual(models.Square.objects.count(), 1)
+
+ @skipUnlessDBFeature('supports_paramstyle_pyformat')
+ def test_cursor_executemany_with_pyformat(self):
+ #10070: Support pyformat style passing of paramters
+ args = [{'root': i, 'square': i**2} for i in range(-5, 6)]
+ self.create_squares(args, 'pyformat', multiple=True)
+ self.assertEqual(models.Square.objects.count(), 11)
+ for i in range(-5, 6):
+ square = models.Square.objects.get(root=i)
+ self.assertEqual(square.square, i**2)
+
+ @skipUnlessDBFeature('supports_paramstyle_pyformat')
+ def test_cursor_executemany_with_pyformat_iterator(self):
+ args = iter({'root': i, 'square': i**2} for i in range(-3, 2))
+ self.create_squares(args, 'pyformat', multiple=True)
+ self.assertEqual(models.Square.objects.count(), 5)
+
+ args = iter({'root': i, 'square': i**2} for i in range(3, 7))
+ with override_settings(DEBUG=True):
+ # same test for DebugCursorWrapper
+ self.create_squares(args, 'pyformat', multiple=True)
+ self.assertEqual(models.Square.objects.count(), 9)
+
def test_unicode_fetches(self):
#6254: fetchone, fetchmany, fetchall return strings as unicode objects
qn = connection.ops.quote_name
diff --git a/tests/compat_checks/__init__.py b/tests/check/__init__.py
index e69de29bb2..e69de29bb2 100644
--- a/tests/compat_checks/__init__.py
+++ b/tests/check/__init__.py
diff --git a/tests/compat_checks/models.py b/tests/check/models.py
index 78a10abba6..78a10abba6 100644
--- a/tests/compat_checks/models.py
+++ b/tests/check/models.py
diff --git a/tests/compat_checks/tests.py b/tests/check/tests.py
index 879988c905..98495e38ae 100644
--- a/tests/compat_checks/tests.py
+++ b/tests/check/tests.py
@@ -1,6 +1,6 @@
-from django.core.compat_checks import base
-from django.core.compat_checks import django_1_6_0
-from django.core.management.commands import checksetup
+from django.core.checks.compatibility import base
+from django.core.checks.compatibility import django_1_6_0
+from django.core.management.commands import check
from django.core.management import call_command
from django.test import TestCase
@@ -86,22 +86,22 @@ class CompatChecksTestCase(TestCase):
def test_management_command(self):
# Again, we unfortunately have to patch out ``warnings``. Different
- old_warnings = checksetup.warnings
- checksetup.warnings = FakeWarnings()
+ old_warnings = check.warnings
+ check.warnings = FakeWarnings()
- self.assertEqual(len(checksetup.warnings._warnings), 0)
+ self.assertEqual(len(check.warnings._warnings), 0)
# Should not produce any warnings.
with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'):
- call_command('checksetup')
+ call_command('check')
- self.assertEqual(len(checksetup.warnings._warnings), 0)
+ self.assertEqual(len(check.warnings._warnings), 0)
with self.settings(TEST_RUNNER='django.test.runner.DiscoverRunner'):
- call_command('checksetup')
+ call_command('check')
- self.assertEqual(len(checksetup.warnings._warnings), 1)
- self.assertTrue("You have not explicitly set 'TEST_RUNNER'" in checksetup.warnings._warnings[0])
+ self.assertEqual(len(check.warnings._warnings), 1)
+ self.assertTrue("You have not explicitly set 'TEST_RUNNER'" in check.warnings._warnings[0])
# Restore the ``warnings``.
base.warnings = old_warnings
diff --git a/tests/field_deconstruction/tests.py b/tests/field_deconstruction/tests.py
index 683854940b..4ccf4d048c 100644
--- a/tests/field_deconstruction/tests.py
+++ b/tests/field_deconstruction/tests.py
@@ -108,7 +108,7 @@ class FieldDeconstructionTests(TestCase):
name, path, args, kwargs = field.deconstruct()
self.assertEqual(path, "django.db.models.EmailField")
self.assertEqual(args, [])
- self.assertEqual(kwargs, {})
+ self.assertEqual(kwargs, {"max_length": 75})
field = models.EmailField(max_length=255)
name, path, args, kwargs = field.deconstruct()
self.assertEqual(path, "django.db.models.EmailField")
diff --git a/tests/files/tests.py b/tests/files/tests.py
index cd2d15acdb..f1e3d5b14b 100644
--- a/tests/files/tests.py
+++ b/tests/files/tests.py
@@ -1,11 +1,13 @@
from __future__ import absolute_import
+import os
import gzip
import shutil
import tempfile
from django.core.cache import cache
from django.core.files import File
+from django.core.files.move import file_move_safe
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
@@ -146,3 +148,15 @@ class FileTests(unittest.TestCase):
file = SimpleUploadedFile("mode_test.txt", b"content")
self.assertFalse(hasattr(file, 'mode'))
g = gzip.GzipFile(fileobj=file)
+
+
+class FileMoveSafeTests(unittest.TestCase):
+ def test_file_move_overwrite(self):
+ handle_a, self.file_a = tempfile.mkstemp(dir=os.environ['DJANGO_TEST_TEMP_DIR'])
+ handle_b, self.file_b = tempfile.mkstemp(dir=os.environ['DJANGO_TEST_TEMP_DIR'])
+
+ # file_move_safe should raise an IOError exception if destination file exists and allow_overwrite is False
+ self.assertRaises(IOError, lambda: file_move_safe(self.file_a, self.file_b, allow_overwrite=False))
+
+ # should allow it and continue on if allow_overwrite is True
+ self.assertIsNone(file_move_safe(self.file_a, self.file_b, allow_overwrite=True))
diff --git a/tests/foreign_object/models.py b/tests/foreign_object/models.py
index 2d02b5624c..eee8091a15 100644
--- a/tests/foreign_object/models.py
+++ b/tests/foreign_object/models.py
@@ -150,3 +150,11 @@ class ArticleTranslation(models.Model):
class Meta:
unique_together = ('article', 'lang')
ordering = ('active_translation__title',)
+
+class ArticleTag(models.Model):
+ article = models.ForeignKey(Article, related_name="tags", related_query_name="tag")
+ name = models.CharField(max_length=255)
+
+class ArticleIdea(models.Model):
+ 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 69636ee49b..670fc94dc5 100644
--- a/tests/foreign_object/tests.py
+++ b/tests/foreign_object/tests.py
@@ -1,9 +1,10 @@
import datetime
from operator import attrgetter
-from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation
+from .models import Country, Person, Group, Membership, Friendship, Article, ArticleTranslation, ArticleTag, ArticleIdea
from django.test import TestCase
from django.utils.translation import activate
+from django.core.exceptions import FieldError
from django import forms
class MultiColumnFKTests(TestCase):
@@ -321,6 +322,24 @@ class MultiColumnFKTests(TestCase):
with self.assertRaisesMessage(Article.DoesNotExist, 'ArticleTranslation has no article'):
referrer.article
+ def test_foreign_key_related_query_name(self):
+ a1 = Article.objects.create(pub_date=datetime.date.today())
+ ArticleTag.objects.create(article=a1, name="foo")
+ self.assertEqual(Article.objects.filter(tag__name="foo").count(), 1)
+ self.assertEqual(Article.objects.filter(tag__name="bar").count(), 0)
+ with self.assertRaises(FieldError):
+ Article.objects.filter(tags__name="foo")
+
+ def test_many_to_many_related_query_name(self):
+ a1 = Article.objects.create(pub_date=datetime.date.today())
+ i1 = ArticleIdea.objects.create(name="idea1")
+ a1.ideas.add(i1)
+ self.assertEqual(Article.objects.filter(idea_things__name="idea1").count(), 1)
+ self.assertEqual(Article.objects.filter(idea_things__name="idea2").count(), 0)
+ with self.assertRaises(FieldError):
+ Article.objects.filter(ideas__name="idea1")
+
+
class FormsTests(TestCase):
# ForeignObjects should not have any form fields, currently the user needs
# to manually deal with the foreignobject relation.
diff --git a/tests/forms_tests/tests/test_fields.py b/tests/forms_tests/tests/test_fields.py
index 47c637befa..f7d83503f4 100644
--- a/tests/forms_tests/tests/test_fields.py
+++ b/tests/forms_tests/tests/test_fields.py
@@ -296,7 +296,7 @@ class FieldsTests(SimpleTestCase):
def test_decimalfield_1(self):
f = DecimalField(max_digits=4, decimal_places=2)
- self.assertWidgetRendersTo(f, '<input id="id_f" step="0.01" type="number" name="f" maxlength="6" />')
+ self.assertWidgetRendersTo(f, '<input id="id_f" step="0.01" type="number" name="f" />')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, '')
self.assertRaisesMessage(ValidationError, "'This field is required.'", f.clean, None)
self.assertEqual(f.clean('1'), Decimal("1"))
@@ -342,7 +342,7 @@ class FieldsTests(SimpleTestCase):
def test_decimalfield_3(self):
f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5'))
- self.assertWidgetRendersTo(f, '<input step="0.01" name="f" min="0.5" max="1.5" maxlength="6" type="number" id="id_f" />')
+ self.assertWidgetRendersTo(f, '<input step="0.01" name="f" min="0.5" max="1.5" type="number" id="id_f" />')
self.assertRaisesMessage(ValidationError, "'Ensure this value is less than or equal to 1.5.'", f.clean, '1.6')
self.assertRaisesMessage(ValidationError, "'Ensure this value is greater than or equal to 0.5.'", f.clean, '0.4')
self.assertEqual(f.clean('1.5'), Decimal("1.5"))
diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py
index b26017bc78..41577e6049 100644
--- a/tests/forms_tests/tests/test_formsets.py
+++ b/tests/forms_tests/tests/test_formsets.py
@@ -1072,7 +1072,8 @@ ArticleFormSet = formset_factory(ArticleForm)
class TestIsBoundBehavior(TestCase):
def test_no_data_raises_validation_error(self):
- self.assertRaises(ValidationError, ArticleFormSet, {})
+ with self.assertRaises(ValidationError):
+ ArticleFormSet({}).is_valid()
def test_with_management_data_attrs_work_fine(self):
data = {
diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py
index 435e48ba99..84d18ebcb2 100644
--- a/tests/generic_views/test_edit.py
+++ b/tests/generic_views/test_edit.py
@@ -7,8 +7,9 @@ from django.core.urlresolvers import reverse
from django import forms
from django.test import TestCase
from django.utils.unittest import expectedFailure
+from django.test.client import RequestFactory
from django.views.generic.base import View
-from django.views.generic.edit import FormMixin, CreateView, UpdateView
+from django.views.generic.edit import FormMixin, CreateView
from . import views
from .models import Artist, Author
@@ -22,6 +23,24 @@ class FormMixinTests(TestCase):
initial_2 = FormMixin().get_initial()
self.assertNotEqual(initial_1, initial_2)
+ def test_get_prefix(self):
+ """ Test prefix can be set (see #18872) """
+ test_string = 'test'
+
+ rf = RequestFactory()
+ get_request = rf.get('/')
+
+ class TestFormMixin(FormMixin):
+ request = get_request
+
+ default_kwargs = TestFormMixin().get_form_kwargs()
+ self.assertEqual(None, default_kwargs.get('prefix'))
+
+ set_mixin = TestFormMixin()
+ set_mixin.prefix = test_string
+ set_kwargs = set_mixin.get_form_kwargs()
+ self.assertEqual(test_string, set_kwargs.get('prefix'))
+
class BasicFormTests(TestCase):
urls = 'generic_views.urls'
diff --git a/tests/i18n/commands/__init__.py b/tests/i18n/commands/__init__.py
index ffd439140e..76249ca34d 100644
--- a/tests/i18n/commands/__init__.py
+++ b/tests/i18n/commands/__init__.py
@@ -1,4 +1,4 @@
-from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext as _, ungettext
# Translators: This comment should be extracted
dummy1 = _("This is a translatable string.")
@@ -6,3 +6,7 @@ dummy1 = _("This is a translatable string.")
# This comment should not be extracted
dummy2 = _("This is another translatable string.")
+# This file has a literal with plural forms. When processed first, makemessages
+# shouldn't create a .po file with duplicate `Plural-Forms` headers
+number = 3
+dummuy3 = ungettext("%(number)s Foo", "%(number)s Foos", number) % {'number': number}
diff --git a/tests/i18n/commands/extraction.py b/tests/i18n/commands/extraction.py
index 8696ae453b..dd1dce2ed0 100644
--- a/tests/i18n/commands/extraction.py
+++ b/tests/i18n/commands/extraction.py
@@ -329,6 +329,15 @@ class SymlinkExtractorTests(ExtractorTests):
class CopyPluralFormsExtractorTests(ExtractorTests):
+ PO_FILE_ES = 'locale/es/LC_MESSAGES/django.po'
+
+ def tearDown(self):
+ os.chdir(self.test_dir)
+ try:
+ self._rmrf('locale/es')
+ except OSError:
+ pass
+ os.chdir(self._cwd)
def test_copy_plural_forms(self):
os.chdir(self.test_dir)
@@ -338,6 +347,16 @@ class CopyPluralFormsExtractorTests(ExtractorTests):
po_contents = force_text(fp.read())
self.assertTrue('Plural-Forms: nplurals=2; plural=(n != 1)' in po_contents)
+ def test_override_plural_forms(self):
+ """Ticket #20311."""
+ os.chdir(self.test_dir)
+ management.call_command('makemessages', locale='es', extensions=['djtpl'], verbosity=0)
+ self.assertTrue(os.path.exists(self.PO_FILE_ES))
+ with open(self.PO_FILE_ES, 'r') as fp:
+ po_contents = force_text(fp.read())
+ found = re.findall(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', po_contents, re.MULTILINE | re.DOTALL)
+ self.assertEqual(1, len(found))
+
class NoWrapExtractorTests(ExtractorTests):
diff --git a/tests/i18n/commands/templates/plural.djtpl b/tests/i18n/commands/templates/plural.djtpl
new file mode 100644
index 0000000000..b2f2a18750
--- /dev/null
+++ b/tests/i18n/commands/templates/plural.djtpl
@@ -0,0 +1,6 @@
+{% load i18n %}
+{% comment %}
+This file has a literal with plural forms. When processed first, makemessages
+shouldn't create a .po file with duplicate `Plural-Forms` headers
+{% endcomment %}
+{% blocktrans count number=3 %}{{ number }} Bar{% plural %}{{ number }} Bars{% endblocktrans %}
diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py
index de7105f92d..ee9c5afe1d 100644
--- a/tests/lookup/tests.py
+++ b/tests/lookup/tests.py
@@ -610,6 +610,28 @@ class LookupTests(TestCase):
self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b(.).*b\1'),
['<Article: barfoobaz>', '<Article: bazbaRFOO>', '<Article: foobarbaz>'])
+ def test_regex_null(self):
+ """
+ Ensure that a regex lookup does not fail on null/None values
+ """
+ Season.objects.create(year=2012, gt=None)
+ self.assertQuerysetEqual(Season.objects.filter(gt__regex=r'^$'), [])
+
+ def test_regex_non_string(self):
+ """
+ Ensure that a regex lookup does not fail on non-string fields
+ """
+ Season.objects.create(year=2013, gt=444)
+ self.assertQuerysetEqual(Season.objects.filter(gt__regex=r'^444$'),
+ ['<Season: 2013>'])
+
+ def test_regex_non_ascii(self):
+ """
+ Ensure that a regex lookup does not trip on non-ascii characters.
+ """
+ Player.objects.create(name='\u2660')
+ Player.objects.get(name__regex='\u2660')
+
def test_nonfield_lookups(self):
"""
Ensure that a lookup query containing non-fields raises the proper
diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py
index 03cd3b0159..43509c471f 100644
--- a/tests/model_formsets/tests.py
+++ b/tests/model_formsets/tests.py
@@ -8,7 +8,7 @@ from decimal import Decimal
from django import forms
from django.db import models
from django.forms.models import (_get_foreign_key, inlineformset_factory,
- modelformset_factory)
+ modelformset_factory, BaseModelFormSet)
from django.test import TestCase, skipUnlessDBFeature
from django.utils import six
@@ -386,6 +386,23 @@ class ModelFormsetTest(TestCase):
formset = PostFormSet()
self.assertFalse("subtitle" in formset.forms[0].fields)
+ def test_custom_queryset_init(self):
+ """
+ Test that a queryset can be overriden in the __init__ method.
+ https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset
+ """
+ author1 = Author.objects.create(name='Charles Baudelaire')
+ author2 = Author.objects.create(name='Paul Verlaine')
+
+ class BaseAuthorFormSet(BaseModelFormSet):
+ def __init__(self, *args, **kwargs):
+ super(BaseAuthorFormSet, self).__init__(*args, **kwargs)
+ self.queryset = Author.objects.filter(name__startswith='Charles')
+
+ AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet)
+ formset = AuthorFormSet()
+ self.assertEqual(len(formset.get_queryset()), 1)
+
def test_model_inheritance(self):
BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__")
formset = BetterAuthorFormSet()
@@ -559,7 +576,7 @@ class ModelFormsetTest(TestCase):
formset = AuthorBooksFormSet2(instance=author)
self.assertEqual(len(formset.forms), 1)
self.assertHTMLEqual(formset.forms[0].as_p(),
- '<p><label for="id_bookwithcustompk_set-0-my_pk">My pk:</label> <input id="id_bookwithcustompk_set-0-my_pk" type="number" name="bookwithcustompk_set-0-my_pk" maxlength="6" /></p>\n'
+ '<p><label for="id_bookwithcustompk_set-0-my_pk">My pk:</label> <input id="id_bookwithcustompk_set-0-my_pk" type="number" name="bookwithcustompk_set-0-my_pk" /></p>\n'
'<p><label for="id_bookwithcustompk_set-0-title">Title:</label> <input id="id_bookwithcustompk_set-0-title" type="text" name="bookwithcustompk_set-0-title" maxlength="100" /><input type="hidden" name="bookwithcustompk_set-0-author" value="1" id="id_bookwithcustompk_set-0-author" /></p>')
data = {
diff --git a/tests/raw_query/tests.py b/tests/raw_query/tests.py
index e404c8b065..7242b8309b 100644
--- a/tests/raw_query/tests.py
+++ b/tests/raw_query/tests.py
@@ -3,7 +3,7 @@ from __future__ import absolute_import
from datetime import date
from django.db.models.query_utils import InvalidQuery
-from django.test import TestCase
+from django.test import TestCase, skipUnlessDBFeature
from .models import Author, Book, Coffee, Reviewer, FriendlyAuthor
@@ -123,10 +123,27 @@ class RawQueryTests(TestCase):
query = "SELECT * FROM raw_query_author WHERE first_name = %s"
author = Author.objects.all()[2]
params = [author.first_name]
- results = list(Author.objects.raw(query, params=params))
+ qset = Author.objects.raw(query, params=params)
+ results = list(qset)
self.assertProcessed(Author, results, [author])
self.assertNoAnnotations(results)
self.assertEqual(len(results), 1)
+ self.assertIsInstance(repr(qset), str)
+
+ @skipUnlessDBFeature('supports_paramstyle_pyformat')
+ def testPyformatParams(self):
+ """
+ Test passing optional query parameters
+ """
+ query = "SELECT * FROM raw_query_author WHERE first_name = %(first)s"
+ author = Author.objects.all()[2]
+ params = {'first': author.first_name}
+ qset = Author.objects.raw(query, params=params)
+ results = list(qset)
+ self.assertProcessed(Author, results, [author])
+ self.assertNoAnnotations(results)
+ self.assertEqual(len(results), 1)
+ self.assertIsInstance(repr(qset), str)
def testManyToMany(self):
"""
diff --git a/tests/servers/tests.py b/tests/servers/tests.py
index 3377793d68..be74afe820 100644
--- a/tests/servers/tests.py
+++ b/tests/servers/tests.py
@@ -96,6 +96,11 @@ class LiveServerAddress(LiveServerBase):
del os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS']
@classmethod
+ def tearDownClass(cls):
+ # skip it, as setUpClass doesn't call its parent either
+ pass
+
+ @classmethod
def raises_exception(cls, address, exception):
os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = address
try:
diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py
index f8d66bb2b7..625c4f31df 100644
--- a/tests/settings_tests/tests.py
+++ b/tests/settings_tests/tests.py
@@ -9,17 +9,19 @@ from django.test.utils import override_settings
from django.utils import unittest, six
-@override_settings(TEST='override')
+@override_settings(TEST='override', TEST_OUTER='outer')
class FullyDecoratedTranTestCase(TransactionTestCase):
available_apps = []
def test_override(self):
self.assertEqual(settings.TEST, 'override')
+ self.assertEqual(settings.TEST_OUTER, 'outer')
@override_settings(TEST='override2')
def test_method_override(self):
self.assertEqual(settings.TEST, 'override2')
+ self.assertEqual(settings.TEST_OUTER, 'outer')
def test_decorated_testcase_name(self):
self.assertEqual(FullyDecoratedTranTestCase.__name__, 'FullyDecoratedTranTestCase')
@@ -168,6 +170,29 @@ class SettingsTests(TestCase):
self.assertRaises(AttributeError, getattr, settings, 'USE_I18N')
self.assertEqual(settings.USE_I18N, previous_i18n)
+ def test_override_settings_nested(self):
+ """
+ Test that override_settings uses the actual _wrapped attribute at
+ runtime, not when it was instantiated.
+ """
+
+ self.assertRaises(AttributeError, getattr, settings, 'TEST')
+ self.assertRaises(AttributeError, getattr, settings, 'TEST2')
+
+ inner = override_settings(TEST2='override')
+ with override_settings(TEST='override'):
+ self.assertEqual('override', settings.TEST)
+ with inner:
+ self.assertEqual('override', settings.TEST)
+ self.assertEqual('override', settings.TEST2)
+ # inner's __exit__ should have restored the settings of the outer
+ # context manager, not those when the class was instantiated
+ self.assertEqual('override', settings.TEST)
+ self.assertRaises(AttributeError, getattr, settings, 'TEST2')
+
+ self.assertRaises(AttributeError, getattr, settings, 'TEST')
+ self.assertRaises(AttributeError, getattr, settings, 'TEST2')
+
def test_allowed_include_roots_string(self):
"""
ALLOWED_INCLUDE_ROOTS is not allowed to be incorrectly set to a string
diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py
index 206c648398..76712a09a6 100644
--- a/tests/template_tests/tests.py
+++ b/tests/template_tests/tests.py
@@ -854,6 +854,10 @@ class TemplateTests(TransRealMixin, TestCase):
'filter02': ('{% filter upper %}django{% endfilter %}', {}, 'DJANGO'),
'filter03': ('{% filter upper|lower %}django{% endfilter %}', {}, 'django'),
'filter04': ('{% filter cut:remove %}djangospam{% endfilter %}', {'remove': 'spam'}, 'django'),
+ 'filter05': ('{% filter safe %}fail{% endfilter %}', {}, template.TemplateSyntaxError),
+ 'filter05bis': ('{% filter upper|safe %}fail{% endfilter %}', {}, template.TemplateSyntaxError),
+ 'filter06': ('{% filter escape %}fail{% endfilter %}', {}, template.TemplateSyntaxError),
+ 'filter06bis': ('{% filter upper|escape %}fail{% endfilter %}', {}, template.TemplateSyntaxError),
### FIRSTOF TAG ###########################################################
'firstof01': ('{% firstof a b c %}', {'a':0,'b':0,'c':0}, ''),
diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py
index 24b7615d6f..756fa40abd 100644
--- a/tests/transactions/tests.py
+++ b/tests/transactions/tests.py
@@ -1,9 +1,8 @@
from __future__ import absolute_import
import sys
-import warnings
-from django.db import connection, transaction, IntegrityError
+from django.db import connection, transaction, DatabaseError, IntegrityError
from django.test import TransactionTestCase, skipUnlessDBFeature
from django.test.utils import IgnorePendingDeprecationWarningsMixin
from django.utils import six
@@ -188,6 +187,29 @@ class AtomicTests(TransactionTestCase):
raise Exception("Oops, that's his first name")
self.assertQuerysetEqual(Reporter.objects.all(), [])
+ def test_force_rollback(self):
+ with transaction.atomic():
+ Reporter.objects.create(first_name="Tintin")
+ # atomic block shouldn't rollback, but force it.
+ self.assertFalse(transaction.get_rollback())
+ transaction.set_rollback(True)
+ self.assertQuerysetEqual(Reporter.objects.all(), [])
+
+ def test_prevent_rollback(self):
+ with transaction.atomic():
+ Reporter.objects.create(first_name="Tintin")
+ sid = transaction.savepoint()
+ # trigger a database error inside an inner atomic without savepoint
+ with self.assertRaises(DatabaseError):
+ with transaction.atomic(savepoint=False):
+ connection.cursor().execute(
+ "SELECT no_such_col FROM transactions_reporter")
+ transaction.savepoint_rollback(sid)
+ # atomic block should rollback, but prevent it, as we just did it.
+ self.assertTrue(transaction.get_rollback())
+ transaction.set_rollback(False)
+ self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>'])
+
class AtomicInsideTransactionTests(AtomicTests):
"""All basic tests for atomic should also pass within an existing transaction."""