diff options
| author | Andrew Godwin <andrew@aeracode.org> | 2013-08-19 18:30:48 +0100 |
|---|---|---|
| committer | Andrew Godwin <andrew@aeracode.org> | 2013-08-19 18:30:48 +0100 |
| commit | b6a957f0ba8a2ed1b24d7ee042a9c4beaf51ab03 (patch) | |
| tree | 87d42b9e8d3d4c1516b53eee1d9332735762826a /tests | |
| parent | 52edc16086e3c28a78c31975bb4da2f9450590b4 (diff) | |
| parent | 3c0300405009b82b52fd15483371097221662fcd (diff) | |
Merge remote-tracking branch 'core/master' into schema-alteration
Conflicts:
docs/ref/django-admin.txt
Diffstat (limited to 'tests')
60 files changed, 729 insertions, 375 deletions
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index df8ced949e..193b64505c 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -29,7 +29,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture, Album, Question, Answer, ComplexSortedPerson, PluggableSearchPerson, PrePopulatedPostLargeSlug, AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated, - RelatedPrepopulated, UndeletableObject, UserMessenger, Simple, Choice, + RelatedPrepopulated, UndeletableObject, UnchangeableObject, UserMessenger, Simple, Choice, ShortMessage, Telegram) @@ -656,6 +656,13 @@ class UndeletableObjectAdmin(admin.ModelAdmin): return super(UndeletableObjectAdmin, self).change_view(*args, **kwargs) +class UnchangeableObjectAdmin(admin.ModelAdmin): + def get_urls(self): + # Disable change_view, but leave other urls untouched + urlpatterns = super(UnchangeableObjectAdmin, self).get_urls() + return [p for p in urlpatterns if not p.name.endswith("_change")] + + def callable_on_unknown(obj): return obj.unknown @@ -741,6 +748,7 @@ site.register(Report, ReportAdmin) site.register(MainPrepopulated, MainPrepopulatedAdmin) site.register(UnorderedObject, UnorderedObjectAdmin) site.register(UndeletableObject, UndeletableObjectAdmin) +site.register(UnchangeableObject, UnchangeableObjectAdmin) # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # That way we cover all four cases: diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index a1131210d7..1accf46819 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -115,7 +115,7 @@ class ModelWithStringPrimaryKey(models.Model): @python_2_unicode_compatible class Color(models.Model): value = models.CharField(max_length=10) - warm = models.BooleanField() + warm = models.BooleanField(default=False) def __str__(self): return self.value @@ -144,7 +144,7 @@ class Actor(models.Model): @python_2_unicode_compatible class Inquisition(models.Model): - expected = models.BooleanField() + expected = models.BooleanField(default=False) leader = models.ForeignKey(Actor) country = models.CharField(max_length=20) @@ -376,7 +376,7 @@ class Link(models.Model): class PrePopulatedPost(models.Model): title = models.CharField(max_length=100) - published = models.BooleanField() + published = models.BooleanField(default=False) slug = models.SlugField() @@ -607,7 +607,7 @@ class PrePopulatedPostLargeSlug(models.Model): the javascript (ie, using THOUSAND_SEPARATOR ends up with maxLength=1,000) """ title = models.CharField(max_length=100) - published = models.BooleanField() + published = models.BooleanField(default=False) slug = models.SlugField(max_length=1000) class AdminOrderedField(models.Model): @@ -674,6 +674,12 @@ class UndeletableObject(models.Model): """ name = models.CharField(max_length=255) +class UnchangeableObject(models.Model): + """ + Model whose change_view is disabled in admin + Refs #20640. + """ + class UserMessenger(models.Model): """ Dummy class for testing message_user functions on ModelAdmin diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 3ee06751fb..8f83324a37 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -15,7 +15,6 @@ from django.core import mail from django.core.files import temp as tempfile from django.core.urlresolvers import reverse # Register auth models with the admin. -from django.contrib import admin from django.contrib.auth import get_permission_codename from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME from django.contrib.admin.models import LogEntry, DELETION @@ -51,7 +50,7 @@ from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount, OtherStory, ComplexSortedPerson, PluggableSearchPerson, Parent, Child, AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable, Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject, - Simple, UndeletableObject, Choice, ShortMessage, Telegram) + Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage, Telegram) from .admin import site, site2 @@ -524,31 +523,28 @@ class AdminViewBasicTest(AdminViewBasicTestCase): if the default language is non-English but the selected language is English. See #13388 and #3594 for more details. """ - with self.settings(LANGUAGE_CODE='fr'): - with translation.override('en-us'): - response = self.client.get('/test_admin/admin/jsi18n/') - self.assertNotContains(response, 'Choisir une heure') + with self.settings(LANGUAGE_CODE='fr'), translation.override('en-us'): + response = self.client.get('/test_admin/admin/jsi18n/') + self.assertNotContains(response, 'Choisir une heure') def testI18NLanguageNonEnglishFallback(self): """ Makes sure that the fallback language is still working properly in cases where the selected language cannot be found. """ - with self.settings(LANGUAGE_CODE='fr'): - with translation.override('none'): - response = self.client.get('/test_admin/admin/jsi18n/') - self.assertContains(response, 'Choisir une heure') + with self.settings(LANGUAGE_CODE='fr'), translation.override('none'): + response = self.client.get('/test_admin/admin/jsi18n/') + self.assertContains(response, 'Choisir une heure') def testL10NDeactivated(self): """ Check if L10N is deactivated, the JavaScript i18n view doesn't return localized date/time formats. Refs #14824. """ - with self.settings(LANGUAGE_CODE='ru', USE_L10N=False): - with translation.override('none'): - response = self.client.get('/test_admin/admin/jsi18n/') - self.assertNotContains(response, '%d.%m.%Y %H:%M:%S') - self.assertContains(response, '%Y-%m-%d %H:%M:%S') + with self.settings(LANGUAGE_CODE='ru', USE_L10N=False), translation.override('none'): + response = self.client.get('/test_admin/admin/jsi18n/') + self.assertNotContains(response, '%d.%m.%Y %H:%M:%S') + self.assertContains(response, '%Y-%m-%d %H:%M:%S') def test_disallowed_filtering(self): with patch_logger('django.security.DisallowedModelAdminLookup', 'error') as calls: @@ -2425,6 +2421,24 @@ class AdminActionsTest(TestCase): self.assertContains(response, '<li>Answer: <a href="/test_admin/admin/admin_views/answer/%s/">Because.</a></li>' % a1.pk, html=True) self.assertContains(response, '<li>Answer: <a href="/test_admin/admin/admin_views/answer/%s/">Yes.</a></li>' % a2.pk, html=True) + def test_model_admin_default_delete_action_no_change_url(self): + """ + Default delete action shouldn't break if a user's ModelAdmin removes the url for change_view. + + Regression test for #20640 + """ + obj = UnchangeableObject.objects.create() + action_data = { + ACTION_CHECKBOX_NAME: obj.pk, + "action": "delete_selected", + "index": "0", + } + response = self.client.post('/test_admin/admin/admin_views/unchangeableobject/', action_data) + # No 500 caused by NoReverseMatch + self.assertEqual(response.status_code, 200) + # The page shouldn't display a link to the nonexistent change page + self.assertContains(response, "<li>Unchangeable object: UnchangeableObject object</li>", 1, html=True) + def test_custom_function_mail_action(self): "Tests a custom action defined in a function" action_data = { diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 5a88df1e57..d2ecf46358 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -16,7 +16,6 @@ from django.test import TestCase as DjangoTestCase from django.test.utils import override_settings from django.utils import six from django.utils import translation -from django.utils.html import conditional_escape from . import models from .widgetadmin import site as widget_admin_site @@ -238,14 +237,14 @@ class FilteredSelectMultipleWidgetTest(DjangoTestCase): def test_render(self): w = widgets.FilteredSelectMultiple('test', False) self.assertHTMLEqual( - conditional_escape(w.render('test', 'test')), + w.render('test', 'test'), '<select multiple="multiple" name="test" class="selectfilter">\n</select><script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("id_test", "test", 0, "%(ADMIN_STATIC_PREFIX)s"); });</script>\n' % admin_static_prefix() ) def test_stacked_render(self): w = widgets.FilteredSelectMultiple('test', True) self.assertHTMLEqual( - conditional_escape(w.render('test', 'test')), + w.render('test', 'test'), '<select multiple="multiple" name="test" class="selectfilterstacked">\n</select><script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("id_test", "test", 1, "%(ADMIN_STATIC_PREFIX)s"); });</script>\n' % admin_static_prefix() ) @@ -257,13 +256,13 @@ class AdminDateWidgetTest(DjangoTestCase): """ w = widgets.AdminDateWidget() self.assertHTMLEqual( - conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))), + w.render('test', datetime(2007, 12, 1, 9, 30)), '<input value="2007-12-01" type="text" class="vDateField" name="test" size="10" />', ) # pass attrs to widget w = widgets.AdminDateWidget(attrs={'size': 20, 'class': 'myDateField'}) self.assertHTMLEqual( - conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))), + w.render('test', datetime(2007, 12, 1, 9, 30)), '<input value="2007-12-01" type="text" class="myDateField" name="test" size="20" />', ) @@ -275,13 +274,13 @@ class AdminTimeWidgetTest(DjangoTestCase): """ w = widgets.AdminTimeWidget() self.assertHTMLEqual( - conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))), + w.render('test', datetime(2007, 12, 1, 9, 30)), '<input value="09:30:00" type="text" class="vTimeField" name="test" size="8" />', ) # pass attrs to widget w = widgets.AdminTimeWidget(attrs={'size': 20, 'class': 'myTimeField'}) self.assertHTMLEqual( - conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))), + w.render('test', datetime(2007, 12, 1, 9, 30)), '<input value="09:30:00" type="text" class="myTimeField" name="test" size="20" />', ) @@ -289,50 +288,55 @@ class AdminSplitDateTimeWidgetTest(DjangoTestCase): def test_render(self): w = widgets.AdminSplitDateTime() self.assertHTMLEqual( - conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))), + w.render('test', datetime(2007, 12, 1, 9, 30)), '<p class="datetime">Date: <input value="2007-12-01" type="text" class="vDateField" name="test_0" size="10" /><br />Time: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>', ) def test_localization(self): w = widgets.AdminSplitDateTime() - with self.settings(USE_L10N=True): - with translation.override('de-at'): - w.is_localized = True - self.assertHTMLEqual( - conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30))), - '<p class="datetime">Datum: <input value="01.12.2007" type="text" class="vDateField" name="test_0" size="10" /><br />Zeit: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>', - ) + with self.settings(USE_L10N=True), translation.override('de-at'): + w.is_localized = True + self.assertHTMLEqual( + w.render('test', datetime(2007, 12, 1, 9, 30)), + '<p class="datetime">Datum: <input value="01.12.2007" type="text" class="vDateField" name="test_0" size="10" /><br />Zeit: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>', + ) class AdminURLWidgetTest(DjangoTestCase): def test_render(self): w = widgets.AdminURLFieldWidget() self.assertHTMLEqual( - conditional_escape(w.render('test', '')), + w.render('test', ''), '<input class="vURLField" name="test" type="url" />' ) self.assertHTMLEqual( - conditional_escape(w.render('test', 'http://example.com')), + 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="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="url" value="http://example-äüö.com" /></p>' + 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="url" value="http://example-äüö.com" /></p>' ) def test_render_quoting(self): + # WARNING: Don't use assertHTMLEqual in that testcase! + # assertHTMLEqual will get rid of some escapes which are tested here! 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="url" value="http://example.com/<sometag>some text</sometag>" /></p>' + self.assertEqual( + 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="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="url" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>' + self.assertEqual( + 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="url" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>' + ) + self.assertEqual( + w.render('test', 'http://www.example.com/%C3%A4"><script>alert("XSS!")</script>"'), + '<p class="url">Currently: <a href="http://www.example.com/%C3%A4%22%3E%3Cscript%3Ealert(%22XSS!%22)%3C/script%3E%22">http://www.example.com/%C3%A4"><script>alert("XSS!")</script>"</a><br />Change: <input class="vURLField" name="test" type="url" value="http://www.example.com/%C3%A4"><script>alert("XSS!")</script>"" /></p>' ) @@ -345,12 +349,12 @@ class AdminFileWidgetTest(DjangoTestCase): w = widgets.AdminFileWidget() self.assertHTMLEqual( - conditional_escape(w.render('test', album.cover_art)), + w.render('test', album.cover_art), '<p class="file-upload">Currently: <a href="%(STORAGE_URL)salbums/hybrid_theory.jpg">albums\hybrid_theory.jpg</a> <span class="clearable-file-input"><input type="checkbox" name="test-clear" id="test-clear_id" /> <label for="test-clear_id">Clear</label></span><br />Change: <input type="file" name="test" /></p>' % { 'STORAGE_URL': default_storage.url('') }, ) self.assertHTMLEqual( - conditional_escape(w.render('test', SimpleUploadedFile('test', b'content'))), + w.render('test', SimpleUploadedFile('test', b'content')), '<input type="file" name="test" />', ) @@ -365,7 +369,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) self.assertHTMLEqual( - conditional_escape(w.render('test', band.pk, attrs={})), + w.render('test', band.pk, attrs={}), '<input type="text" name="test" value="%(bandpk)s" class="vForeignKeyRawIdAdminField" /><a href="/widget_admin/admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_STATIC_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a> <strong>Linkin Park</strong>' % dict(admin_static_prefix(), bandpk=band.pk) ) @@ -393,7 +397,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) self.assertHTMLEqual( - conditional_escape(w.render('honeycomb_widget', big_honeycomb.pk, attrs={})), + w.render('honeycomb_widget', big_honeycomb.pk, attrs={}), '<input type="text" name="honeycomb_widget" value="%(hcombpk)s" /> <strong>Honeycomb object</strong>' % {'hcombpk': big_honeycomb.pk} ) @@ -406,7 +410,7 @@ class ForeignKeyRawIdWidgetTest(DjangoTestCase): w = widgets.ForeignKeyRawIdWidget(rel, widget_admin_site) self.assertHTMLEqual( - conditional_escape(w.render('individual_widget', subject1.pk, attrs={})), + w.render('individual_widget', subject1.pk, attrs={}), '<input type="text" name="individual_widget" value="%(subj1pk)s" /> <strong>Individual object</strong>' % {'subj1pk': subject1.pk} ) @@ -438,12 +442,12 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase): w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site) self.assertHTMLEqual( - conditional_escape(w.render('test', [m1.pk, m2.pk], attrs={})), + w.render('test', [m1.pk, m2.pk], attrs={}), '<input type="text" name="test" value="%(m1pk)s,%(m2pk)s" class="vManyToManyRawIdAdminField" /><a href="/widget_admin/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="/static/admin/img/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_static_prefix(), m1pk=m1.pk, m2pk=m2.pk) ) self.assertHTMLEqual( - conditional_escape(w.render('test', [m1.pk])), + w.render('test', [m1.pk]), '<input type="text" name="test" value="%(m1pk)s" class="vManyToManyRawIdAdminField" /><a href="/widget_admin/admin_widgets/member/" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_STATIC_PREFIX)simg/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % dict(admin_static_prefix(), m1pk=m1.pk) ) @@ -459,12 +463,12 @@ class ManyToManyRawIdWidgetTest(DjangoTestCase): w = widgets.ManyToManyRawIdWidget(rel, widget_admin_site) self.assertHTMLEqual( - conditional_escape(w.render('company_widget1', [c1.pk, c2.pk], attrs={})), + w.render('company_widget1', [c1.pk, c2.pk], attrs={}), '<input type="text" name="company_widget1" value="%(c1pk)s,%(c2pk)s" />' % {'c1pk': c1.pk, 'c2pk': c2.pk} ) self.assertHTMLEqual( - conditional_escape(w.render('company_widget2', [c1.pk])), + w.render('company_widget2', [c1.pk]), '<input type="text" name="company_widget2" value="%(c1pk)s" />' % {'c1pk': c1.pk} ) diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index 4d46cae766..7d2490a77c 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -3,8 +3,10 @@ from __future__ import unicode_literals import datetime from decimal import Decimal +from django.db import connection from django.db.models import Avg, Sum, Count, Max, Min from django.test import TestCase, Approximate +from django.test.utils import CaptureQueriesContext from .models import Author, Publisher, Book, Store @@ -617,3 +619,26 @@ class BaseAggregateTestCase(TestCase): # 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) + + def test_ticket12886(self): + """ + Check that aggregation over sliced queryset works correctly. + """ + qs = Book.objects.all().order_by('-rating')[0:3] + vals = qs.aggregate(average_top3_rating=Avg('rating'))['average_top3_rating'] + self.assertAlmostEqual(vals, 4.5, places=2) + + def test_ticket11881(self): + """ + Check that subqueries do not needlessly contain ORDER BY, SELECT FOR UPDATE + or select_related() stuff. + """ + qs = Book.objects.all().select_for_update().order_by( + 'pk').select_related('publisher').annotate(max_pk=Max('pk')) + with CaptureQueriesContext(connection) as captured_queries: + qs.aggregate(avg_pk=Avg('max_pk')) + self.assertEqual(len(captured_queries), 1) + qstr = captured_queries[0]['sql'].lower() + self.assertNotIn('for update', qstr) + self.assertNotIn('order by', qstr) + self.assertEqual(qstr.count(' join '), 0) diff --git a/tests/aggregation_regress/models.py b/tests/aggregation_regress/models.py index 047c871c39..dfef691882 100644 --- a/tests/aggregation_regress/models.py +++ b/tests/aggregation_regress/models.py @@ -64,7 +64,7 @@ class Store(models.Model): class Entries(models.Model): EntryID = models.AutoField(primary_key=True, db_column='Entry ID') Entry = models.CharField(unique=True, max_length=50) - Exclude = models.BooleanField() + Exclude = models.BooleanField(default=False) class Clues(models.Model): diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 55ed6a436f..2b051621ef 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -707,6 +707,10 @@ class ModelTest(TestCase): with self.assertRaises(ObjectDoesNotExist): SelfRef.objects.get(selfref=sr) + def test_eq(self): + self.assertNotEqual(Article(id=1), object()) + self.assertNotEqual(object(), Article(id=1)) + class ConcurrentSaveTests(TransactionTestCase): diff --git a/tests/check/models.py b/tests/check/models.py index 78a10abba6..212b01bdd2 100644 --- a/tests/check/models.py +++ b/tests/check/models.py @@ -1 +1,9 @@ -# Stubby. +from django.db import models + +class Book(models.Model): + title = models.CharField(max_length=250) + is_published = models.BooleanField(default=False) + +class BlogPost(models.Model): + title = models.CharField(max_length=250) + is_published = models.BooleanField(default=False) diff --git a/tests/check/tests.py b/tests/check/tests.py index 98495e38ae..3faf086557 100644 --- a/tests/check/tests.py +++ b/tests/check/tests.py @@ -2,8 +2,10 @@ 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.db.models.fields import NOT_PROVIDED from django.test import TestCase +from .models import Book class StubCheckModule(object): # Has no ``run_checks`` attribute & will trigger a warning. @@ -53,6 +55,24 @@ class CompatChecksTestCase(TestCase): with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'): self.assertEqual(len(django_1_6_0.run_checks()), 0) + def test_boolean_field_default_value(self): + with self.settings(TEST_RUNNER='myapp.test.CustomRunnner'): + # We patch the field's default value to trigger the warning + boolean_field = Book._meta.get_field('is_published') + old_default = boolean_field.default + try: + boolean_field.default = NOT_PROVIDED + result = django_1_6_0.run_checks() + self.assertEqual(len(result), 1) + self.assertTrue("You have not set a default value for one or more BooleanFields" in result[0]) + self.assertTrue('check.Book: "is_published"' in result[0]) + # We did not patch the BlogPost.is_published field so + # there should not be a warning about it + self.assertFalse('check.BlogPost' in result[0]) + finally: + # Restore the ``default`` + boolean_field.default = old_default + def test_check_compatibility(self): with self.settings(TEST_RUNNER='django.test.runner.DiscoverRunner'): result = base.check_compatibility() diff --git a/tests/comment_tests/models.py b/tests/comment_tests/models.py index 472b66decd..a2b27e7e87 100644 --- a/tests/comment_tests/models.py +++ b/tests/comment_tests/models.py @@ -30,7 +30,7 @@ class Entry(models.Model): title = models.CharField(max_length=250) body = models.TextField() pub_date = models.DateField() - enable_comments = models.BooleanField() + enable_comments = models.BooleanField(default=False) def __str__(self): return self.title diff --git a/tests/custom_managers/models.py b/tests/custom_managers/models.py index 44d5eb70da..cba375f4d7 100644 --- a/tests/custom_managers/models.py +++ b/tests/custom_managers/models.py @@ -67,7 +67,7 @@ CustomManager = BaseCustomManager.from_queryset(CustomQuerySet) class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - fun = models.BooleanField() + fun = models.BooleanField(default=False) objects = PersonManager() custom_queryset_default_manager = CustomQuerySet.as_manager() @@ -80,7 +80,7 @@ class Person(models.Model): class Book(models.Model): title = models.CharField(max_length=50) author = models.CharField(max_length=30) - is_published = models.BooleanField() + is_published = models.BooleanField(default=False) published_objects = PublishedBookManager() authors = models.ManyToManyField(Person, related_name='books') diff --git a/tests/defaultfilters/tests.py b/tests/defaultfilters/tests.py index 725b673903..7db872001d 100644 --- a/tests/defaultfilters/tests.py +++ b/tests/defaultfilters/tests.py @@ -647,22 +647,21 @@ class DefaultFiltersI18NTests(TransRealMixin, TestCase): def test_localized_filesizeformat(self): # NOTE: \xa0 avoids wrapping between value and unit - with self.settings(USE_L10N=True): - with translation.override('de', deactivate=True): - self.assertEqual(filesizeformat(1023), '1023\xa0Bytes') - self.assertEqual(filesizeformat(1024), '1,0\xa0KB') - self.assertEqual(filesizeformat(10*1024), '10,0\xa0KB') - self.assertEqual(filesizeformat(1024*1024-1), '1024,0\xa0KB') - self.assertEqual(filesizeformat(1024*1024), '1,0\xa0MB') - self.assertEqual(filesizeformat(1024*1024*50), '50,0\xa0MB') - self.assertEqual(filesizeformat(1024*1024*1024-1), '1024,0\xa0MB') - self.assertEqual(filesizeformat(1024*1024*1024), '1,0\xa0GB') - self.assertEqual(filesizeformat(1024*1024*1024*1024), '1,0\xa0TB') - self.assertEqual(filesizeformat(1024*1024*1024*1024*1024), - '1,0\xa0PB') - self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000), - '2000,0\xa0PB') - self.assertEqual(filesizeformat(complex(1,-1)), '0\xa0Bytes') - self.assertEqual(filesizeformat(""), '0\xa0Bytes') - self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"), - '0\xa0Bytes') + with self.settings(USE_L10N=True), translation.override('de', deactivate=True): + self.assertEqual(filesizeformat(1023), '1023\xa0Bytes') + self.assertEqual(filesizeformat(1024), '1,0\xa0KB') + self.assertEqual(filesizeformat(10*1024), '10,0\xa0KB') + self.assertEqual(filesizeformat(1024*1024-1), '1024,0\xa0KB') + self.assertEqual(filesizeformat(1024*1024), '1,0\xa0MB') + self.assertEqual(filesizeformat(1024*1024*50), '50,0\xa0MB') + self.assertEqual(filesizeformat(1024*1024*1024-1), '1024,0\xa0MB') + self.assertEqual(filesizeformat(1024*1024*1024), '1,0\xa0GB') + self.assertEqual(filesizeformat(1024*1024*1024*1024), '1,0\xa0TB') + self.assertEqual(filesizeformat(1024*1024*1024*1024*1024), + '1,0\xa0PB') + self.assertEqual(filesizeformat(1024*1024*1024*1024*1024*2000), + '2000,0\xa0PB') + self.assertEqual(filesizeformat(complex(1,-1)), '0\xa0Bytes') + self.assertEqual(filesizeformat(""), '0\xa0Bytes') + self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"), + '0\xa0Bytes') diff --git a/tests/defer/tests.py b/tests/defer/tests.py index 1df312e1bf..b66b173f7a 100644 --- a/tests/defer/tests.py +++ b/tests/defer/tests.py @@ -183,3 +183,9 @@ class DeferTests(TestCase): with self.assertNumQueries(0): bc_deferred.id self.assertEqual(bc_deferred.pk, bc_deferred.id) + + def test_eq(self): + s1 = Secondary.objects.create(first="x1", second="y1") + s1_defer = Secondary.objects.only('pk').get(pk=s1.pk) + self.assertEqual(s1, s1_defer) + self.assertEqual(s1_defer, s1) diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index cdd9720374..2a6f602e3e 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -462,6 +462,18 @@ class FileStoragePermissions(unittest.TestCase): mode = os.stat(self.storage.path(fname))[0] & 0o777 self.assertEqual(mode, 0o666 & ~self.umask) + @override_settings(FILE_UPLOAD_DIRECTORY_PERMISSIONS=0o765) + def test_file_upload_directory_permissions(self): + name = self.storage.save("the_directory/the_file", ContentFile("data")) + dir_mode = os.stat(os.path.dirname(self.storage.path(name)))[0] & 0o777 + self.assertEqual(dir_mode, 0o765) + + @override_settings(FILE_UPLOAD_DIRECTORY_PERMISSIONS=None) + def test_file_upload_directory_default_permissions(self): + name = self.storage.save("the_directory/the_file", ContentFile("data")) + dir_mode = os.stat(os.path.dirname(self.storage.path(name)))[0] & 0o777 + self.assertEqual(dir_mode, 0o777 & ~self.umask) + class FileStoragePathParsing(unittest.TestCase): def setUp(self): self.storage_dir = tempfile.mkdtemp() diff --git a/tests/foreign_object/tests.py b/tests/foreign_object/tests.py index cd81cc68a2..77582162a8 100644 --- a/tests/foreign_object/tests.py +++ b/tests/foreign_object/tests.py @@ -132,7 +132,6 @@ class MultiColumnFKTests(TestCase): ], attrgetter('person_id') ) - self.assertQuerysetEqual( Membership.objects.filter(person__in=Person.objects.filter(name='Jim')), [ self.jim.id, @@ -140,6 +139,24 @@ class MultiColumnFKTests(TestCase): attrgetter('person_id') ) + def test_double_nested_query(self): + m1 = Membership.objects.create(membership_country_id=self.usa.id, person_id=self.bob.id, + group_id=self.cia.id) + m2 = Membership.objects.create(membership_country_id=self.usa.id, person_id=self.jim.id, + group_id=self.cia.id) + Friendship.objects.create(from_friend_country_id=self.usa.id, from_friend_id=self.bob.id, + to_friend_country_id=self.usa.id, to_friend_id=self.jim.id) + self.assertQuerysetEqual(Membership.objects.filter( + person__in=Person.objects.filter( + from_friend__in=Friendship.objects.filter( + to_friend__in=Person.objects.all()))), + [m1], lambda x: x) + self.assertQuerysetEqual(Membership.objects.exclude( + person__in=Person.objects.filter( + from_friend__in=Friendship.objects.filter( + to_friend__in=Person.objects.all()))), + [m2], lambda x: x) + def test_select_related_foreignkey_forward_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) diff --git a/tests/forms_tests/tests/test_fields.py b/tests/forms_tests/tests/test_fields.py index f02593e488..a4e81d31ae 100644 --- a/tests/forms_tests/tests/test_fields.py +++ b/tests/forms_tests/tests/test_fields.py @@ -286,11 +286,10 @@ class FieldsTests(SimpleTestCase): n = 4.35 self.assertFalse(f._has_changed(n, '4.3500')) - with translation.override('fr'): - with self.settings(USE_L10N=True): - f = FloatField(localize=True) - localized_n = formats.localize_input(n) # -> '4,35' in French - self.assertFalse(f._has_changed(n, localized_n)) + with translation.override('fr'), self.settings(USE_L10N=True): + f = FloatField(localize=True) + localized_n = formats.localize_input(n) # -> '4,35' in French + self.assertFalse(f._has_changed(n, localized_n)) # DecimalField ################################################################ @@ -399,11 +398,10 @@ class FieldsTests(SimpleTestCase): self.assertFalse(f._has_changed(d, '0.10')) self.assertTrue(f._has_changed(d, '0.101')) - with translation.override('fr'): - with self.settings(USE_L10N=True): - f = DecimalField(max_digits=2, decimal_places=2, localize=True) - localized_d = formats.localize_input(d) # -> '0,1' in French - self.assertFalse(f._has_changed(d, localized_d)) + with translation.override('fr'), self.settings(USE_L10N=True): + f = DecimalField(max_digits=2, decimal_places=2, localize=True) + localized_d = formats.localize_input(d) # -> '0,1' in French + self.assertFalse(f._has_changed(d, localized_d)) # DateField ################################################################### diff --git a/tests/forms_tests/tests/test_widgets.py b/tests/forms_tests/tests/test_widgets.py index 06ee2d6c53..3f11771d7a 100644 --- a/tests/forms_tests/tests/test_widgets.py +++ b/tests/forms_tests/tests/test_widgets.py @@ -833,6 +833,22 @@ beatle J R Ringo False""") with self.assertRaises(IndexError): r[42] + def test_subwidget(self): + # Each subwidget tag gets a separate ID when the widget has an ID specified + self.assertHTMLEqual("\n".join([c.tag() for c in CheckboxSelectMultiple(attrs={'id': 'abc'}).subwidgets('letters', list('ac'), choices=zip(list('abc'), list('ABC')))]), """<input checked="checked" type="checkbox" name="letters" value="a" id="abc_0" /> +<input type="checkbox" name="letters" value="b" id="abc_1" /> +<input checked="checked" type="checkbox" name="letters" value="c" id="abc_2" />""") + + # Each subwidget tag does not get an ID if the widget does not have an ID specified + self.assertHTMLEqual("\n".join([c.tag() for c in CheckboxSelectMultiple().subwidgets('letters', list('ac'), choices=zip(list('abc'), list('ABC')))]), """<input checked="checked" type="checkbox" name="letters" value="a" /> +<input type="checkbox" name="letters" value="b" /> +<input checked="checked" type="checkbox" name="letters" value="c" />""") + + # The id_for_label property of the subwidget should return the ID that is used on the subwidget's tag + self.assertHTMLEqual("\n".join(['<input type="checkbox" name="letters" value="%s" id="%s" />' % (c.choice_value, c.id_for_label) for c in CheckboxSelectMultiple(attrs={'id': 'abc'}).subwidgets('letters', [], choices=zip(list('abc'), list('ABC')))]), """<input type="checkbox" name="letters" value="a" id="abc_0" /> +<input type="checkbox" name="letters" value="b" id="abc_1" /> +<input type="checkbox" name="letters" value="c" id="abc_2" />""") + def test_multi(self): class MyMultiWidget(MultiWidget): def decompress(self, value): diff --git a/tests/generic_relations/models.py b/tests/generic_relations/models.py index 211df8aa3d..d819a53c0e 100644 --- a/tests/generic_relations/models.py +++ b/tests/generic_relations/models.py @@ -92,7 +92,7 @@ class GeckoManager(models.Manager): return super(GeckoManager, self).get_queryset().filter(has_tail=True) class Gecko(models.Model): - has_tail = models.BooleanField() + has_tail = models.BooleanField(default=False) objects = GeckoManager() # To test fix for #11263 diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index 05d1065a75..12e325b773 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -746,11 +746,11 @@ class FormattingTests(TransRealMixin, TestCase): self.assertEqual('.', get_format('DECIMAL_SEPARATOR', lang='en')) def test_get_format_modules_stability(self): - with self.settings(FORMAT_MODULE_PATH='i18n.other.locale'): - with translation.override('de', deactivate=True): - old = str("%r") % get_format_modules(reverse=True) - new = str("%r") % get_format_modules(reverse=True) # second try - self.assertEqual(new, old, 'Value returned by get_formats_modules() must be preserved between calls.') + with self.settings(FORMAT_MODULE_PATH='i18n.other.locale'), \ + translation.override('de', deactivate=True): + old = str("%r") % get_format_modules(reverse=True) + new = str("%r") % get_format_modules(reverse=True) # second try + self.assertEqual(new, old, 'Value returned by get_formats_modules() must be preserved between calls.') def test_localize_templatetag_and_filter(self): """ @@ -1062,9 +1062,8 @@ class MultipleLocaleActivationTests(TransRealMixin, TestCase): def test_multiple_locale_filter(self): with translation.override('de'): t = Template("{% load i18n %}{{ 0|yesno:_('yes,no,maybe') }}") - with translation.override(self._old_language): - with translation.override('nl'): - self.assertEqual(t.render(Context({})), 'nee') + with translation.override(self._old_language), translation.override('nl'): + self.assertEqual(t.render(Context({})), 'nee') def test_multiple_locale_filter_deactivate(self): with translation.override('de', deactivate=True): @@ -1083,9 +1082,8 @@ class MultipleLocaleActivationTests(TransRealMixin, TestCase): def test_multiple_locale(self): with translation.override('de'): t = Template("{{ _('No') }}") - with translation.override(self._old_language): - with translation.override('nl'): - self.assertEqual(t.render(Context({})), 'Nee') + with translation.override(self._old_language), translation.override('nl'): + self.assertEqual(t.render(Context({})), 'Nee') def test_multiple_locale_deactivate(self): with translation.override('de', deactivate=True): @@ -1104,9 +1102,8 @@ class MultipleLocaleActivationTests(TransRealMixin, TestCase): def test_multiple_locale_loadi18n(self): with translation.override('de'): t = Template("{% load i18n %}{{ _('No') }}") - with translation.override(self._old_language): - with translation.override('nl'): - self.assertEqual(t.render(Context({})), 'Nee') + with translation.override(self._old_language), translation.override('nl'): + self.assertEqual(t.render(Context({})), 'Nee') def test_multiple_locale_loadi18n_deactivate(self): with translation.override('de', deactivate=True): @@ -1125,9 +1122,8 @@ class MultipleLocaleActivationTests(TransRealMixin, TestCase): def test_multiple_locale_trans(self): with translation.override('de'): t = Template("{% load i18n %}{% trans 'No' %}") - with translation.override(self._old_language): - with translation.override('nl'): - self.assertEqual(t.render(Context({})), 'Nee') + with translation.override(self._old_language), translation.override('nl'): + self.assertEqual(t.render(Context({})), 'Nee') def test_multiple_locale_deactivate_trans(self): with translation.override('de', deactivate=True): @@ -1146,9 +1142,8 @@ class MultipleLocaleActivationTests(TransRealMixin, TestCase): def test_multiple_locale_btrans(self): with translation.override('de'): t = Template("{% load i18n %}{% blocktrans %}No{% endblocktrans %}") - with translation.override(self._old_language): - with translation.override('nl'): - self.assertEqual(t.render(Context({})), 'Nee') + with translation.override(self._old_language), translation.override('nl'): + self.assertEqual(t.render(Context({})), 'Nee') def test_multiple_locale_deactivate_btrans(self): with translation.override('de', deactivate=True): diff --git a/tests/inspectdb/models.py b/tests/inspectdb/models.py index 2862f7f3dc..c25202f248 100644 --- a/tests/inspectdb/models.py +++ b/tests/inspectdb/models.py @@ -37,7 +37,7 @@ class SpecialColumnName(models.Model): class ColumnTypes(models.Model): id = models.AutoField(primary_key=True) big_int_field = models.BigIntegerField() - bool_field = models.BooleanField() + bool_field = models.BooleanField(default=False) null_bool_field = models.NullBooleanField() char_field = models.CharField(max_length=10) comma_separated_int_field = models.CommaSeparatedIntegerField(max_length=99) diff --git a/tests/logging_tests/tests.py b/tests/logging_tests/tests.py index 8ba4702aba..ef9f979e7e 100644 --- a/tests/logging_tests/tests.py +++ b/tests/logging_tests/tests.py @@ -355,20 +355,19 @@ class SettingsConfigureLogging(TestCase): self.assertTrue(dictConfig.called) +@override_settings(DEBUG=True) class SecurityLoggerTest(TestCase): urls = 'logging_tests.urls' def test_suspicious_operation_creates_log_message(self): - with self.settings(DEBUG=True): - with patch_logger('django.security.SuspiciousOperation', 'error') as calls: - response = self.client.get('/suspicious/') - self.assertEqual(len(calls), 1) - self.assertEqual(calls[0], 'dubious') + with patch_logger('django.security.SuspiciousOperation', 'error') as calls: + response = self.client.get('/suspicious/') + self.assertEqual(len(calls), 1) + self.assertEqual(calls[0], 'dubious') def test_suspicious_operation_uses_sublogger(self): - with self.settings(DEBUG=True): - with patch_logger('django.security.DisallowedHost', 'error') as calls: - response = self.client.get('/suspicious_spec/') - self.assertEqual(len(calls), 1) - self.assertEqual(calls[0], 'dubious') + with patch_logger('django.security.DisallowedHost', 'error') as calls: + response = self.client.get('/suspicious_spec/') + self.assertEqual(len(calls), 1) + self.assertEqual(calls[0], 'dubious') diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py index 2d602d6412..8c304cc726 100644 --- a/tests/model_fields/models.py +++ b/tests/model_fields/models.py @@ -58,7 +58,7 @@ class NullBooleanModel(models.Model): nbfield = models.NullBooleanField() class BooleanModel(models.Model): - bfield = models.BooleanField() + bfield = models.BooleanField(default=None) string = models.CharField(max_length=10, default='abc') class FksToBooleans(models.Model): @@ -72,7 +72,7 @@ class RenamedField(models.Model): class VerboseNameField(models.Model): id = models.AutoField("verbose pk", primary_key=True) field1 = models.BigIntegerField("verbose field1") - field2 = models.BooleanField("verbose field2") + field2 = models.BooleanField("verbose field2", default=False) field3 = models.CharField("verbose field3", max_length=10) field4 = models.CommaSeparatedIntegerField("verbose field4", max_length=99) field5 = models.DateField("verbose field5") diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index 378e6280cc..429337f31c 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -12,7 +12,7 @@ from django.db.models.fields import ( AutoField, BigIntegerField, BinaryField, BooleanField, CharField, CommaSeparatedIntegerField, DateField, DateTimeField, DecimalField, EmailField, FilePathField, FloatField, IntegerField, IPAddressField, - GenericIPAddressField, NullBooleanField, PositiveIntegerField, + GenericIPAddressField, NOT_PROVIDED, NullBooleanField, PositiveIntegerField, PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField, TimeField, URLField) from django.db.models.fields.files import FileField, ImageField @@ -275,10 +275,23 @@ class BooleanFieldTests(unittest.TestCase): Check that a BooleanField defaults to None -- which isn't a valid value (#15124). """ - b = BooleanModel() - self.assertIsNone(b.bfield) - with self.assertRaises(IntegrityError): - b.save() + # Patch the boolean field's default value. We give it a default + # value when defining the model to satisfy the check tests + # #20895. + boolean_field = BooleanModel._meta.get_field('bfield') + self.assertTrue(boolean_field.has_default()) + old_default = boolean_field.default + try: + boolean_field.default = NOT_PROVIDED + # check patch was succcessful + self.assertFalse(boolean_field.has_default()) + b = BooleanModel() + self.assertIsNone(b.bfield) + with self.assertRaises(IntegrityError): + b.save() + finally: + boolean_field.default = old_default + nb = NullBooleanModel() self.assertIsNone(nb.nbfield) nb.save() # no error diff --git a/tests/model_formsets/models.py b/tests/model_formsets/models.py index ae152448ab..adeb3455a4 100644 --- a/tests/model_formsets/models.py +++ b/tests/model_formsets/models.py @@ -117,7 +117,7 @@ class OwnerProfile(models.Model): @python_2_unicode_compatible class Restaurant(Place): - serves_pizza = models.BooleanField() + serves_pizza = models.BooleanField(default=False) def __str__(self): return self.name @@ -141,11 +141,11 @@ class Price(models.Model): unique_together = (('price', 'quantity'),) class MexicanRestaurant(Restaurant): - serves_tacos = models.BooleanField() + serves_tacos = models.BooleanField(default=False) class ClassyMexicanRestaurant(MexicanRestaurant): restaurant = models.OneToOneField(MexicanRestaurant, parent_link=True, primary_key=True) - tacos_are_yummy = models.BooleanField() + tacos_are_yummy = models.BooleanField(default=False) # models for testing unique_together validation when a fk is involved and # using inlineformset_factory. diff --git a/tests/model_inheritance/models.py b/tests/model_inheritance/models.py index 2101f394f7..106645d23c 100644 --- a/tests/model_inheritance/models.py +++ b/tests/model_inheritance/models.py @@ -63,7 +63,7 @@ class Attachment(models.Model): return self.content class Comment(Attachment): - is_spam = models.BooleanField() + is_spam = models.BooleanField(default=False) class Link(Attachment): url = models.URLField() @@ -96,8 +96,8 @@ class Rating(models.Model): @python_2_unicode_compatible class Restaurant(Place, Rating): - serves_hot_dogs = models.BooleanField() - serves_pizza = models.BooleanField() + serves_hot_dogs = models.BooleanField(default=False) + serves_pizza = models.BooleanField(default=False) chef = models.ForeignKey(Chef, null=True, blank=True) class Meta(Rating.Meta): @@ -108,7 +108,7 @@ class Restaurant(Place, Rating): @python_2_unicode_compatible class ItalianRestaurant(Restaurant): - serves_gnocchi = models.BooleanField() + serves_gnocchi = models.BooleanField(default=False) def __str__(self): return "%s the italian restaurant" % self.name diff --git a/tests/model_inheritance/tests.py b/tests/model_inheritance/tests.py index eb50d30cf8..b8ab0c8581 100644 --- a/tests/model_inheritance/tests.py +++ b/tests/model_inheritance/tests.py @@ -8,8 +8,9 @@ from django.test import TestCase from django.test.utils import CaptureQueriesContext from django.utils import six -from .models import (Chef, CommonInfo, ItalianRestaurant, ParkingLot, Place, - Post, Restaurant, Student, StudentWorker, Supplier, Worker, MixinModel) +from .models import ( + Chef, CommonInfo, ItalianRestaurant, ParkingLot, Place, Post, + Restaurant, Student, StudentWorker, Supplier, Worker, MixinModel) class ModelInheritanceTests(TestCase): @@ -20,7 +21,7 @@ class ModelInheritanceTests(TestCase): # information for programming purposes, but still completely # independent separate models at the database level. w1 = Worker.objects.create(name="Fred", age=35, job="Quarry worker") - w2 = Worker.objects.create(name="Barney", age=34, job="Quarry worker") + Worker.objects.create(name="Barney", age=34, job="Quarry worker") s = Student.objects.create(name="Pebbles", age=5, school_class="1B") @@ -48,10 +49,12 @@ class ModelInheritanceTests(TestCase): # A StudentWorker which does not exist is both a Student and Worker # which does not exist. - self.assertRaises(Student.DoesNotExist, + self.assertRaises( + Student.DoesNotExist, StudentWorker.objects.get, pk=12321321 ) - self.assertRaises(Worker.DoesNotExist, + self.assertRaises( + Worker.DoesNotExist, StudentWorker.objects.get, pk=12321321 ) @@ -67,10 +70,12 @@ class ModelInheritanceTests(TestCase): sw2.age = 24 sw2.save() - self.assertRaises(Student.MultipleObjectsReturned, + self.assertRaises( + Student.MultipleObjectsReturned, StudentWorker.objects.get, pk__lt=sw2.pk + 100 ) - self.assertRaises(Worker.MultipleObjectsReturned, + self.assertRaises( + Worker.MultipleObjectsReturned, StudentWorker.objects.get, pk__lt=sw2.pk + 100 ) @@ -85,16 +90,16 @@ class ModelInheritanceTests(TestCase): # The Post model doesn't have an attribute called # 'attached_%(class)s_set'. - self.assertRaises(AttributeError, - getattr, post, "attached_%(class)s_set" + self.assertRaises( + AttributeError, getattr, post, "attached_%(class)s_set" ) # The Place/Restaurant/ItalianRestaurant models all exist as # independent models. However, the subclasses also have transparent # access to the fields of their ancestors. # Create a couple of Places. - p1 = Place.objects.create(name="Master Shakes", address="666 W. Jersey") - p2 = Place.objects.create(name="Ace Harware", address="1013 N. Ashland") + Place.objects.create(name="Master Shakes", address="666 W. Jersey") + Place.objects.create(name="Ace Harware", address="1013 N. Ashland") # Test constructor for Restaurant. r = Restaurant.objects.create( @@ -134,11 +139,13 @@ class ModelInheritanceTests(TestCase): # the right order. self.assertEqual( [f.name for f in Restaurant._meta.fields], - ["id", "name", "address", "place_ptr", "rating", "serves_hot_dogs", "serves_pizza", "chef"] + ["id", "name", "address", "place_ptr", "rating", "serves_hot_dogs", + "serves_pizza", "chef"] ) self.assertEqual( [f.name for f in ItalianRestaurant._meta.fields], - ["id", "name", "address", "place_ptr", "rating", "serves_hot_dogs", "serves_pizza", "chef", "restaurant_ptr", "serves_gnocchi"], + ["id", "name", "address", "place_ptr", "rating", "serves_hot_dogs", + "serves_pizza", "chef", "restaurant_ptr", "serves_gnocchi"], ) self.assertEqual(Restaurant._meta.ordering, ["-rating"]) @@ -146,8 +153,8 @@ class ModelInheritanceTests(TestCase): # Restaurant object cannot access that reverse relation, since it's not # part of the Place-Supplier Hierarchy. self.assertQuerysetEqual(Place.objects.filter(supplier__name="foo"), []) - self.assertRaises(FieldError, - Restaurant.objects.filter, supplier__name="foo" + self.assertRaises( + FieldError, Restaurant.objects.filter, supplier__name="foo" ) # Parent fields can be used directly in filters on the child model. @@ -185,16 +192,19 @@ class ModelInheritanceTests(TestCase): # This won't work because the Demon Dogs restaurant is not an Italian # restaurant. - self.assertRaises(ItalianRestaurant.DoesNotExist, + self.assertRaises( + ItalianRestaurant.DoesNotExist, lambda: p.restaurant.italianrestaurant ) # An ItalianRestaurant which does not exist is also a Place which does # not exist. - self.assertRaises(Place.DoesNotExist, + self.assertRaises( + Place.DoesNotExist, ItalianRestaurant.objects.get, name="The Noodle Void" ) # MultipleObjectsReturned is also inherited. - self.assertRaises(Place.MultipleObjectsReturned, + self.assertRaises( + Place.MultipleObjectsReturned, Restaurant.objects.get, id__lt=12321 ) @@ -207,8 +217,8 @@ class ModelInheritanceTests(TestCase): # This won't work because the Place we select is not a Restaurant (it's # a Supplier). p = Place.objects.get(name="Joe's Chickens") - self.assertRaises(Restaurant.DoesNotExist, - lambda: p.restaurant + self.assertRaises( + Restaurant.DoesNotExist, lambda: p.restaurant ) self.assertEqual(p.supplier, s1) @@ -233,10 +243,10 @@ class ModelInheritanceTests(TestCase): attrgetter("name"), ) - park1 = ParkingLot.objects.create( + ParkingLot.objects.create( name="Main St", address="111 Main St", main_site=s1 ) - park2 = ParkingLot.objects.create( + ParkingLot.objects.create( name="Well Lit", address="124 Sesame St", main_site=ir ) @@ -268,11 +278,11 @@ class ModelInheritanceTests(TestCase): # select_related works with fields from the parent object as if they # were a normal part of the model. - self.assertNumQueries(2, - lambda: ItalianRestaurant.objects.all()[0].chef + self.assertNumQueries( + 2, lambda: ItalianRestaurant.objects.all()[0].chef ) - self.assertNumQueries(1, - lambda: ItalianRestaurant.objects.select_related("chef")[0].chef + self.assertNumQueries( + 1, lambda: ItalianRestaurant.objects.select_related("chef")[0].chef ) def test_mixin_init(self): @@ -318,3 +328,32 @@ class ModelInheritanceTests(TestCase): sql = query['sql'] if 'UPDATE' in sql: self.assertEqual(expected_sql, sql) + + def test_eq(self): + # Equality doesn't transfer in multitable inheritance. + self.assertNotEqual(Place(id=1), Restaurant(id=1)) + self.assertNotEqual(Restaurant(id=1), Place(id=1)) + + def test_ticket_12567(self): + r = Restaurant.objects.create(name='n1', address='a1') + s = Supplier.objects.create(name='s1', address='a2') + self.assertQuerysetEqual( + Place.objects.filter(supplier__isnull=False), + [Place.objects.get(pk=s.pk)], + lambda x: x + ) + self.assertQuerysetEqual( + Place.objects.filter(supplier__isnull=True), + [Place.objects.get(pk=r.pk)], + lambda x: x + ) + self.assertQuerysetEqual( + Place.objects.exclude(supplier__isnull=False), + [Place.objects.get(pk=r.pk)], + lambda x: x + ) + self.assertQuerysetEqual( + Place.objects.exclude(supplier__isnull=True), + [Place.objects.get(pk=s.pk)], + lambda x: x + ) diff --git a/tests/model_inheritance_regress/models.py b/tests/model_inheritance_regress/models.py index 811c8175bb..bf076a47c6 100644 --- a/tests/model_inheritance_regress/models.py +++ b/tests/model_inheritance_regress/models.py @@ -18,15 +18,15 @@ class Place(models.Model): @python_2_unicode_compatible class Restaurant(Place): - serves_hot_dogs = models.BooleanField() - serves_pizza = models.BooleanField() + serves_hot_dogs = models.BooleanField(default=False) + serves_pizza = models.BooleanField(default=False) def __str__(self): return "%s the restaurant" % self.name @python_2_unicode_compatible class ItalianRestaurant(Restaurant): - serves_gnocchi = models.BooleanField() + serves_gnocchi = models.BooleanField(default=False) def __str__(self): return "%s the italian restaurant" % self.name @@ -50,6 +50,19 @@ class ParkingLot3(Place): primary_key = models.AutoField(primary_key=True) parent = models.OneToOneField(Place, parent_link=True) +class ParkingLot4(models.Model): + # Test parent_link connector can be discovered in abstract classes. + parent = models.OneToOneField(Place, parent_link=True) + + class Meta: + abstract = True + +class ParkingLot4A(ParkingLot4, Place): + pass + +class ParkingLot4B(Place, ParkingLot4): + pass + class Supplier(models.Model): restaurant = models.ForeignKey(Restaurant) @@ -171,7 +184,7 @@ class Station(SearchableLocation): class BusStation(Station): bus_routes = models.CommaSeparatedIntegerField(max_length=128) - inbound = models.BooleanField() + inbound = models.BooleanField(default=False) class TrainStation(Station): zone = models.IntegerField() diff --git a/tests/model_inheritance_regress/tests.py b/tests/model_inheritance_regress/tests.py index 10a1230685..7f78fc7a98 100644 --- a/tests/model_inheritance_regress/tests.py +++ b/tests/model_inheritance_regress/tests.py @@ -14,7 +14,8 @@ from .models import (Place, Restaurant, ItalianRestaurant, ParkingLot, ParkingLot2, ParkingLot3, Supplier, Wholesaler, Child, SelfRefParent, SelfRefChild, ArticleWithAuthor, M2MChild, QualityControl, DerivedM, Person, BirthdayParty, BachelorParty, MessyBachelorParty, - InternalCertificationAudit, BusStation, TrainStation, User, Profile) + InternalCertificationAudit, BusStation, TrainStation, User, Profile, + ParkingLot4A, ParkingLot4B) class ModelInheritanceTest(TestCase): @@ -311,6 +312,19 @@ class ModelInheritanceTest(TestCase): ParkingLot3._meta.get_ancestor_link(Place).name, "parent") + def test_use_explicit_o2o_to_parent_from_abstract_model(self): + self.assertEqual(ParkingLot4A._meta.pk.name, "parent") + ParkingLot4A.objects.create( + name="Parking4A", + address='21 Jump Street', + ) + + self.assertEqual(ParkingLot4B._meta.pk.name, "parent") + ParkingLot4A.objects.create( + name="Parking4B", + address='21 Jump Street', + ) + def test_all_fields_from_abstract_base_class(self): """ Regression tests for #7588 diff --git a/tests/model_inheritance_select_related/models.py b/tests/model_inheritance_select_related/models.py index 6b28772620..46c67cf07d 100644 --- a/tests/model_inheritance_select_related/models.py +++ b/tests/model_inheritance_select_related/models.py @@ -20,8 +20,8 @@ class Place(models.Model): @python_2_unicode_compatible class Restaurant(Place): - serves_sushi = models.BooleanField() - serves_steak = models.BooleanField() + serves_sushi = models.BooleanField(default=False) + serves_steak = models.BooleanField(default=False) def __str__(self): return "%s the restaurant" % self.name diff --git a/tests/modeladmin/models.py b/tests/modeladmin/models.py index fdbcabd187..4789e35a43 100644 --- a/tests/modeladmin/models.py +++ b/tests/modeladmin/models.py @@ -32,7 +32,7 @@ class ValidationTestModel(models.Model): slug = models.SlugField() users = models.ManyToManyField(User) state = models.CharField(max_length=2, choices=(("CO", "Colorado"), ("WA", "Washington"))) - is_active = models.BooleanField() + is_active = models.BooleanField(default=False) pub_date = models.DateTimeField() band = models.ForeignKey(Band) no = models.IntegerField(verbose_name="Number", blank=True, null=True) # This field is intentionally 2 characters long. See #16080. diff --git a/tests/one_to_one/models.py b/tests/one_to_one/models.py index 9599496cb7..ff809be22d 100644 --- a/tests/one_to_one/models.py +++ b/tests/one_to_one/models.py @@ -22,8 +22,8 @@ class Place(models.Model): @python_2_unicode_compatible class Restaurant(models.Model): place = models.OneToOneField(Place, primary_key=True) - serves_hot_dogs = models.BooleanField() - serves_pizza = models.BooleanField() + serves_hot_dogs = models.BooleanField(default=False) + serves_pizza = models.BooleanField(default=False) def __str__(self): return "%s the restaurant" % self.place.name diff --git a/tests/one_to_one_regress/models.py b/tests/one_to_one_regress/models.py index 058baafc82..9b65edf2ab 100644 --- a/tests/one_to_one_regress/models.py +++ b/tests/one_to_one_regress/models.py @@ -15,8 +15,8 @@ class Place(models.Model): @python_2_unicode_compatible class Restaurant(models.Model): place = models.OneToOneField(Place) - serves_hot_dogs = models.BooleanField() - serves_pizza = models.BooleanField() + serves_hot_dogs = models.BooleanField(default=False) + serves_pizza = models.BooleanField(default=False) def __str__(self): return "%s the restaurant" % self.place.name diff --git a/tests/one_to_one_regress/tests.py b/tests/one_to_one_regress/tests.py index 7d82194abb..0e20f19acb 100644 --- a/tests/one_to_one_regress/tests.py +++ b/tests/one_to_one_regress/tests.py @@ -141,12 +141,10 @@ class OneToOneRegressionTests(TestCase): """ p = Place(name='Zombie Cats', address='Not sure') p.save() - with self.assertNumQueries(1): - with self.assertRaises(Restaurant.DoesNotExist): - p.restaurant - with self.assertNumQueries(0): - with self.assertRaises(Restaurant.DoesNotExist): - p.restaurant + with self.assertNumQueries(1), self.assertRaises(Restaurant.DoesNotExist): + p.restaurant + with self.assertNumQueries(0), self.assertRaises(Restaurant.DoesNotExist): + p.restaurant def test_reverse_object_cached_when_related_is_accessed(self): """ @@ -199,9 +197,8 @@ class OneToOneRegressionTests(TestCase): self.assertEqual(self.p1.undergroundbar, b) b.place = None b.save() - with self.assertNumQueries(0): - with self.assertRaises(UndergroundBar.DoesNotExist): - self.p1.undergroundbar + with self.assertNumQueries(0), self.assertRaises(UndergroundBar.DoesNotExist): + self.p1.undergroundbar def test_get_reverse_on_unsaved_object(self): """ @@ -213,24 +210,21 @@ class OneToOneRegressionTests(TestCase): p = Place() # When there's no instance of the origin of the one-to-one - with self.assertNumQueries(0): - with self.assertRaises(UndergroundBar.DoesNotExist): - p.undergroundbar + with self.assertNumQueries(0), self.assertRaises(UndergroundBar.DoesNotExist): + p.undergroundbar UndergroundBar.objects.create() # When there's one instance of the origin # (p.undergroundbar used to return that instance) - with self.assertNumQueries(0): - with self.assertRaises(UndergroundBar.DoesNotExist): - p.undergroundbar + with self.assertNumQueries(0), self.assertRaises(UndergroundBar.DoesNotExist): + p.undergroundbar UndergroundBar.objects.create() # When there are several instances of the origin - with self.assertNumQueries(0): - with self.assertRaises(UndergroundBar.DoesNotExist): - p.undergroundbar + with self.assertNumQueries(0), self.assertRaises(UndergroundBar.DoesNotExist): + p.undergroundbar def test_set_reverse_on_unsaved_object(self): """ @@ -239,6 +233,5 @@ class OneToOneRegressionTests(TestCase): """ p = Place() b = UndergroundBar.objects.create() - with self.assertNumQueries(0): - with self.assertRaises(ValueError): - p.undergroundbar = b + with self.assertNumQueries(0), self.assertRaises(ValueError): + p.undergroundbar = b diff --git a/tests/proxy_models/admin.py b/tests/proxy_models/admin.py new file mode 100644 index 0000000000..d2b6319301 --- /dev/null +++ b/tests/proxy_models/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin + +from .models import TrackerUser, ProxyTrackerUser + +admin.site.register(TrackerUser) +admin.site.register(ProxyTrackerUser) diff --git a/tests/proxy_models/fixtures/myhorses.json b/tests/proxy_models/fixtures/myhorses.json index 15943064c8..97e190a8a2 100644 --- a/tests/proxy_models/fixtures/myhorses.json +++ b/tests/proxy_models/fixtures/myhorses.json @@ -1,6 +1,24 @@ [ { "pk": 100, + "model": "auth.user", + "fields": { + "username": "super", + "first_name": "Super", + "last_name": "User", + "is_active": true, + "is_superuser": true, + "is_staff": true, + "last_login": "2007-05-30 13:20:10", + "groups": [], + "user_permissions": [], + "password": "sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158", + "email": "super@example.com", + "date_joined": "2007-05-30 13:20:10" + } + }, + { + "pk": 100, "model": "proxy_models.BaseUser", "fields": { "name": "Django Pony" @@ -21,4 +39,4 @@ "assignee": 100 } } -]
\ No newline at end of file +] diff --git a/tests/proxy_models/models.py b/tests/proxy_models/models.py index ffb36657e1..2e5741ab6b 100644 --- a/tests/proxy_models/models.py +++ b/tests/proxy_models/models.py @@ -117,9 +117,13 @@ class StateProxy(State): # Proxy models still works with filters (on related fields) # and select_related, even when mixed with model inheritance +@python_2_unicode_compatible class BaseUser(models.Model): name = models.CharField(max_length=255) + def __str__(self): + return ':'.join((self.__class__.__name__, self.name,)) + class TrackerUser(BaseUser): status = models.CharField(max_length=50) @@ -134,7 +138,7 @@ class Issue(models.Model): assignee = models.ForeignKey(TrackerUser) def __str__(self): - return ':'.join((self.__class__.__name__,self.summary,)) + return ':'.join((self.__class__.__name__, self.summary,)) class Bug(Issue): version = models.CharField(max_length=50) diff --git a/tests/proxy_models/tests.py b/tests/proxy_models/tests.py index 240198cc39..f0c0d046e1 100644 --- a/tests/proxy_models/tests.py +++ b/tests/proxy_models/tests.py @@ -10,12 +10,14 @@ from django.db import models, DEFAULT_DB_ALIAS from django.db.models import signals from django.db.models.loading import cache from django.test import TestCase +from django.test.utils import override_settings from .models import (MyPerson, Person, StatusPerson, LowerStatusPerson, MyPersonProxy, Abstract, OtherPerson, User, UserProxy, UserProxyProxy, Country, State, StateProxy, TrackerUser, BaseUser, Bug, ProxyTrackerUser, Improvement, ProxyProxyBug, ProxyBug, ProxyImprovement, Issue) +from .admin import admin as force_admin_model_registration class ProxyModelTests(TestCase): @@ -362,9 +364,14 @@ class ProxyModelTests(TestCase): p = MyPerson.objects.get(pk=100) self.assertEqual(p.name, 'Elvis Presley') + def test_eq(self): + self.assertEqual(MyPerson(id=100), Person(id=100)) + +@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class ProxyModelAdminTests(TestCase): fixtures = ['myhorses'] + urls = 'proxy_models.urls' def test_cascade_delete_proxy_model_admin_warning(self): """ @@ -380,3 +387,26 @@ class ProxyModelAdminTests(TestCase): self.assertTrue(tracker_user in collector.edges.get(None, ())) self.assertTrue(base_user in collector.edges.get(None, ())) self.assertTrue(issue in collector.edges.get(tracker_user, ())) + + def test_delete_str_in_model_admin(self): + """ + Test if the admin delete page shows the correct string representation + for a proxy model. + """ + user = TrackerUser.objects.get(name='Django Pony') + proxy = ProxyTrackerUser.objects.get(name='Django Pony') + + user_str = ( + 'Tracker user: <a href="/admin/proxy_models/trackeruser/%s/">%s</a>' % (user.pk, user)) + proxy_str = ( + 'Proxy tracker user: <a href="/admin/proxy_models/proxytrackeruser/%s/">%s</a>' % + (proxy.pk, proxy)) + + self.client.login(username='super', password='secret') + response = self.client.get('/admin/proxy_models/trackeruser/%s/delete/' % (user.pk,)) + delete_str = response.context['deleted_objects'][0] + self.assertEqual(delete_str, user_str) + response = self.client.get('/admin/proxy_models/proxytrackeruser/%s/delete/' % (proxy.pk,)) + delete_str = response.context['deleted_objects'][0] + self.assertEqual(delete_str, proxy_str) + self.client.logout() diff --git a/tests/proxy_models/urls.py b/tests/proxy_models/urls.py new file mode 100644 index 0000000000..644a34f556 --- /dev/null +++ b/tests/proxy_models/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import patterns, include + +from django.contrib import admin + +urlpatterns = patterns('', + (r'^admin/', include(admin.site.urls)), +) diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 6e03b0d7f6..4d9ffb353f 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -27,7 +27,6 @@ from .models import ( BaseA, FK1, Identifier, Program, Channel, Page, Paragraph, Chapter, Book, MyObject, Order, OrderItem) - class BaseQuerysetTest(TestCase): def assertValueQuerysetEqual(self, qs, values): return self.assertQuerysetEqual(qs, values, transform=lambda x: x) @@ -84,6 +83,19 @@ class Queries1Tests(BaseQuerysetTest): Cover.objects.create(title="first", item=i4) Cover.objects.create(title="second", item=self.i2) + def test_subquery_condition(self): + qs1 = Tag.objects.filter(pk__lte=0) + qs2 = Tag.objects.filter(parent__in=qs1) + qs3 = Tag.objects.filter(parent__in=qs2) + self.assertEqual(qs3.query.subq_aliases, set(['T', 'U', 'V'])) + self.assertIn('v0', str(qs3.query).lower()) + qs4 = qs3.filter(parent__in=qs1) + self.assertEqual(qs4.query.subq_aliases, set(['T', 'U', 'V'])) + # It is possible to reuse U for the second subquery, no need to use W. + self.assertNotIn('w0', str(qs4.query).lower()) + # So, 'U0."id"' is referenced twice. + self.assertTrue(str(qs4.query).lower().count('u0'), 2) + def test_ticket1050(self): self.assertQuerysetEqual( Item.objects.filter(tags__isnull=True), @@ -810,7 +822,7 @@ class Queries1Tests(BaseQuerysetTest): # Make sure bump_prefix() (an internal Query method) doesn't (re-)break. It's # sufficient that this query runs without error. qs = Tag.objects.values_list('id', flat=True).order_by('id') - qs.query.bump_prefix() + qs.query.bump_prefix(qs.query) first = qs[0] self.assertEqual(list(qs), list(range(first, first+5))) @@ -2940,6 +2952,16 @@ class Ticket20788Tests(TestCase): self.assertQuerysetEqual( sentences_not_in_pub, [book2], lambda x: x) +class Ticket12807Tests(TestCase): + def test_ticket_12807(self): + p1 = Paragraph.objects.create() + p2 = Paragraph.objects.create() + # The ORed condition below should have no effect on the query - the + # ~Q(pk__in=[]) will always be True. + qs = Paragraph.objects.filter((Q(pk=p2.pk) | ~Q(pk__in=[])) & Q(pk=p1.pk)) + self.assertQuerysetEqual(qs, [p1], lambda x: x) + + class RelatedLookupTypeTests(TestCase): def test_wrong_type_lookup(self): oa = ObjectA.objects.create(name="oa") diff --git a/tests/raw_query/models.py b/tests/raw_query/models.py index e7e221dc6e..33f754958e 100644 --- a/tests/raw_query/models.py +++ b/tests/raw_query/models.py @@ -18,7 +18,7 @@ class Author(models.Model): class Book(models.Model): title = models.CharField(max_length=255) author = models.ForeignKey(Author) - paperback = models.BooleanField() + paperback = models.BooleanField(default=False) opening_line = models.TextField() class Coffee(models.Model): diff --git a/tests/reverse_single_related/models.py b/tests/reverse_single_related/models.py index 30ba345120..5d53e04772 100644 --- a/tests/reverse_single_related/models.py +++ b/tests/reverse_single_related/models.py @@ -6,7 +6,7 @@ class SourceManager(models.Manager): return super(SourceManager, self).get_queryset().filter(is_public=True) class Source(models.Model): - is_public = models.BooleanField() + is_public = models.BooleanField(default=False) objects = SourceManager() class Item(models.Model): diff --git a/tests/runtests.py b/tests/runtests.py index 53318a7461..adfc77b13b 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -60,6 +60,7 @@ ALWAYS_INSTALLED_APPS = [ 'staticfiles_tests', 'staticfiles_tests.apps.test', 'staticfiles_tests.apps.no_label', + 'servers.another_app', ] diff --git a/tests/serializers_regress/models.py b/tests/serializers_regress/models.py index 21a3448a8e..ab3d3063a4 100644 --- a/tests/serializers_regress/models.py +++ b/tests/serializers_regress/models.py @@ -16,7 +16,7 @@ class BinaryData(models.Model): data = models.BinaryField(null=True) class BooleanData(models.Model): - data = models.BooleanField() + data = models.BooleanField(default=False) class CharData(models.Model): data = models.CharField(max_length=30, null=True) @@ -166,7 +166,7 @@ class Intermediate(models.Model): # or all database backends. class BooleanPKData(models.Model): - data = models.BooleanField(primary_key=True) + data = models.BooleanField(primary_key=True, default=False) class CharPKData(models.Model): data = models.CharField(max_length=30, primary_key=True) diff --git a/tests/servers/another_app/__init__.py b/tests/servers/another_app/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/servers/another_app/__init__.py diff --git a/tests/servers/another_app/models.py b/tests/servers/another_app/models.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/servers/another_app/models.py diff --git a/tests/servers/another_app/static/another_app/another_app_static_file.txt b/tests/servers/another_app/static/another_app/another_app_static_file.txt new file mode 100644 index 0000000000..a2784fa8e2 --- /dev/null +++ b/tests/servers/another_app/static/another_app/another_app_static_file.txt @@ -0,0 +1 @@ +static file from another_app diff --git a/tests/servers/tests.py b/tests/servers/tests.py index 1f02a88d5a..0340873013 100644 --- a/tests/servers/tests.py +++ b/tests/servers/tests.py @@ -145,6 +145,14 @@ class LiveServerViews(LiveServerBase): f = self.urlopen('/static/example_static_file.txt') self.assertEqual(f.read().rstrip(b'\r\n'), b'example static file') + def test_collectstatic_emulation(self): + """ + Test LiveServerTestCase use of staticfiles' serve() allows it to + discover app's static assets without having to collectstatic first. + """ + f = self.urlopen('/static/another_app/another_app_static_file.txt') + self.assertEqual(f.read().rstrip(b'\r\n'), b'static file from another_app') + def test_media_files(self): """ Ensure that the LiveServerTestCase serves media files. diff --git a/tests/template_tests/test_context.py b/tests/template_tests/test_context.py index ca167a73f3..7bcfb7f9f2 100644 --- a/tests/template_tests/test_context.py +++ b/tests/template_tests/test_context.py @@ -2,7 +2,7 @@ from unittest import TestCase -from django.template import Context +from django.template import Context, Variable, VariableDoesNotExist class ContextTests(TestCase): @@ -25,3 +25,12 @@ class ContextTests(TestCase): with c.push(a=3): self.assertEqual(c['a'], 3) self.assertEqual(c['a'], 1) + + def test_resolve_on_context_method(self): + # Regression test for #17778 + empty_context = Context() + self.assertRaises(VariableDoesNotExist, + Variable('no_such_variable').resolve, empty_context) + self.assertRaises(VariableDoesNotExist, + Variable('new').resolve, empty_context) + self.assertEqual(Variable('new').resolve(Context({'new': 'foo'})), 'foo') diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index aa0283be79..034280f08d 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -1575,6 +1575,13 @@ class TemplateTests(TransRealMixin, TestCase): # Test whitespace in filter argument 'widthratio15': ('{% load custom %}{% widthratio a|noop:"x y" b 0 %}', {'a':50,'b':100}, '0'), + # Widthratio with variable assignment + 'widthratio16': ('{% widthratio a b 100 as variable %}-{{ variable }}-', {'a':50,'b':100}, '-50-'), + 'widthratio17': ('{% widthratio a b 100 as variable %}-{{ variable }}-', {'a':100,'b':100}, '-100-'), + + 'widthratio18': ('{% widthratio a b 100 as %}', { }, template.TemplateSyntaxError), + 'widthratio19': ('{% widthratio a b 100 not_as variable %}', { }, template.TemplateSyntaxError), + ### WITH TAG ######################################################## 'with01': ('{% with key=dict.key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'), 'legacywith01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'), diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py index 67e66fa52d..857d6d83c0 100644 --- a/tests/test_client_regress/tests.py +++ b/tests/test_client_regress/tests.py @@ -925,6 +925,14 @@ class ContextTests(TestCase): finally: django.template.context._standard_context_processors = None + def test_nested_requests(self): + """ + response.context is not lost when view call another view. + """ + response = self.client.get("/test_client_regress/nested_view/") + self.assertEqual(response.context.__class__, Context) + self.assertEqual(response.context['nested'], 'yes') + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class SessionTests(TestCase): diff --git a/tests/test_client_regress/urls.py b/tests/test_client_regress/urls.py index 6a0b330e02..1bde10315d 100644 --- a/tests/test_client_regress/urls.py +++ b/tests/test_client_regress/urls.py @@ -11,6 +11,7 @@ urlpatterns = patterns('', (r'^request_data/$', views.request_data), (r'^request_data_extended/$', views.request_data, {'template':'extended.html', 'data':'bacon'}), url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'), + url(r'^nested_view/$', views.nested_view, name='nested_view'), (r'^login_protected_redirect_view/$', views.login_protected_redirect_view), (r'^redirects/$', RedirectView.as_view(url='/test_client_regress/redirects/further/')), (r'^redirects/further/$', RedirectView.as_view(url='/test_client_regress/redirects/further/more/')), diff --git a/tests/test_client_regress/views.py b/tests/test_client_regress/views.py index 71e5b526e5..d7c54cf2f7 100644 --- a/tests/test_client_regress/views.py +++ b/tests/test_client_regress/views.py @@ -5,8 +5,10 @@ from django.contrib.auth.decorators import login_required from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from django.core.serializers.json import DjangoJSONEncoder -from django.test.client import CONTENT_TYPE_RE from django.template import RequestContext +from django.test import Client +from django.test.client import CONTENT_TYPE_RE +from django.test.utils import setup_test_environment class CustomTestException(Exception): @@ -52,6 +54,15 @@ def view_with_argument(request, name): else: return HttpResponse('Howdy, %s' % name) +def nested_view(request): + """ + A view that uses test client to call another view. + """ + setup_test_environment() + c = Client() + c.get("/test_client_regress/no_template_view") + return render_to_response('base.html', {'nested':'yes'}) + def login_protected_redirect_view(request): "A view that redirects all requests to the GET view" return HttpResponseRedirect('/test_client_regress/get_view/') diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index eb850cc3be..1b3c78b2e2 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -152,9 +152,8 @@ class CaptureQueriesContextManagerTests(TestCase): self.assertEqual(2, len(captured_queries)) def test_failure(self): - with self.assertRaises(TypeError): - with CaptureQueriesContext(connection): - raise TypeError + with self.assertRaises(TypeError), CaptureQueriesContext(connection): + raise TypeError def test_with_client(self): with CaptureQueriesContext(connection) as captured_queries: @@ -190,14 +189,12 @@ class AssertNumQueriesContextManagerTests(TestCase): Person.objects.count() def test_failure(self): - with self.assertRaises(AssertionError) as exc_info: - with self.assertNumQueries(2): - Person.objects.count() + with self.assertRaises(AssertionError) as exc_info, self.assertNumQueries(2): + Person.objects.count() self.assertIn("1 queries executed, 2 expected", str(exc_info.exception)) - with self.assertRaises(TypeError): - with self.assertNumQueries(4000): - raise TypeError + with self.assertRaises(TypeError), self.assertNumQueries(4000): + raise TypeError def test_with_client(self): person = Person.objects.create(name="test") @@ -232,13 +229,13 @@ class AssertTemplateUsedContextManagerTests(TestCase): render_to_string('template_used/base.html') def test_nested_usage(self): - with self.assertTemplateUsed('template_used/base.html'): - with self.assertTemplateUsed('template_used/include.html'): - render_to_string('template_used/include.html') + with self.assertTemplateUsed('template_used/base.html'), \ + self.assertTemplateUsed('template_used/include.html'): + render_to_string('template_used/include.html') - with self.assertTemplateUsed('template_used/extends.html'): - with self.assertTemplateUsed('template_used/base.html'): - render_to_string('template_used/extends.html') + with self.assertTemplateUsed('template_used/extends.html'), \ + self.assertTemplateUsed('template_used/base.html'): + render_to_string('template_used/extends.html') with self.assertTemplateUsed('template_used/base.html'): with self.assertTemplateUsed('template_used/alternative.html'): @@ -258,38 +255,34 @@ class AssertTemplateUsedContextManagerTests(TestCase): pass def test_error_message(self): - with six.assertRaisesRegex(self, AssertionError, r'^template_used/base\.html'): - with self.assertTemplateUsed('template_used/base.html'): - pass + with six.assertRaisesRegex(self, AssertionError, r'^template_used/base\.html'), \ + self.assertTemplateUsed('template_used/base.html'): + pass - with six.assertRaisesRegex(self, AssertionError, r'^template_used/base\.html'): - with self.assertTemplateUsed(template_name='template_used/base.html'): - pass + with six.assertRaisesRegex(self, AssertionError, r'^template_used/base\.html'), \ + self.assertTemplateUsed(template_name='template_used/base.html'): + pass - with six.assertRaisesRegex(self, AssertionError, r'^template_used/base\.html.*template_used/alternative\.html$'): - with self.assertTemplateUsed('template_used/base.html'): - render_to_string('template_used/alternative.html') + with six.assertRaisesRegex(self, AssertionError, r'^template_used/base\.html.*template_used/alternative\.html$'), \ + self.assertTemplateUsed('template_used/base.html'): + render_to_string('template_used/alternative.html') def test_failure(self): - with self.assertRaises(TypeError): - with self.assertTemplateUsed(): - pass + with self.assertRaises(TypeError), self.assertTemplateUsed(): + pass - with self.assertRaises(AssertionError): - with self.assertTemplateUsed(''): - pass + with self.assertRaises(AssertionError), self.assertTemplateUsed(''): + pass - with self.assertRaises(AssertionError): - with self.assertTemplateUsed(''): - render_to_string('template_used/base.html') + with self.assertRaises(AssertionError), self.assertTemplateUsed(''): + render_to_string('template_used/base.html') - with self.assertRaises(AssertionError): - with self.assertTemplateUsed(template_name=''): - pass + with self.assertRaises(AssertionError), self.assertTemplateUsed(template_name=''): + pass - with self.assertRaises(AssertionError): - with self.assertTemplateUsed('template_used/base.html'): - render_to_string('template_used/alternative.html') + with self.assertRaises(AssertionError), \ + self.assertTemplateUsed('template_used/base.html'): + render_to_string('template_used/alternative.html') class HTMLEqualTests(TestCase): diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py index 9cf8b4d742..d7cbbd0f3c 100644 --- a/tests/transactions/tests.py +++ b/tests/transactions/tests.py @@ -65,10 +65,9 @@ class AtomicTests(TransactionTestCase): self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>']) def test_rollback(self): - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(): - Reporter.objects.create(first_name="Haddock") - raise Exception("Oops, that's his last name") + with six.assertRaisesRegex(self, Exception, "Oops"), transaction.atomic(): + Reporter.objects.create(first_name="Haddock") + raise Exception("Oops, that's his last name") self.assertQuerysetEqual(Reporter.objects.all(), []) def test_nested_commit_commit(self): @@ -82,30 +81,27 @@ class AtomicTests(TransactionTestCase): def test_nested_commit_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(): - Reporter.objects.create(first_name="Haddock") - raise Exception("Oops, that's his last name") + with six.assertRaisesRegex(self, Exception, "Oops"), transaction.atomic(): + Reporter.objects.create(first_name="Haddock") + raise Exception("Oops, that's his last name") self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>']) def test_nested_rollback_commit(self): - with six.assertRaisesRegex(self, Exception, "Oops"): + with six.assertRaisesRegex(self, Exception, "Oops"), transaction.atomic(): + Reporter.objects.create(last_name="Tintin") with transaction.atomic(): - Reporter.objects.create(last_name="Tintin") - with transaction.atomic(): - Reporter.objects.create(last_name="Haddock") - raise Exception("Oops, that's his first name") + Reporter.objects.create(last_name="Haddock") + raise Exception("Oops, that's his first name") self.assertQuerysetEqual(Reporter.objects.all(), []) def test_nested_rollback_rollback(self): - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(): - Reporter.objects.create(last_name="Tintin") - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(): - Reporter.objects.create(first_name="Haddock") - raise Exception("Oops, that's his last name") - raise Exception("Oops, that's his first name") + with six.assertRaisesRegex(self, Exception, "Oops"), transaction.atomic(): + Reporter.objects.create(last_name="Tintin") + with six.assertRaisesRegex(self, Exception, "Oops"): + with transaction.atomic(): + Reporter.objects.create(first_name="Haddock") + raise Exception("Oops, that's his last name") + raise Exception("Oops, that's his first name") self.assertQuerysetEqual(Reporter.objects.all(), []) def test_merged_commit_commit(self): @@ -119,31 +115,29 @@ class AtomicTests(TransactionTestCase): def test_merged_commit_rollback(self): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(savepoint=False): - Reporter.objects.create(first_name="Haddock") - raise Exception("Oops, that's his last name") + with six.assertRaisesRegex(self, Exception, "Oops"), \ + transaction.atomic(savepoint=False): + Reporter.objects.create(first_name="Haddock") + raise Exception("Oops, that's his last name") # Writes in the outer block are rolled back too. self.assertQuerysetEqual(Reporter.objects.all(), []) def test_merged_rollback_commit(self): - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(): - Reporter.objects.create(last_name="Tintin") - with transaction.atomic(savepoint=False): - Reporter.objects.create(last_name="Haddock") - raise Exception("Oops, that's his first name") + with six.assertRaisesRegex(self, Exception, "Oops"), transaction.atomic(): + Reporter.objects.create(last_name="Tintin") + with transaction.atomic(savepoint=False): + Reporter.objects.create(last_name="Haddock") + raise Exception("Oops, that's his first name") self.assertQuerysetEqual(Reporter.objects.all(), []) def test_merged_rollback_rollback(self): - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(): - Reporter.objects.create(last_name="Tintin") - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(savepoint=False): - Reporter.objects.create(first_name="Haddock") - raise Exception("Oops, that's his last name") - raise Exception("Oops, that's his first name") + with six.assertRaisesRegex(self, Exception, "Oops"), transaction.atomic(): + Reporter.objects.create(last_name="Tintin") + with six.assertRaisesRegex(self, Exception, "Oops"): + with transaction.atomic(savepoint=False): + Reporter.objects.create(first_name="Haddock") + raise Exception("Oops, that's his last name") + raise Exception("Oops, that's his first name") self.assertQuerysetEqual(Reporter.objects.all(), []) def test_reuse_commit_commit(self): @@ -159,32 +153,29 @@ class AtomicTests(TransactionTestCase): atomic = transaction.atomic() with atomic: Reporter.objects.create(first_name="Tintin") - with six.assertRaisesRegex(self, Exception, "Oops"): - with atomic: - Reporter.objects.create(first_name="Haddock") - raise Exception("Oops, that's his last name") + with six.assertRaisesRegex(self, Exception, "Oops"), atomic: + Reporter.objects.create(first_name="Haddock") + raise Exception("Oops, that's his last name") self.assertQuerysetEqual(Reporter.objects.all(), ['<Reporter: Tintin>']) def test_reuse_rollback_commit(self): atomic = transaction.atomic() - with six.assertRaisesRegex(self, Exception, "Oops"): + with six.assertRaisesRegex(self, Exception, "Oops"), atomic: + Reporter.objects.create(last_name="Tintin") with atomic: - Reporter.objects.create(last_name="Tintin") - with atomic: - Reporter.objects.create(last_name="Haddock") - raise Exception("Oops, that's his first name") + Reporter.objects.create(last_name="Haddock") + raise Exception("Oops, that's his first name") self.assertQuerysetEqual(Reporter.objects.all(), []) def test_reuse_rollback_rollback(self): atomic = transaction.atomic() - with six.assertRaisesRegex(self, Exception, "Oops"): - with atomic: - Reporter.objects.create(last_name="Tintin") - with six.assertRaisesRegex(self, Exception, "Oops"): - with atomic: - Reporter.objects.create(first_name="Haddock") - raise Exception("Oops, that's his last name") - raise Exception("Oops, that's his first name") + with six.assertRaisesRegex(self, Exception, "Oops"), atomic: + Reporter.objects.create(last_name="Tintin") + with six.assertRaisesRegex(self, Exception, "Oops"): + with atomic: + Reporter.objects.create(first_name="Haddock") + raise Exception("Oops, that's his last name") + raise Exception("Oops, that's his first name") self.assertQuerysetEqual(Reporter.objects.all(), []) def test_force_rollback(self): @@ -200,10 +191,9 @@ class AtomicTests(TransactionTestCase): 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") + with self.assertRaises(DatabaseError), 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()) @@ -263,10 +253,10 @@ class AtomicMergeTests(TransactionTestCase): Reporter.objects.create(first_name="Tintin") with transaction.atomic(savepoint=False): Reporter.objects.create(first_name="Archibald", last_name="Haddock") - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(savepoint=False): - Reporter.objects.create(first_name="Tournesol") - raise Exception("Oops, that's his last name") + with six.assertRaisesRegex(self, Exception, "Oops"), \ + transaction.atomic(savepoint=False): + Reporter.objects.create(first_name="Tournesol") + raise Exception("Oops, that's his last name") # It wasn't possible to roll back self.assertEqual(Reporter.objects.count(), 3) # It wasn't possible to roll back @@ -279,10 +269,10 @@ class AtomicMergeTests(TransactionTestCase): Reporter.objects.create(first_name="Tintin") with transaction.atomic(): Reporter.objects.create(first_name="Archibald", last_name="Haddock") - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(savepoint=False): - Reporter.objects.create(first_name="Tournesol") - raise Exception("Oops, that's his last name") + with six.assertRaisesRegex(self, Exception, "Oops"), \ + transaction.atomic(savepoint=False): + Reporter.objects.create(first_name="Tournesol") + raise Exception("Oops, that's his last name") # It wasn't possible to roll back self.assertEqual(Reporter.objects.count(), 3) # The first block with a savepoint must roll back @@ -293,10 +283,10 @@ class AtomicMergeTests(TransactionTestCase): with transaction.atomic(): Reporter.objects.create(first_name="Tintin") # Inner block without a savepoint fails - with six.assertRaisesRegex(self, Exception, "Oops"): - with transaction.atomic(savepoint=False): - Reporter.objects.create(first_name="Haddock") - raise Exception("Oops, that's his last name") + with six.assertRaisesRegex(self, Exception, "Oops"), \ + transaction.atomic(savepoint=False): + Reporter.objects.create(first_name="Haddock") + raise Exception("Oops, that's his last name") # It wasn't possible to roll back self.assertEqual(Reporter.objects.count(), 2) # Inner block with a savepoint succeeds @@ -316,9 +306,9 @@ class AtomicErrorsTests(TransactionTestCase): def test_atomic_prevents_setting_autocommit(self): autocommit = transaction.get_autocommit() - with transaction.atomic(): - with self.assertRaises(transaction.TransactionManagementError): - transaction.set_autocommit(not autocommit) + with transaction.atomic(), \ + self.assertRaises(transaction.TransactionManagementError): + transaction.set_autocommit(not autocommit) # Make sure autocommit wasn't changed. self.assertEqual(connection.autocommit, autocommit) @@ -552,9 +542,8 @@ class TransactionContextManagerTests(IgnoreDeprecationWarningsMixin, Transaction The autocommit context manager works exactly the same as the default behavior. """ - with self.assertRaises(Exception): - with transaction.autocommit(): - self.create_reporter_and_fail() + with self.assertRaises(Exception), transaction.autocommit(): + self.create_reporter_and_fail() self.assertEqual(Reporter.objects.count(), 1) @@ -563,9 +552,8 @@ class TransactionContextManagerTests(IgnoreDeprecationWarningsMixin, Transaction """ The autocommit context manager also works with a using argument. """ - with self.assertRaises(Exception): - with transaction.autocommit(using="default"): - self.create_reporter_and_fail() + with self.assertRaises(Exception), transaction.autocommit(using="default"): + self.create_reporter_and_fail() self.assertEqual(Reporter.objects.count(), 1) @@ -575,9 +563,8 @@ class TransactionContextManagerTests(IgnoreDeprecationWarningsMixin, Transaction With the commit_on_success context manager, the transaction is only committed if the block doesn't throw an exception. """ - with self.assertRaises(Exception): - with transaction.commit_on_success(): - self.create_reporter_and_fail() + with self.assertRaises(Exception), transaction.commit_on_success(): + self.create_reporter_and_fail() self.assertEqual(Reporter.objects.count(), 0) @@ -586,9 +573,8 @@ class TransactionContextManagerTests(IgnoreDeprecationWarningsMixin, Transaction """ The commit_on_success context manager also works with a using argument. """ - with self.assertRaises(Exception): - with transaction.commit_on_success(using="default"): - self.create_reporter_and_fail() + with self.assertRaises(Exception), transaction.commit_on_success(using="default"): + self.create_reporter_and_fail() self.assertEqual(Reporter.objects.count(), 0) @@ -633,18 +619,18 @@ class TransactionContextManagerTests(IgnoreDeprecationWarningsMixin, Transaction """ If you forget, you'll get bad errors. """ - with self.assertRaises(transaction.TransactionManagementError): - with transaction.commit_manually(): - Reporter.objects.create(first_name="Scott", last_name="Browning") + with self.assertRaises(transaction.TransactionManagementError), \ + transaction.commit_manually(): + Reporter.objects.create(first_name="Scott", last_name="Browning") @skipUnlessDBFeature('supports_transactions') def test_manually_managed_with_using(self): """ The commit_manually function also works with a using argument. """ - with self.assertRaises(transaction.TransactionManagementError): - with transaction.commit_manually(using="default"): - Reporter.objects.create(first_name="Walter", last_name="Cronkite") + with self.assertRaises(transaction.TransactionManagementError), \ + transaction.commit_manually(using="default"): + Reporter.objects.create(first_name="Walter", last_name="Cronkite") @skipUnlessDBFeature('requires_rollback_on_dirty_transaction') def test_bad_sql(self): @@ -654,8 +640,7 @@ class TransactionContextManagerTests(IgnoreDeprecationWarningsMixin, Transaction be rolled back. The bug is only visible using the psycopg2 backend, though the fix is generally a good idea. """ - with self.assertRaises(IntegrityError): - with transaction.commit_on_success(): - cursor = connection.cursor() - cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');") + with self.assertRaises(IntegrityError), transaction.commit_on_success(): + cursor = connection.cursor() + cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');") transaction.rollback() diff --git a/tests/utils_tests/test_simplelazyobject.py b/tests/utils_tests/test_simplelazyobject.py index 7b681d9290..f16b5e4da1 100644 --- a/tests/utils_tests/test_simplelazyobject.py +++ b/tests/utils_tests/test_simplelazyobject.py @@ -136,6 +136,9 @@ class TestUtilsSimpleLazyObject(TestCase): self.assertEqual(lazydict['one'], 1) lazydict['one'] = -1 self.assertEqual(lazydict['one'], -1) + self.assertTrue('one' in lazydict) + self.assertFalse('two' in lazydict) + self.assertEqual(len(lazydict), 1) del lazydict['one'] with self.assertRaises(KeyError): lazydict['one'] @@ -183,3 +186,13 @@ class TestUtilsSimpleLazyObject(TestCase): # This would fail with "TypeError: expected string or Unicode object, NoneType found". pickled = cPickle.dumps(x) + + def test_list_set(self): + lazy_list = SimpleLazyObject(lambda: [1, 2, 3, 4, 5]) + lazy_set = SimpleLazyObject(lambda: set([1, 2, 3, 4])) + self.assertTrue(1 in lazy_list) + self.assertTrue(1 in lazy_set) + self.assertFalse(6 in lazy_list) + self.assertFalse(6 in lazy_set) + self.assertEqual(len(lazy_list), 5) + self.assertEqual(len(lazy_set), 4) diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index 0159886918..e941f4ae0e 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import inspect import os +import re import shutil import sys from tempfile import NamedTemporaryFile, mkdtemp, mkstemp @@ -69,6 +70,21 @@ class DebugViewTests(TestCase): self.assertRaises(BrokenException, self.client.get, reverse('view_exception', args=(n,))) + def test_non_l10ned_numeric_ids(self): + """ + Numeric IDs and fancy traceback context blocks line numbers shouldn't be localized. + """ + with self.settings(DEBUG=True, USE_L10N=True): + response = self.client.get('/views/raises500/') + # We look for a HTML fragment of the form + # '<div class="context" id="c38123208">', not '<div class="context" id="c38,123,208"' + self.assertContains(response, '<div class="context" id="', status_code=500) + match = re.search(b'<div class="context" id="(?P<id>[^"]+)">', response.content) + self.assertFalse(match is None) + id_repr = match.group('id') + self.assertFalse(re.search(b'[^c\d]', id_repr), + "Numeric IDs in debug response HTML page shouldn't be localized (value: %s)." % id_repr) + def test_template_exceptions(self): for n in range(len(except_args)): try: diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index cfee4f5093..50677947b6 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -85,20 +85,18 @@ class JsI18NTests(TestCase): languages and you've set settings.LANGUAGE_CODE to some other language than English. """ - with self.settings(LANGUAGE_CODE='es'): - with override('en-us'): - response = self.client.get('/views/jsi18n/') - self.assertNotContains(response, 'esto tiene que ser traducido') + with self.settings(LANGUAGE_CODE='es'), override('en-us'): + response = self.client.get('/views/jsi18n/') + self.assertNotContains(response, 'esto tiene que ser traducido') def test_jsi18n_fallback_language(self): """ Let's make sure that the fallback language is still working properly in cases where the selected language cannot be found. """ - with self.settings(LANGUAGE_CODE='fr'): - with override('fi'): - response = self.client.get('/views/jsi18n/') - self.assertContains(response, 'il faut le traduire') + with self.settings(LANGUAGE_CODE='fr'), override('fi'): + response = self.client.get('/views/jsi18n/') + self.assertContains(response, 'il faut le traduire') def testI18NLanguageNonEnglishDefault(self): """ @@ -107,10 +105,9 @@ class JsI18NTests(TestCase): is English and there is not 'en' translation available. See #13388, #3594 and #13726 for more details. """ - with self.settings(LANGUAGE_CODE='fr'): - with override('en-us'): - response = self.client.get('/views/jsi18n/') - self.assertNotContains(response, 'Choisir une heure') + with self.settings(LANGUAGE_CODE='fr'), override('en-us'): + response = self.client.get('/views/jsi18n/') + self.assertNotContains(response, 'Choisir une heure') def test_nonenglish_default_english_userpref(self): """ @@ -119,20 +116,19 @@ class JsI18NTests(TestCase): with the proper English translations. See #13726 for more details. """ extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app0'] - with self.settings(LANGUAGE_CODE='fr', INSTALLED_APPS=extended_apps): - with override('en-us'): - response = self.client.get('/views/jsi18n_english_translation/') - self.assertContains(response, javascript_quote('this app0 string is to be translated')) + with self.settings(LANGUAGE_CODE='fr', INSTALLED_APPS=extended_apps), \ + override('en-us'): + response = self.client.get('/views/jsi18n_english_translation/') + self.assertContains(response, javascript_quote('this app0 string is to be translated')) def testI18NLanguageNonEnglishFallback(self): """ Makes sure that the fallback language is still working properly in cases where the selected language cannot be found. """ - with self.settings(LANGUAGE_CODE='fr'): - with override('none'): - response = self.client.get('/views/jsi18n/') - self.assertContains(response, 'Choisir une heure') + with self.settings(LANGUAGE_CODE='fr'), override('none'): + response = self.client.get('/views/jsi18n/') + self.assertContains(response, 'Choisir une heure') class JsI18NTestsMultiPackage(TestCase): @@ -149,10 +145,11 @@ class JsI18NTestsMultiPackage(TestCase): #3594 and #13514 for more details. """ extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app1', 'view_tests.app2'] - with self.settings(LANGUAGE_CODE='en-us', INSTALLED_APPS=extended_apps): - with override('fr'): - response = self.client.get('/views/jsi18n_multi_packages1/') - self.assertContains(response, javascript_quote('il faut traduire cette chaîne de caractères de app1')) + with self.settings(LANGUAGE_CODE='en-us', INSTALLED_APPS=extended_apps), \ + override('fr'): + response = self.client.get('/views/jsi18n_multi_packages1/') + self.assertContains(response, + javascript_quote('il faut traduire cette chaîne de caractères de app1')) def testI18NDifferentNonEnLangs(self): """ @@ -160,20 +157,21 @@ class JsI18NTestsMultiPackage(TestCase): English. """ extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app3', 'view_tests.app4'] - with self.settings(LANGUAGE_CODE='fr', INSTALLED_APPS=extended_apps): - with override('es-ar'): - response = self.client.get('/views/jsi18n_multi_packages2/') - self.assertContains(response, javascript_quote('este texto de app3 debe ser traducido')) + with self.settings(LANGUAGE_CODE='fr', INSTALLED_APPS=extended_apps), \ + override('es-ar'): + response = self.client.get('/views/jsi18n_multi_packages2/') + self.assertContains(response, + javascript_quote('este texto de app3 debe ser traducido')) def testI18NWithLocalePaths(self): extended_locale_paths = settings.LOCALE_PATHS + ( path.join(path.dirname( path.dirname(path.abspath(upath(__file__)))), 'app3', 'locale'),) - with self.settings(LANGUAGE_CODE='es-ar', LOCALE_PATHS=extended_locale_paths): - with override('es-ar'): - response = self.client.get('/views/jsi18n/') - self.assertContains(response, - javascript_quote('este texto de app3 debe ser traducido')) + with self.settings(LANGUAGE_CODE='es-ar', LOCALE_PATHS=extended_locale_paths), \ + override('es-ar'): + response = self.client.get('/views/jsi18n/') + self.assertContains(response, + javascript_quote('este texto de app3 debe ser traducido')) skip_selenium = not os.environ.get('DJANGO_SELENIUM_TESTS', False) diff --git a/tests/view_tests/tests/test_static.py b/tests/view_tests/tests/test_static.py index d2f3f47fa7..8f67890c11 100644 --- a/tests/view_tests/tests/test_static.py +++ b/tests/view_tests/tests/test_static.py @@ -6,7 +6,7 @@ import unittest from django.conf.urls.static import static from django.http import HttpResponseNotModified -from django.test import TestCase +from django.test import SimpleTestCase from django.test.utils import override_settings from django.utils.http import http_date from django.views.static import was_modified_since @@ -16,7 +16,7 @@ from ..urls import media_dir @override_settings(DEBUG=True) -class StaticTests(TestCase): +class StaticTests(SimpleTestCase): """Tests django views in django/views/static.py""" prefix = 'site_media' @@ -94,6 +94,10 @@ class StaticTests(TestCase): self.assertEqual(len(response_content), int(response['Content-Length'])) + def test_404(self): + response = self.client.get('/views/%s/non_existing_resource' % self.prefix) + self.assertEqual(404, response.status_code) + class StaticHelperTest(StaticTests): """ diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py index f2c910368d..4db3e262ed 100644 --- a/tests/view_tests/urls.py +++ b/tests/view_tests/urls.py @@ -49,6 +49,7 @@ urlpatterns = patterns('', (r'raises400/$', views.raises400), (r'raises403/$', views.raises403), (r'raises404/$', views.raises404), + (r'raises500/$', views.raises500), # i18n views (r'^i18n/', include('django.conf.urls.i18n')), diff --git a/tests/view_tests/views.py b/tests/view_tests/views.py index 0bac7d9321..04924bc9f7 100644 --- a/tests/view_tests/views.py +++ b/tests/view_tests/views.py @@ -31,6 +31,14 @@ def raises(request): except Exception: return technical_500_response(request, *sys.exc_info()) +def raises500(request): + # We need to inspect the HTML generated by the fancy 500 debug view but + # the test client ignores it, so we send it explicitly. + try: + raise Exception + except Exception: + return technical_500_response(request, *sys.exc_info()) + def raises400(request): raise SuspiciousOperation |
