From 70f2d9aaffcdd255b1fde6c83b7fe584c4ee3d45 Mon Sep 17 00:00:00 2001 From: Jacob Kaplan-Moss Date: Fri, 28 Jun 2013 08:15:10 -0500 Subject: [PATCH 001/944] Updated version numbers and setup.py for 1.6beta. --- django/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/django/__init__.py b/django/__init__.py index 5a1c74efa780..d0c5d77bfa17 100644 --- a/django/__init__.py +++ b/django/__init__.py @@ -1,4 +1,4 @@ -VERSION = (1, 6, 0, 'alpha', 1) +VERSION = (1, 6, 0, 'beta', 1) def get_version(*args, **kwargs): # Don't litter django/__init__.py with all the get_version stuff. diff --git a/setup.py b/setup.py index 2c64868d0014..3abbac8ae79d 100644 --- a/setup.py +++ b/setup.py @@ -93,7 +93,7 @@ def is_package(package_name): package_data=package_data, scripts=['django/bin/django-admin.py'], classifiers=[ - 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', From ec2a102d8491a708b56abb19caeebdc5562c2a9c Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 28 Jun 2013 16:27:07 +0200 Subject: [PATCH 002/944] [1.6.x] Updated FAQ entry about python 3 Backport of 94f420ef4 from master. --- docs/faq/install.txt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/faq/install.txt b/docs/faq/install.txt index 5a4cab94cfac..5ca7a471c85c 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -77,15 +77,12 @@ Django version Python versions Can I use Django with Python 3? ------------------------------- -Django 1.5 introduces experimental support for Python 3.2.3 and above. However, -we don't yet suggest that you use Django and Python 3 in production. +Yes, you can! -Python 3 support should be considered a "preview". It's offered to bootstrap -the transition of the Django ecosystem to Python 3, and to help you start -porting your apps for future Python 3 compatibility. But we're not yet -confident enough to promise stability in production. +Django 1.5 introduced experimental support for Python 3.2.3 and above. -Our current plan is to make Django 1.6 suitable for general use with Python 3. +As of Django 1.6, Python 3 support is considered stable and you can safely use +it in production. See also :doc:`/topics/python3`. Will Django run under shared hosting (like TextDrive or Dreamhost)? ------------------------------------------------------------------- From dc99128343a08dce8a056e50047ea8bc909740d5 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 28 Jun 2013 16:38:55 +0200 Subject: [PATCH 003/944] [1.6.x] Updated FAQ to reflect official Python 3 support Backport of 8809da67a from master. --- docs/faq/install.txt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/faq/install.txt b/docs/faq/install.txt index 5ca7a471c85c..d221f93d0226 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -16,9 +16,8 @@ How do I get started? What are Django's prerequisites? -------------------------------- -Django requires Python, specifically Python 2.6.5 - 2.7.x. No other Python -libraries are required for basic Django usage. Django 1.5 also has -experimental support for Python 3.2.3 and above. +Django requires Python, specifically Python 2.6.5 - 2.7.x, or 3.2.3 and above. +No other Python libraries are required for basic Django usage. For a development environment -- if you just want to experiment with Django -- you don't need to have a separate Web server installed; Django comes with its @@ -43,7 +42,7 @@ Do I lose anything by using Python 2.6 versus newer Python versions, such as Pyt ---------------------------------------------------------------------------------------- Not in the core framework. Currently, Django itself officially supports -Python 2.6 (2.6.5 or higher) and 2.7. However, newer versions of +Python 2.6 (2.6.5 or higher), 2.7, 3.2.3 or higher. However, newer versions of Python are often faster, have more features, and are better supported. If you use a newer version of Python you will also have access to some APIs that aren't available under older versions of Python. @@ -51,12 +50,9 @@ aren't available under older versions of Python. Third-party applications for use with Django are, of course, free to set their own version requirements. -All else being equal, we recommend that you use the latest 2.x release -(currently Python 2.7). This will let you take advantage of the numerous -improvements and optimizations to the Python language since version 2.6. - -Generally speaking, we don't recommend running Django on Python 3 yet; see -below for more. +All else being equal, we recommend that you use the latest 2.7 or 3.x release. +This will let you take advantage of the numerous improvements and optimizations +to the Python language since version 2.6. What Python version can I use with Django? ------------------------------------------ From e628753e7dd1611fb4cf770a78126e96a02ab1d7 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Fri, 28 Jun 2013 15:33:20 -0400 Subject: [PATCH 004/944] [1.6.x] Added missing deprecation note for model permission methods. refs #20642. Backport of e1dd24d2f from master. --- docs/internals/deprecation.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 9672746717c5..5513c7996608 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -404,6 +404,9 @@ these changes. forms for ManyToMany model fields will not be performed by Django anymore either at the model or forms layer. +* The ``Model._meta.get_(add|change|delete)_permission`` methods will + be removed. + 2.0 --- From 7bd9c32f14a0a56089358f7927a3326e78bee093 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sat, 29 Jun 2013 10:50:04 +0200 Subject: [PATCH 005/944] [1.6.x] Fixed 1.6 release notes. Backport of adc6f38867dd5b57c9e1c50395eec01f6bdd651f from master. --- docs/releases/1.6.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 1fd98e127175..4219edb1a0cc 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -41,9 +41,9 @@ Simplified default project and app templates The default templates used by :djadmin:`startproject` and :djadmin:`startapp` have been simplified and modernized. The :doc:`admin ` is now enabled by default in new projects; the -:doc:`sites ` framework no longer is. :ref:`Language -detection ` and :ref:`clickjacking -prevention ` are turned on. +:doc:`sites ` framework no longer is. :ref:`clickjacking +prevention ` is now on and the database defaults to +SQLite. If the default templates don't suit your tastes, you can use :ref:`custom project and app templates `. From 6908b6593949a205d05da342060a9d952bd7b77c Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 29 Jun 2013 11:40:31 +0200 Subject: [PATCH 006/944] [1.6.x] Removed obsolete comment. Refs #20079. Thanks Gavin Wahl. --- django/contrib/auth/models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 5fec99a783cc..406852a41646 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -14,7 +14,6 @@ from django.utils import timezone from django.contrib import auth -# UNUSABLE_PASSWORD is still imported here for backwards compatibility from django.contrib.auth.hashers import ( check_password, make_password, is_password_usable) from django.contrib.auth.signals import user_logged_in From c55cb6fa4524884c8b881b255ed0bb20f5e0b26a Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sat, 29 Jun 2013 16:28:05 +0200 Subject: [PATCH 007/944] [1.6.x] Removed comment from setup.cfg which broke newer wheel versions. Backport of b5f709e6f4c67020bedb141b9b18c5cd1e05f829 from master. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 330eff69770c..4c25b840b5b3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,4 +6,4 @@ install-script = scripts/rpm-install.sh license-file = LICENSE [wheel] -universal = 1 # use py2.py3 tag for pure-python dist +universal = 1 From b6aed803b20cc7898a82fd65845e97676276f3fa Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 27 Jun 2013 10:58:05 +0200 Subject: [PATCH 008/944] [1.6.x] Fixed #20660 -- Do not try to delete an unset FieldFile Thanks stanislas.guerra at gmail.com for the report and Baptiste Mispelon for the review. Backport of ea3fe78a9d from master. --- django/db/models/fields/files.py | 2 ++ tests/model_fields/tests.py | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py index e631f177e92c..3b3c1ec2b658 100644 --- a/django/db/models/fields/files.py +++ b/django/db/models/fields/files.py @@ -96,6 +96,8 @@ def save(self, name, content, save=True): save.alters_data = True def delete(self, save=True): + if not self: + return # Only close the file if it's already open, which we know by the # presence of self._file if hasattr(self, '_file'): diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index ccff8b8cfa9a..6abeed8c4290 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -432,6 +432,17 @@ def test_changed(self): field.save_form_data(d, 'else.txt') self.assertEqual(d.myfile, 'else.txt') + def test_delete_when_file_unset(self): + """ + Calling delete on an unset FileField should not call the file deletion + process, but fail silently (#20660). + """ + d = Document() + try: + d.myfile.delete() + except OSError: + self.fail("Deleting an unset FileField should not raise OSError.") + class BinaryFieldTests(test.TestCase): binary_data = b'\x00\x46\xFE' From a9b5a1e506a9e8492407399b8bec1c2a8420be60 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 27 Jun 2013 10:59:30 +0200 Subject: [PATCH 009/944] [1.6.x] Do not allow FileSystemStorage.delete to receive an empty name Refs #20660. Backport of 7fbab3eba from master. --- django/core/files/storage.py | 1 + tests/file_storage/tests.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/django/core/files/storage.py b/django/core/files/storage.py index 977b6a68a8b5..5d301a317c5a 100644 --- a/django/core/files/storage.py +++ b/django/core/files/storage.py @@ -231,6 +231,7 @@ def _save(self, name, content): return name def delete(self, name): + assert name, "The name argument is not allowed to be empty." name = self.path(name) # If the file exists, delete it from the filesystem. # Note that there is a race between os.path.exists and os.remove: diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index 6c3c559660bd..e6caabf9d996 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -364,6 +364,14 @@ def failing_chunks(): with self.assertRaises(IOError): self.storage.save('error.file', f1) + def test_delete_no_name(self): + """ + Calling delete with an empty name should not try to remove the base + storage directory, but fail loudly (#20660). + """ + with self.assertRaises(AssertionError): + self.storage.delete('') + class CustomStorage(FileSystemStorage): def get_available_name(self, name): From 59be2c6875eaf744798d14c9dc8498552c8d48d5 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 29 Jun 2013 18:44:41 +0200 Subject: [PATCH 010/944] [1.6.x] Fixed #18592 -- Prevented crash when accessing MySQL _last_executed Thanks reames at asymmetricventures.com for the report. Backport of 59b0c48ce from master. --- django/db/backends/mysql/base.py | 2 +- tests/backends/tests.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index ea687715e4e0..d10be94f4396 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -284,7 +284,7 @@ def last_executed_query(self, cursor, sql, params): # With MySQLdb, cursor objects have an (undocumented) "_last_executed" # attribute where the exact query sent to the database is saved. # See MySQLdb/cursors.py in the source distribution. - return force_text(cursor._last_executed, errors='replace') + return force_text(getattr(cursor, '_last_executed', None), errors='replace') def no_limit_value(self): # 2**64 - 1, as recommended by the MySQL documentation diff --git a/tests/backends/tests.py b/tests/backends/tests.py index c1a26df7fcfa..488f8d518b01 100644 --- a/tests/backends/tests.py +++ b/tests/backends/tests.py @@ -164,6 +164,17 @@ def test_django_date_extract(self): @override_settings(DEBUG=True) class LastExecutedQueryTest(TestCase): + def test_last_executed_query(self): + """ + last_executed_query should not raise an exception even if no previous + query has been run. + """ + cursor = connection.cursor() + try: + connection.ops.last_executed_query(cursor, '', ()) + except Exception: + self.fail("'last_executed_query' should not raise an exception.") + def test_debug_sql(self): list(models.Reporter.objects.filter(first_name="test")) sql = connection.queries[-1]['sql'].lower() From b930733a67cbc1235daf675de06d73e3b6406113 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sat, 29 Jun 2013 14:14:32 -0400 Subject: [PATCH 011/944] [1.6.x] Fixed #20677 - Typos in generic_inlineformset_factory docs. Thanks Riley Strong for the report. Backport of 3fd0ee5b46 from master --- django/contrib/contenttypes/generic.py | 4 ++-- docs/ref/contrib/contenttypes.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index 26db4ab17110..e14aa5ecee92 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -434,8 +434,8 @@ def generic_inlineformset_factory(model, form=ModelForm, """ Returns a ``GenericInlineFormSet`` for the given kwargs. - You must provide ``ct_field`` and ``object_id`` if they different from the - defaults ``content_type`` and ``object_id`` respectively. + You must provide ``ct_field`` and ``fk_field`` if they are different from + the defaults ``content_type`` and ``object_id`` respectively. """ opts = model._meta # if there is no field called `ct_field` let the exception propagate diff --git a/docs/ref/contrib/contenttypes.txt b/docs/ref/contrib/contenttypes.txt index de9c5dcbd6bf..199401c64aa1 100644 --- a/docs/ref/contrib/contenttypes.txt +++ b/docs/ref/contrib/contenttypes.txt @@ -506,9 +506,9 @@ information. Returns a ``GenericInlineFormSet`` using :func:`~django.forms.models.modelformset_factory`. - You must provide ``ct_field`` and ``object_id`` if they different from the - defaults, ``content_type`` and ``object_id`` respectively. Other parameters - are similar to those documented in + You must provide ``ct_field`` and ``fk_field`` if they are different from + the defaults, ``content_type`` and ``object_id`` respectively. Other + parameters are similar to those documented in :func:`~django.forms.models.modelformset_factory` and :func:`~django.forms.models.inlineformset_factory`. From d2550042894726c5b74a9a2374304b75a4ac30bc Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 30 Jun 2013 14:17:33 +0200 Subject: [PATCH 012/944] [1.6.x] Stopped calling loaddata with commit=False. This was a stealth option only used by the tests, and it isn't useful any more since `atomic` provides nested transactions. Backport of 2c40681 from master. --- tests/fixtures/tests.py | 56 +++++++++++++-------------- tests/fixtures_model_package/tests.py | 7 ++-- tests/fixtures_regress/tests.py | 32 --------------- tests/proxy_models/tests.py | 2 +- 4 files changed, 31 insertions(+), 66 deletions(-) diff --git a/tests/fixtures/tests.py b/tests/fixtures/tests.py index d0942afdb708..6f2218b19ec5 100644 --- a/tests/fixtures/tests.py +++ b/tests/fixtures/tests.py @@ -56,7 +56,7 @@ def test_initial_data(self): def test_loading_and_dumping(self): Site.objects.all().delete() # Load fixture 1. Single JSON file, with two objects. - management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1.json', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -87,7 +87,7 @@ def test_loading_and_dumping(self): self._dumpdata_assert(['fixtures.Category', 'sites'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]') # Load fixture 2. JSON file imported by default. Overwrites some existing objects - management.call_command('loaddata', 'fixture2.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture2.json', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -95,7 +95,7 @@ def test_loading_and_dumping(self): ]) # Load fixture 3, XML format. - management.call_command('loaddata', 'fixture3.xml', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture3.xml', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -104,14 +104,14 @@ def test_loading_and_dumping(self): ]) # Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne. - management.call_command('loaddata', 'fixture6.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture6.json', verbosity=0) self.assertQuerysetEqual(Tag.objects.all(), [ ' tagged "copyright">', ' tagged "law">', ], ordered=False) # Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne. - management.call_command('loaddata', 'fixture7.xml', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture7.xml', verbosity=0) self.assertQuerysetEqual(Tag.objects.all(), [ ' tagged "copyright">', ' tagged "legal">', @@ -120,7 +120,7 @@ def test_loading_and_dumping(self): ], ordered=False) # Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany. - management.call_command('loaddata', 'fixture8.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture8.json', verbosity=0) self.assertQuerysetEqual(Visa.objects.all(), [ '', '', @@ -128,7 +128,7 @@ def test_loading_and_dumping(self): ], ordered=False) # Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany. - management.call_command('loaddata', 'fixture9.xml', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture9.xml', verbosity=0) self.assertQuerysetEqual(Visa.objects.all(), [ '', '', @@ -143,15 +143,13 @@ def test_loading_and_dumping(self): # Loading a fixture that doesn't exist emits a warning with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") - management.call_command('loaddata', 'unknown.json', verbosity=0, - commit=False) + management.call_command('loaddata', 'unknown.json', verbosity=0) self.assertEqual(len(w), 1) self.assertTrue(w[0].message, "No fixture named 'unknown' found.") # An attempt to load a nonexistent 'initial_data' fixture isn't an error with warnings.catch_warnings(record=True) as w: - management.call_command('loaddata', 'initial_data.json', verbosity=0, - commit=False) + management.call_command('loaddata', 'initial_data.json', verbosity=0) self.assertEqual(len(w), 0) # object list is unaffected @@ -178,7 +176,7 @@ def test_loading_and_dumping(self): def test_dumpdata_with_excludes(self): # Load fixture1 which has a site, two articles, and a category Site.objects.all().delete() - management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1.json', verbosity=0) # Excluding fixtures app should only leave sites self._dumpdata_assert( @@ -226,8 +224,8 @@ def test_dumpdata_with_filtering_manager(self): self._dumpdata_assert(['fixtures.Spy'], '[{"pk": %d, "model": "fixtures.spy", "fields": {"cover_blown": true}}, {"pk": %d, "model": "fixtures.spy", "fields": {"cover_blown": false}}]' % (spy2.pk, spy1.pk), use_base_manager=True) def test_dumpdata_with_pks(self): - management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False) - management.call_command('loaddata', 'fixture2.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1.json', verbosity=0) + management.call_command('loaddata', 'fixture2.json', verbosity=0) self._dumpdata_assert( ['fixtures.Article'], '[{"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16T14:00:00"}}]', @@ -266,21 +264,21 @@ def test_dumpdata_with_pks(self): def test_compress_format_loading(self): # Load fixture 4 (compressed), using format specification - management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture4.json', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', ]) def test_compressed_specified_loading(self): # Load fixture 5 (compressed), using format *and* compression specification - management.call_command('loaddata', 'fixture5.json.zip', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture5.json.zip', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', ]) def test_compressed_loading(self): # Load fixture 5 (compressed), only compression specification - management.call_command('loaddata', 'fixture5.zip', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture5.zip', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', ]) @@ -288,13 +286,13 @@ def test_compressed_loading(self): def test_ambiguous_compressed_fixture(self): # The name "fixture5" is ambigous, so loading it will raise an error with self.assertRaises(management.CommandError) as cm: - management.call_command('loaddata', 'fixture5', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture5', verbosity=0) self.assertIn("Multiple fixtures named 'fixture5'", cm.exception.args[0]) def test_db_loading(self): # Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly - management.call_command('loaddata', 'db_fixture_1', verbosity=0, commit=False) - management.call_command('loaddata', 'db_fixture_2', verbosity=0, commit=False) + management.call_command('loaddata', 'db_fixture_1', verbosity=0) + management.call_command('loaddata', 'db_fixture_2', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -312,13 +310,13 @@ def test_loaddata_error_message(self): if connection.vendor == 'mysql': connection.cursor().execute("SET sql_mode = 'TRADITIONAL'") with self.assertRaises(IntegrityError) as cm: - management.call_command('loaddata', 'invalid.json', verbosity=0, commit=False) + management.call_command('loaddata', 'invalid.json', verbosity=0) self.assertIn("Could not load fixtures.Article(pk=1):", cm.exception.args[0]) def test_loading_using(self): # Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly - management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default', commit=False) - management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default', commit=False) + management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default') + management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default') self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -327,18 +325,18 @@ def test_loading_using(self): def test_unmatched_identifier_loading(self): # Try to load db fixture 3. This won't load because the database identifier doesn't match with warnings.catch_warnings(record=True): - management.call_command('loaddata', 'db_fixture_3', verbosity=0, commit=False) + management.call_command('loaddata', 'db_fixture_3', verbosity=0) with warnings.catch_warnings(record=True): - management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default', commit=False) + management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default') self.assertQuerysetEqual(Article.objects.all(), []) def test_output_formats(self): # Load back in fixture 1, we need the articles from it - management.call_command('loaddata', 'fixture1', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1', verbosity=0) # Try to load fixture 6 using format discovery - management.call_command('loaddata', 'fixture6', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture6', verbosity=0) self.assertQuerysetEqual(Tag.objects.all(), [ ' tagged "copyright">', ' tagged "law">' @@ -364,7 +362,7 @@ class FixtureTransactionTests(DumpDataAssertMixin, TransactionTestCase): @skipUnlessDBFeature('supports_forward_references') def test_format_discovery(self): # Load fixture 1 again, using format discovery - management.call_command('loaddata', 'fixture1', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture1', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', @@ -386,7 +384,7 @@ def test_format_discovery(self): self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16T12:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16T13:00:00"}}, {"pk": 10, "model": "fixtures.book", "fields": {"name": "Achieving self-awareness of Python programs", "authors": []}}]') # Load fixture 4 (compressed), using format discovery - management.call_command('loaddata', 'fixture4', verbosity=0, commit=False) + management.call_command('loaddata', 'fixture4', verbosity=0) self.assertQuerysetEqual(Article.objects.all(), [ '', '', diff --git a/tests/fixtures_model_package/tests.py b/tests/fixtures_model_package/tests.py index dbcc721d8fce..4b00e6dfb9aa 100644 --- a/tests/fixtures_model_package/tests.py +++ b/tests/fixtures_model_package/tests.py @@ -60,7 +60,6 @@ def test_flush(self): 'flush', verbosity=0, interactive=False, - commit=False, load_initial_data=False ) self.assertQuerysetEqual(Book.objects.all(), []) @@ -83,7 +82,7 @@ def test_initial_data(self): def test_loaddata(self): "Fixtures can load data into models defined in packages" # Load fixture 1. Single JSON file, with two objects - management.call_command("loaddata", "fixture1.json", verbosity=0, commit=False) + management.call_command("loaddata", "fixture1.json", verbosity=0) self.assertQuerysetEqual( Article.objects.all(), [ "Time to reform copyright", @@ -94,7 +93,7 @@ def test_loaddata(self): # Load fixture 2. JSON file imported by default. Overwrites some # existing objects - management.call_command("loaddata", "fixture2.json", verbosity=0, commit=False) + management.call_command("loaddata", "fixture2.json", verbosity=0) self.assertQuerysetEqual( Article.objects.all(), [ "Django conquers world!", @@ -107,7 +106,7 @@ def test_loaddata(self): # Load a fixture that doesn't exist with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") - management.call_command("loaddata", "unknown.json", verbosity=0, commit=False) + management.call_command("loaddata", "unknown.json", verbosity=0) self.assertEqual(len(w), 1) self.assertTrue(w[0].message, "No fixture named 'unknown' found.") diff --git a/tests/fixtures_regress/tests.py b/tests/fixtures_regress/tests.py index 0f6ac659765a..a9d67ec9a239 100644 --- a/tests/fixtures_regress/tests.py +++ b/tests/fixtures_regress/tests.py @@ -45,7 +45,6 @@ def test_duplicate_pk(self): 'loaddata', 'sequence', verbosity=0, - commit=False ) # Create a new animal. Without a sequence reset, this new object @@ -84,7 +83,6 @@ def test_loaddata_not_found_fields_ignore(self): 'sequence_extra', ignore=True, verbosity=0, - commit=False ) self.assertEqual(Animal.specimens.all()[0].name, 'Lion') @@ -98,7 +96,6 @@ def test_loaddata_not_found_fields_ignore_xml(self): 'sequence_extra_xml', ignore=True, verbosity=0, - commit=False ) self.assertEqual(Animal.specimens.all()[0].name, 'Wolf') @@ -113,7 +110,6 @@ def test_pretty_print_xml(self): 'loaddata', 'pretty.xml', verbosity=0, - commit=False ) self.assertEqual(Stuff.objects.all()[0].name, None) self.assertEqual(Stuff.objects.all()[0].owner, None) @@ -129,7 +125,6 @@ def test_pretty_print_xml_empty_strings(self): 'loaddata', 'pretty.xml', verbosity=0, - commit=False ) self.assertEqual(Stuff.objects.all()[0].name, '') self.assertEqual(Stuff.objects.all()[0].owner, None) @@ -152,7 +147,6 @@ def test_absolute_path(self): 'loaddata', load_absolute_path, verbosity=0, - commit=False ) self.assertEqual(Absolute.load_count, 1) @@ -168,7 +162,6 @@ def test_unknown_format(self): 'loaddata', 'bad_fixture1.unkn', verbosity=0, - commit=False, ) @override_settings(SERIALIZATION_MODULES={'unkn': 'unexistent.path'}) @@ -182,7 +175,6 @@ def test_unimportable_serializer(self): 'loaddata', 'bad_fixture1.unkn', verbosity=0, - commit=False, ) def test_invalid_data(self): @@ -197,7 +189,6 @@ def test_invalid_data(self): 'loaddata', 'bad_fixture2.xml', verbosity=0, - commit=False, ) def test_invalid_data_no_ext(self): @@ -212,7 +203,6 @@ def test_invalid_data_no_ext(self): 'loaddata', 'bad_fixture2', verbosity=0, - commit=False, ) def test_empty(self): @@ -226,7 +216,6 @@ def test_empty(self): 'loaddata', 'empty', verbosity=0, - commit=False, ) def test_error_message(self): @@ -240,7 +229,6 @@ def test_error_message(self): 'bad_fixture2', 'animal', verbosity=0, - commit=False, ) def test_pg_sequence_resetting_checks(self): @@ -253,7 +241,6 @@ def test_pg_sequence_resetting_checks(self): 'loaddata', 'model-inheritance.json', verbosity=0, - commit=False ) self.assertEqual(Parent.objects.all()[0].id, 1) self.assertEqual(Child.objects.all()[0].id, 1) @@ -270,7 +257,6 @@ def test_close_connection_after_loaddata(self): 'loaddata', 'big-fixture.json', verbosity=0, - commit=False ) articles = Article.objects.exclude(id=9) self.assertEqual( @@ -297,7 +283,6 @@ def test_field_value_coerce(self): 'loaddata', 'animal.xml', verbosity=0, - commit=False, ) self.assertEqual( self.pre_save_checks, @@ -319,13 +304,11 @@ def test_dumpdata_uses_default_manager(self): 'loaddata', 'animal.xml', verbosity=0, - commit=False, ) management.call_command( 'loaddata', 'sequence.json', verbosity=0, - commit=False, ) animal = Animal( name='Platypus', @@ -390,7 +373,6 @@ def test_loaddata_works_when_fixture_has_forward_refs(self): 'loaddata', 'forward_ref.json', verbosity=0, - commit=False ) self.assertEqual(Book.objects.all()[0].id, 1) self.assertEqual(Person.objects.all()[0].id, 4) @@ -405,7 +387,6 @@ def test_loaddata_raises_error_when_fixture_has_invalid_foreign_key(self): 'loaddata', 'forward_ref_bad_data.json', verbosity=0, - commit=False, ) _cur_dir = os.path.dirname(os.path.abspath(upath(__file__))) @@ -422,7 +403,6 @@ def test_loaddata_forward_refs_split_fixtures(self): 'forward_ref_1.json', 'forward_ref_2.json', verbosity=0, - commit=False ) self.assertEqual(Book.objects.all()[0].id, 1) self.assertEqual(Person.objects.all()[0].id, 4) @@ -437,7 +417,6 @@ def test_loaddata_no_fixture_specified(self): management.call_command( 'loaddata', verbosity=0, - commit=False, ) def test_loaddata_not_existant_fixture_file(self): @@ -447,7 +426,6 @@ def test_loaddata_not_existant_fixture_file(self): 'loaddata', 'this_fixture_doesnt_exist', verbosity=2, - commit=False, stdout=stdout_output, ) self.assertTrue("No fixture 'this_fixture_doesnt_exist' in" in @@ -465,13 +443,11 @@ def test_nk_deserialize(self): 'loaddata', 'model-inheritance.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'nk-inheritance.json', verbosity=0, - commit=False ) self.assertEqual( NKChild.objects.get(pk=1).data, @@ -492,19 +468,16 @@ def test_nk_deserialize_xml(self): 'loaddata', 'model-inheritance.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'nk-inheritance.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'nk-inheritance2.xml', verbosity=0, - commit=False ) self.assertEqual( NKChild.objects.get(pk=2).data, @@ -524,7 +497,6 @@ def test_nk_on_serialize(self): 'loaddata', 'forward_ref_lookup.json', verbosity=0, - commit=False ) stdout = StringIO() @@ -662,19 +634,16 @@ def test_normal_pk(self): 'loaddata', 'non_natural_1.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'forward_ref_lookup.json', verbosity=0, - commit=False ) management.call_command( 'loaddata', 'non_natural_2.xml', verbosity=0, - commit=False ) books = Book.objects.all() self.assertEqual( @@ -696,7 +665,6 @@ def ticket_11101(self): 'loaddata', 'thingy.json', verbosity=0, - commit=False ) self.assertEqual(Thingy.objects.count(), 1) transaction.rollback() diff --git a/tests/proxy_models/tests.py b/tests/proxy_models/tests.py index 77d2ba9a7429..5cc5ef54789e 100644 --- a/tests/proxy_models/tests.py +++ b/tests/proxy_models/tests.py @@ -358,7 +358,7 @@ def test_proxy_bug(self): ) def test_proxy_load_from_fixture(self): - management.call_command('loaddata', 'mypeople.json', verbosity=0, commit=False) + management.call_command('loaddata', 'mypeople.json', verbosity=0) p = MyPerson.objects.get(pk=100) self.assertEqual(p.name, 'Elvis Presley') From 02976a46c926880e78eb64424a9c8aa28d8be48a Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 30 Jun 2013 15:57:00 +0200 Subject: [PATCH 013/944] [1.6.x] Introduced getters for connection.autocommit and .needs_rollback. They ensure that the attributes aren't accessed in conditions where they don't contain a valid value. Fixed #20666. Backport of dd9c6bc359a799fcbed647055b596239956a472a from master. --- django/db/backends/__init__.py | 34 +++++++++++++++++++++++----------- django/db/transaction.py | 12 ++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 9abb9a9637e7..a1fd923faf7e 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -204,7 +204,7 @@ def _savepoint_commit(self, sid): def _savepoint_allowed(self): # Savepoints cannot be created outside a transaction - return self.features.uses_savepoints and not self.autocommit + return self.features.uses_savepoints and not self.get_autocommit() ##### Generic savepoint management methods ##### @@ -279,15 +279,13 @@ def enter_transaction_management(self, managed=True, forced=False): """ self.validate_no_atomic_block() - self.ensure_connection() - self.transaction_state.append(managed) if not managed and self.is_dirty() and not forced: self.commit() self.set_clean() - if managed == self.autocommit: + if managed == self.get_autocommit(): self.set_autocommit(not managed) def leave_transaction_management(self): @@ -298,8 +296,6 @@ def leave_transaction_management(self): """ self.validate_no_atomic_block() - self.ensure_connection() - if self.transaction_state: del self.transaction_state[-1] else: @@ -313,14 +309,21 @@ def leave_transaction_management(self): if self._dirty: self.rollback() - if managed == self.autocommit: + if managed == self.get_autocommit(): self.set_autocommit(not managed) raise TransactionManagementError( "Transaction managed block ended with pending COMMIT/ROLLBACK") - if managed == self.autocommit: + if managed == self.get_autocommit(): self.set_autocommit(not managed) + def get_autocommit(self): + """ + Check the autocommit state. + """ + self.ensure_connection() + return self.autocommit + def set_autocommit(self, autocommit): """ Enable or disable autocommit. @@ -330,13 +333,22 @@ def set_autocommit(self, autocommit): self._set_autocommit(autocommit) self.autocommit = autocommit + def get_rollback(self): + """ + Get the "needs rollback" flag -- for *advanced use* only. + """ + if not self.in_atomic_block: + raise TransactionManagementError( + "The rollback flag doesn't work outside of an 'atomic' block.") + return self.needs_rollback + def set_rollback(self, rollback): """ Set or unset the "needs rollback" flag -- for *advanced use* only. """ if not self.in_atomic_block: raise TransactionManagementError( - "needs_rollback doesn't work outside of an 'atomic' block.") + "The rollback flag doesn't work outside of an 'atomic' block.") self.needs_rollback = rollback def validate_no_atomic_block(self): @@ -370,7 +382,7 @@ def set_dirty(self): to decide in a managed block of code to decide whether there are open changes waiting for commit. """ - if not self.autocommit: + if not self.get_autocommit(): self._dirty = True def set_clean(self): @@ -436,7 +448,7 @@ def close_if_unusable_or_obsolete(self): if self.connection is not None: # If the application didn't restore the original autocommit setting, # don't take chances, drop the connection. - if self.autocommit != self.settings_dict['AUTOCOMMIT']: + if self.get_autocommit() != self.settings_dict['AUTOCOMMIT']: self.close() return diff --git a/django/db/transaction.py b/django/db/transaction.py index 95b9ae165e29..5f60e6807f8c 100644 --- a/django/db/transaction.py +++ b/django/db/transaction.py @@ -123,7 +123,7 @@ def get_autocommit(using=None): """ Get the autocommit status of the connection. """ - return get_connection(using).autocommit + return get_connection(using).get_autocommit() def set_autocommit(autocommit, using=None): """ @@ -175,7 +175,7 @@ def get_rollback(using=None): """ Gets the "needs rollback" flag -- for *advanced use* only. """ - return get_connection(using).needs_rollback + return get_connection(using).get_rollback() def set_rollback(rollback, using=None): """ @@ -229,15 +229,11 @@ def __init__(self, using, savepoint): def __enter__(self): connection = get_connection(self.using) - # Ensure we have a connection to the database before testing - # autocommit status. - connection.ensure_connection() - if not connection.in_atomic_block: # Reset state when entering an outermost atomic block. connection.commit_on_exit = True connection.needs_rollback = False - if not connection.autocommit: + if not connection.get_autocommit(): # Some database adapters (namely sqlite3) don't handle # transactions and savepoints properly when autocommit is off. # Turning autocommit back on isn't an option; it would trigger @@ -500,7 +496,7 @@ def commit_on_success_unless_managed(using=None, savepoint=False): legacy behavior. """ connection = get_connection(using) - if connection.autocommit or connection.in_atomic_block: + if connection.get_autocommit() or connection.in_atomic_block: return atomic(using, savepoint) else: def entering(using): From 3493f18d7850236dcb6292ebb1b949d6aeed7a9c Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sun, 30 Jun 2013 14:48:15 -0400 Subject: [PATCH 014/944] [1.6.x] Fixed #20667 - Removed discussion of DEBUG from tutorial. Backport of 0d642aac86 from master. --- docs/howto/deployment/checklist.txt | 15 ++++++++++ docs/intro/tutorial03.txt | 45 ----------------------------- docs/intro/whatsnext.txt | 5 ++++ docs/topics/http/views.txt | 14 ++++----- 4 files changed, 27 insertions(+), 52 deletions(-) diff --git a/docs/howto/deployment/checklist.txt b/docs/howto/deployment/checklist.txt index 1a235673e869..4ec0c266f898 100644 --- a/docs/howto/deployment/checklist.txt +++ b/docs/howto/deployment/checklist.txt @@ -206,6 +206,21 @@ See :doc:`/howto/error-reporting` for details on error reporting by email. .. _Sentry: http://sentry.readthedocs.org/en/latest/ +Customize the default error views +--------------------------------- + +Django includes default views and templates for several HTTP error codes. You +may want to override the default templates by creating the following templates +in your root template directory: ``404.html``, ``500.html``, ``403.html``, and +``400.html``. The default views should suffice for 99% of Web applications, but +if you desire to customize them, see these instructions which also contain +details about the default templates: + +* :ref:`http_not_found_view` +* :ref:`http_internal_server_error_view` +* :ref:`http_forbidden_view` +* :ref:`http_bad_request_view` + Miscellaneous ============= diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index 91409848cf24..777b8ef23bee 100644 --- a/docs/intro/tutorial03.txt +++ b/docs/intro/tutorial03.txt @@ -454,51 +454,6 @@ just as :func:`~django.shortcuts.get_object_or_404` -- except using :meth:`~django.db.models.query.QuerySet.get`. It raises :exc:`~django.http.Http404` if the list is empty. -Write a 404 (page not found) view -================================= - -When you raise :exc:`~django.http.Http404` from within a view, Django -will load a special view devoted to handling 404 errors. It finds it -by looking for the variable ``handler404`` in your root URLconf (and -only in your root URLconf; setting ``handler404`` anywhere else will -have no effect), which is a string in Python dotted syntax -- the same -format the normal URLconf callbacks use. A 404 view itself has nothing -special: It's just a normal view. - -You normally won't have to bother with writing 404 views. If you don't set -``handler404``, the built-in view :func:`django.views.defaults.page_not_found` -is used by default. Optionally, you can create a ``404.html`` template -in the root of your template directory. The default 404 view will then use that -template for all 404 errors when :setting:`DEBUG` is set to ``False`` (in your -settings module). If you do create the template, add at least some dummy -content like "Page not found". - -.. warning:: - - If :setting:`DEBUG` is set to ``False``, all responses will be - "Bad Request (400)" unless you specify the proper :setting:`ALLOWED_HOSTS` - as well (something like ``['localhost', '127.0.0.1']`` for - local development). - -A couple more things to note about 404 views: - -* If :setting:`DEBUG` is set to ``True`` (in your settings module) then your - 404 view will never be used (and thus the ``404.html`` template will never - be rendered) because the traceback will be displayed instead. - -* The 404 view is also called if Django doesn't find a match after checking - every regular expression in the URLconf. - -Write a 500 (server error) view -=============================== - -Similarly, your root URLconf may define a ``handler500``, which points -to a view to call in case of server errors. Server errors happen when -you have runtime errors in view code. - -Likewise, you should create a ``500.html`` template at the root of your -template directory and add some content like "Something went wrong". - Use the template system ======================= diff --git a/docs/intro/whatsnext.txt b/docs/intro/whatsnext.txt index a677bc9efd17..638d219afe85 100644 --- a/docs/intro/whatsnext.txt +++ b/docs/intro/whatsnext.txt @@ -66,6 +66,11 @@ different needs: where you'll turn to find the details of a particular function or whathaveyou. +* If you are interested in deploying a project for public use, our docs have + :doc:`several guides` for various deployment + setups as well as a :doc:`deployment checklist` + for some things you'll need to think about. + * Finally, there's some "specialized" documentation not usually relevant to most developers. This includes the :doc:`release notes ` and :doc:`internals documentation ` for those who want to add diff --git a/docs/topics/http/views.txt b/docs/topics/http/views.txt index 5c27c9c95852..ebe5302ef8f1 100644 --- a/docs/topics/http/views.txt +++ b/docs/topics/http/views.txt @@ -140,18 +140,18 @@ The 404 (page not found) view .. function:: django.views.defaults.page_not_found(request, template_name='404.html') -When you raise an ``Http404`` exception, Django loads a special view devoted -to handling 404 errors. By default, it's the view -``django.views.defaults.page_not_found``, which either produces a very simple -"Not Found" message or loads and renders the template ``404.html`` if you -created it in your root template directory. +When you raise :exc:`~django.http.Http404` from within a view, Django loads a +special view devoted to handling 404 errors. By default, it's the view +:func:`django.views.defaults.page_not_found`, which either produces a very +simple "Not Found" message or loads and renders the template ``404.html`` if +you created it in your root template directory. The default 404 view will pass one variable to the template: ``request_path``, which is the URL that resulted in the error. The ``page_not_found`` view should suffice for 99% of Web applications, but if -you want to override it, you can specify ``handler404`` in your URLconf, like -so:: +you want to override it, you can specify ``handler404`` in your root URLconf +(setting ``handler404`` anywhere else will have no effect), like so:: handler404 = 'mysite.views.my_custom_404_view' From c1d8f3b2078db05bcc66b664c1d17b7799342bd5 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 1 Jul 2013 11:44:59 +0200 Subject: [PATCH 015/944] Updated FAQ on Python versions to explain 2 vs 3. Required the latest version for each Python series to minimize bookkeeping in the future. --- docs/faq/install.txt | 49 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/docs/faq/install.txt b/docs/faq/install.txt index d221f93d0226..2c7c35299bbf 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -38,22 +38,6 @@ PostgreSQL fans, and MySQL_, `SQLite 3`_, and Oracle_ are also supported. .. _`SQLite 3`: http://www.sqlite.org/ .. _Oracle: http://www.oracle.com/ -Do I lose anything by using Python 2.6 versus newer Python versions, such as Python 2.7? ----------------------------------------------------------------------------------------- - -Not in the core framework. Currently, Django itself officially supports -Python 2.6 (2.6.5 or higher), 2.7, 3.2.3 or higher. However, newer versions of -Python are often faster, have more features, and are better supported. If you -use a newer version of Python you will also have access to some APIs that -aren't available under older versions of Python. - -Third-party applications for use with Django are, of course, free to set their -own version requirements. - -All else being equal, we recommend that you use the latest 2.7 or 3.x release. -This will let you take advantage of the numerous improvements and optimizations -to the Python language since version 2.6. - What Python version can I use with Django? ------------------------------------------ @@ -65,20 +49,35 @@ Django version Python versions 1.2 2.4, 2.5, 2.6, 2.7 1.3 2.4, 2.5, 2.6, 2.7 1.4 2.5, 2.6, 2.7 -1.5 2.6.5, 2.7 and 3.2.3, 3.3 (experimental) -**1.6** **2.6.5, 2.7** and **3.2.3, 3.3** -*1.7 (future)* *2.7, 3.3 (to be confirmed)* +1.5 2.6, 2.7 and 3.2, 3.3 (experimental) +**1.6** **2.6, 2.7** and **3.2, 3.3** +*1.7 (future)* *2.7* and *3.2, 3.3* ============== =============== -Can I use Django with Python 3? -------------------------------- +For a given series of Python versions, only the latest release is officially +supported. For instance, at the time of writing (July 1st, 2013), the latest +release in the 2.7 series is 2.7.5. -Yes, you can! - -Django 1.5 introduced experimental support for Python 3.2.3 and above. +What Python version should I use with Django? +--------------------------------------------- As of Django 1.6, Python 3 support is considered stable and you can safely use -it in production. See also :doc:`/topics/python3`. +it in production. See also :doc:`/topics/python3`. However, the community is +still in the process of migrating third-party packages and applications to +Python 3. + +If you're starting a new project, and the dependencies you plan to use work on +Python 3, you should use Python 3. If they don't, consider contributing to the +porting efforts, or stick to Python 2. + +Since newer versions of Python are often faster, have more features, and are +better supported, all else being equal, we recommend that you use the latest +2.x.y or 3.x.y release. + +You don't lose anything in Django by using an older release, but you don't take +advantage of the improvements and optimizations in newer Python releases. +Third-party applications for use with Django are, of course, free to set their +own version requirements. Will Django run under shared hosting (like TextDrive or Dreamhost)? ------------------------------------------------------------------- From 0de21a6a7a6c651382bae50f13468316e40471b5 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 1 Jul 2013 11:52:00 +0200 Subject: [PATCH 016/944] Stopped branding Python 3 support as experimental. --- docs/intro/install.txt | 5 ++--- docs/topics/install.txt | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/intro/install.txt b/docs/intro/install.txt index 3cbc8d88ab26..40375e5eca3d 100644 --- a/docs/intro/install.txt +++ b/docs/intro/install.txt @@ -9,9 +9,8 @@ that'll work while you walk through the introduction. Install Python -------------- -Being a Python Web framework, Django requires Python. It works with any Python -version from 2.6.5 to 2.7. It also features experimental support for versions -3.2 and 3.3. All these versions of Python include a lightweight database +Being a Python Web framework, Django requires Python. It works with Python 2.6, +2.7, 3.2 or 3.3. All these versions of Python include a lightweight database called SQLite_ so you won't need to set up a database just yet. .. _sqlite: http://sqlite.org/ diff --git a/docs/topics/install.txt b/docs/topics/install.txt index 1c99dc5d5ad8..505d51a846db 100644 --- a/docs/topics/install.txt +++ b/docs/topics/install.txt @@ -7,10 +7,8 @@ This document will get you up and running with Django. Install Python ============== -Being a Python Web framework, Django requires Python. - -It works with any Python version from 2.6.5 to 2.7. It also features -experimental support for versions from 3.2.3 to 3.3. +Being a Python Web framework, Django requires Python. It works with Python 2.6, +2.7, 3.2 or 3.3. Get Python at http://www.python.org. If you're running Linux or Mac OS X, you probably already have it installed. From e03a88ba217006fbd7618e3f836c2f6210638aaf Mon Sep 17 00:00:00 2001 From: Baptiste Mispelon Date: Mon, 1 Jul 2013 14:05:49 +0200 Subject: [PATCH 017/944] [1.6.x] Fixed #20659 -- Fixed PublisherDetail in CBV topic documentation. Backport of 88de53d4a86548016f245a1413b856aa334bc737 from master. --- docs/topics/class-based-views/mixins.txt | 25 ++++++++++++------------ 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt index 84d741723369..df1a5505a5b3 100644 --- a/docs/topics/class-based-views/mixins.txt +++ b/docs/topics/class-based-views/mixins.txt @@ -286,18 +286,18 @@ One way to do this is to combine :class:`ListView` with for the paginated list of books can hang off the publisher found as the single object. In order to do this, we need to have two different querysets: -``Publisher`` queryset for use in - :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()` - We'll set the ``model`` attribute on the view and rely on the default - implementation of ``get_object()`` to fetch the correct ``Publisher`` - object. - ``Book`` queryset for use by :class:`~django.views.generic.list.ListView` - The default implementation of ``get_queryset()`` uses the ``model`` attribute - to construct the queryset. This conflicts with our use of this attribute - for ``get_object()`` so we'll override that method and have it return - the queryset of ``Book`` objects linked to the ``Publisher`` we're looking - at. + Since we have access to the ``Publisher`` whose books we want to list, we + simply override ``get_queryset()`` and use the ``Publisher``'s + :ref:`reverse foreign key manager`. + +``Publisher`` queryset for use in :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()` + We'll rely on the default implementation of ``get_object()`` to fetch the + correct ``Publisher`` object. + However, we need to explicitly pass a ``queryset`` argument because + otherwise the default implementation of ``get_object()`` would call + ``get_queryset()`` which we have overridden to return ``Book`` objects + instead of ``Publisher`` ones. .. note:: @@ -317,12 +317,11 @@ Now we can write a new ``PublisherDetail``:: from books.models import Publisher class PublisherDetail(SingleObjectMixin, ListView): - model = Publisher # for SingleObjectMixin.get_object paginate_by = 2 template_name = "books/publisher_detail.html" def get(self, request, *args, **kwargs): - self.object = self.get_object() + self.object = self.get_object(queryset=Publisher.objects.all()) return super(PublisherDetail, self).get(request, *args, **kwargs) def get_context_data(self, **kwargs): From 3c51962cabc9537221b86c667aac5ffaa1469660 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 1 Jul 2013 09:19:55 -0400 Subject: [PATCH 018/944] [1.6.x] Updated tests for deprecation of Option.get_(add|change|delete)_permission. refs #20642. Backport of a6a905c619 from master. --- django/contrib/admin/util.py | 3 ++- tests/admin_views/tests.py | 15 ++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py index a53cd367b462..dd9047c42824 100644 --- a/django/contrib/admin/util.py +++ b/django/contrib/admin/util.py @@ -3,6 +3,7 @@ import datetime import decimal +from django.contrib.auth import get_permission_codename from django.db import models from django.db.models.constants import LOOKUP_SEP from django.db.models.deletion import Collector @@ -119,7 +120,7 @@ def format_callback(obj): opts.model_name), None, (quote(obj._get_pk_val()),)) p = '%s.%s' % (opts.app_label, - opts.get_delete_permission()) + get_permission_codename('delete', opts)) if not user.has_perm(p): perms_needed.add(opts.verbose_name) # Display a link to the admin page. diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 235fe0cb54b9..1d63163771f5 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -15,6 +15,7 @@ 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 from django.contrib.admin.sites import LOGIN_FORM_KEY @@ -854,20 +855,20 @@ def setUp(self): # User who can add Articles add_user = User.objects.get(username='adduser') add_user.user_permissions.add(get_perm(Article, - opts.get_add_permission())) + get_permission_codename('add', opts))) # User who can change Articles change_user = User.objects.get(username='changeuser') change_user.user_permissions.add(get_perm(Article, - opts.get_change_permission())) + get_permission_codename('change', opts))) # User who can delete Articles delete_user = User.objects.get(username='deleteuser') delete_user.user_permissions.add(get_perm(Article, - opts.get_delete_permission())) + get_permission_codename('delete', opts))) delete_user.user_permissions.add(get_perm(Section, - Section._meta.get_delete_permission())) + get_permission_codename('delete', Section._meta))) # login POST dicts self.super_login = { @@ -1210,7 +1211,7 @@ def testConditionallyShowAddSectionLink(self): # Allow the add user to add sections too. Now they can see the "add # section" link. add_user = User.objects.get(username='adduser') - perm = get_perm(Section, Section._meta.get_add_permission()) + perm = get_perm(Section, get_permission_codename('add', Section._meta)) add_user.user_permissions.add(perm) response = self.client.get(url) self.assertContains(response, add_link_text) @@ -1315,7 +1316,7 @@ def setUp(self): # User who can change Reports change_user = User.objects.get(username='changeuser') change_user.user_permissions.add(get_perm(Report, - opts.get_change_permission())) + get_permission_codename('change', opts))) # login POST dict self.changeuser_login = { @@ -1372,7 +1373,7 @@ def test_perms_needed(self): self.client.logout() delete_user = User.objects.get(username='deleteuser') delete_user.user_permissions.add(get_perm(Plot, - Plot._meta.get_delete_permission())) + get_permission_codename('delete', Plot._meta))) self.assertTrue(self.client.login(username='deleteuser', password='secret')) From 4c1029971e810cde32ee4cd489627239b9b1b6ed Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 1 Jul 2013 09:36:31 -0400 Subject: [PATCH 019/944] [1.6.x] Fixed a couple form/formset deprecation warnings in tests. Backport of a521d10322 from master. --- tests/model_forms/tests.py | 2 ++ tests/model_formsets/tests.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 39be8247985f..19f0216ec73a 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -252,10 +252,12 @@ class Meta: fields = '__all__' widgets = {'status': forms.CheckboxSelectMultiple} + class CustomErrorMessageForm(forms.ModelForm): name1 = forms.CharField(error_messages={'invalid': 'Form custom error message.'}) class Meta: + fields = '__all__' model = CustomErrorMessage diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index 43509c471ff1..09a3ba177885 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -399,7 +399,7 @@ def __init__(self, *args, **kwargs): super(BaseAuthorFormSet, self).__init__(*args, **kwargs) self.queryset = Author.objects.filter(name__startswith='Charles') - AuthorFormSet = modelformset_factory(Author, formset=BaseAuthorFormSet) + AuthorFormSet = modelformset_factory(Author, fields='__all__', formset=BaseAuthorFormSet) formset = AuthorFormSet() self.assertEqual(len(formset.get_queryset()), 1) From 5ecdf0eb9ccd47c102deb873a242ed40d1cf45cd Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 2 Jul 2013 14:14:56 -0400 Subject: [PATCH 020/944] [1.6.x] A couple more semicolon -> colon fixes; refs #18134. Backport of 3632d289de from master. --- docs/ref/forms/api.txt | 2 +- docs/releases/1.6.txt | 8 ++++---- docs/topics/forms/index.txt | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt index 3c17827800bf..aa19719a68a5 100644 --- a/docs/ref/forms/api.txt +++ b/docs/ref/forms/api.txt @@ -661,7 +661,7 @@ additional attributes for the ``