From f71fdf83a8959f1b2131965cd893925d91ce81bc Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 18 Apr 2013 13:40:45 -0400 Subject: [PATCH 01/70] Fixed #20286 - Typo in static files docs. Thanks bmispelon. --- docs/howto/static-files/index.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/howto/static-files/index.txt b/docs/howto/static-files/index.txt index 2c98566e88..a26fc04cc9 100644 --- a/docs/howto/static-files/index.txt +++ b/docs/howto/static-files/index.txt @@ -32,7 +32,7 @@ Configuring static files {% load staticfiles %} My image -3. Store your static files in a folder called ``static`` in your app. For +4. Store your static files in a folder called ``static`` in your app. For example ``my_app/static/my_app/myimage.jpg``. Now, if you use ``./manage.py runserver``, all static files should be served From 9f7b277d2e14abd89972f7593aad3543a5028760 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 18 Apr 2013 13:44:20 -0400 Subject: [PATCH 02/70] Fixed #20285 - Added missing commas in translation docs. Thanks cody.j.b.scott@ --- docs/topics/i18n/translation.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 811425d229..ed0dd33a0f 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -1030,11 +1030,11 @@ prepend the current active language code to all url patterns defined within from django.conf.urls import patterns, include, url from django.conf.urls.i18n import i18n_patterns - urlpatterns = patterns('' + urlpatterns = patterns('', url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'), ) - news_patterns = patterns('' + news_patterns = patterns('', url(r'^$', 'news.views.index', name='index'), url(r'^category/(?P[\w-]+)/$', 'news.views.category', name='category'), url(r'^(?P[\w-]+)/$', 'news.views.details', name='detail'), From 5306285ce21d59742d67a447c9c1a1e4be6d86a4 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 18 Apr 2013 20:38:07 +0200 Subject: [PATCH 03/70] Complemented documentation following commit be9ae693c Refs #17840. Thanks Carl Meyer for noticing the omission. --- docs/ref/forms/fields.txt | 9 +++++++++ docs/releases/1.6.txt | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt index c8b8044d26..03898e60ea 100644 --- a/docs/ref/forms/fields.txt +++ b/docs/ref/forms/fields.txt @@ -467,6 +467,10 @@ For each field, we describe the default widget used if you don't specify The ``max_value`` and ``min_value`` error messages may contain ``%(limit_value)s``, which will be substituted by the appropriate limit. + .. versionchanged:: 1.6 + Similarly, the ``max_digits``, ``max_decimal_places`` and + ``max_whole_digits`` error messages may contain ``%(max)s``. + Takes four optional arguments: .. attribute:: max_value @@ -1013,6 +1017,11 @@ objects (in the case of ``ModelMultipleChoiceField``) into the The empty and normalized values were changed to be consistently ``QuerySets`` instead of ``[]`` and ``QuerySet`` respectively. + .. versionchanged:: 1.6 + The ``invalid_choice`` message may contain ``%(value)s`` and the + ``invalid_pk_value`` message may contain ``%(pk)s``, which will be + substituted by the appropriate values. + Allows the selection of one or more model objects, suitable for representing a many-to-many relation. As with :class:`ModelChoiceField`, you can use ``label_from_instance`` to customize the object diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 2f06756a1b..3e15f5b767 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -423,6 +423,13 @@ Miscellaneous ``type='email'``, ``type='url'`` or ``type='number'`` depending on their corresponding field type. +* Form field's :attr:`~django.forms.Field.error_messages` that contain a + placeholder should now always use a named placeholder (``"Value '%(value)s' is + too big"`` instead of ``"Value '%s' is too big"``). See the corresponding + field documentation for details about the names of the placeholders. The + changes in 1.6 particularly affect :class:`~django.forms.DecimalField` and + :class:`~django.forms.ModelMultipleChoiceField`. + Features deprecated in 1.6 ========================== From bfe25de42993fdbfccd4759f48ac6694e1ed32d9 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Fri, 19 Apr 2013 09:53:19 +0200 Subject: [PATCH 04/70] Explained the pattern for special methods compatibility. --- docs/topics/python3.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index 33f5fcd4c0..22e609c75c 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -317,6 +317,9 @@ Division def __idiv__(self, other): # Python 2 compatibility return type(self).__itruediv__(self, other) +Special methods are looked up on the class and not on the instance to reflect +the behavior of the Python interpreter. + .. module: django.utils.six Writing compatible code with six From 59d127e45f5ed31e346162a0ac312f672c096821 Mon Sep 17 00:00:00 2001 From: Anton Baklanov Date: Wed, 17 Apr 2013 18:20:31 +0300 Subject: [PATCH 05/70] Fixed #20276 -- Implemented __bool__ for MergeDict MergeDict evaluates now to False if all contained dicts are empty. Thanks til for the report and the initial patch. --- django/utils/datastructures.py | 6 ++++++ tests/utils_tests/test_datastructures.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 64c218fe43..b3060202be 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -14,6 +14,12 @@ class MergeDict(object): def __init__(self, *dicts): self.dicts = dicts + def __bool__(self): + return any(self.dicts) + + def __nonzero__(self): + return type(self).__bool__(self) + def __getitem__(self, key): for dict_ in self.dicts: try: diff --git a/tests/utils_tests/test_datastructures.py b/tests/utils_tests/test_datastructures.py index 3161c04f97..91111cc2ed 100644 --- a/tests/utils_tests/test_datastructures.py +++ b/tests/utils_tests/test_datastructures.py @@ -203,6 +203,13 @@ class MergeDictTests(SimpleTestCase): ('key2', ['value2', 'value3']), ('key4', ['value5', 'value6'])]) + def test_bool_casting(self): + empty = MergeDict({}, {}, {}) + not_empty = MergeDict({}, {}, {"key": "value"}) + self.assertFalse(empty) + self.assertTrue(not_empty) + + class MultiValueDictTests(SimpleTestCase): def test_multivaluedict(self): From 714161c8642646f1f617436479313ca49c71b5c8 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Fri, 19 Apr 2013 10:58:29 -0700 Subject: [PATCH 06/70] Fix != operations on lazy objects. --- django/utils/functional.py | 1 + tests/utils_tests/test_simplelazyobject.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/django/utils/functional.py b/django/utils/functional.py index 13aec9cb82..1592828c7f 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -346,6 +346,7 @@ class SimpleLazyObject(LazyObject): # care about this (especially in equality tests) __class__ = property(new_method_proxy(operator.attrgetter("__class__"))) __eq__ = new_method_proxy(operator.eq) + __ne__ = new_method_proxy(operator.ne) __hash__ = new_method_proxy(hash) __bool__ = new_method_proxy(bool) # Python 3 __nonzero__ = __bool__ # Python 2 diff --git a/tests/utils_tests/test_simplelazyobject.py b/tests/utils_tests/test_simplelazyobject.py index 883e60aa81..f925e01eb6 100644 --- a/tests/utils_tests/test_simplelazyobject.py +++ b/tests/utils_tests/test_simplelazyobject.py @@ -152,3 +152,12 @@ class TestUtilsSimpleLazyObject(TestCase): SimpleLazyObject(None) finally: sys.settrace(old_trace_func) + + def test_not_equal(self): + lazy1 = SimpleLazyObject(lambda: 2) + lazy2 = SimpleLazyObject(lambda: 2) + lazy3 = SimpleLazyObject(lambda: 3) + self.assertEqual(lazy1, lazy2) + self.assertNotEqual(lazy1, lazy3) + self.assertTrue(lazy1 != lazy3) + self.assertFalse(lazy1 != lazy2) From 3be8165b6267bea021df6e6c4e758a4c877c961e Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 20 Apr 2013 11:30:00 +0200 Subject: [PATCH 07/70] Updated tutorial INSTALLED_APPS section (removed contrib.sites) --- docs/intro/tutorial01.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index 7f69945300..55e85dcbe3 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -242,9 +242,6 @@ come with Django: * :mod:`django.contrib.sessions` -- A session framework. -* :mod:`django.contrib.sites` -- A framework for managing multiple sites - with one Django installation. - * :mod:`django.contrib.messages` -- A messaging framework. * :mod:`django.contrib.staticfiles` -- A framework for managing @@ -252,7 +249,7 @@ come with Django: These applications are included by default as a convenience for the common case. -Each of these applications makes use of at least one database table, though, +Some of these applications makes use of at least one database table, though, so we need to create the tables in the database before we can use them. To do that, run the following command: From 4e25198ec298732409217321be10e1e06be2fcbd Mon Sep 17 00:00:00 2001 From: Juan Catalano Date: Sun, 24 Mar 2013 22:48:23 -0700 Subject: [PATCH 08/70] Fixed #20104 -- Changed VersionDirective in order to avoid ambiguity. As explained in ticket #20104, the use of versionchanged/versionadded was confusing. To solve this ambiguity these directives no longer accept a second argument but now they only receive the version number (1st arg) and then a content with the proper comment. --- docs/_ext/djangodocs.py | 16 ++++++++++------ .../contributing/writing-documentation.txt | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py index 572bcd2e29..80967580ac 100644 --- a/docs/_ext/djangodocs.py +++ b/docs/_ext/djangodocs.py @@ -64,21 +64,25 @@ class VersionDirective(Directive): option_spec = {} def run(self): + if len(self.arguments) > 1: + msg = """Only one argument accepted for directive '{directive_name}::'. + Comments should be provided as content, + not as an extra argument.""".format(directive_name=self.name) + raise ValueError(msg) + env = self.state.document.settings.env ret = [] node = addnodes.versionmodified() ret.append(node) + if self.arguments[0] == env.config.django_next_version: node['version'] = "Development version" else: node['version'] = self.arguments[0] + node['type'] = self.name - if len(self.arguments) == 2: - inodes, messages = self.state.inline_text(self.arguments[1], self.lineno+1) - node.extend(inodes) - if self.content: - self.state.nested_parse(self.content, self.content_offset, node) - ret = ret + messages + if self.content: + self.state.nested_parse(self.content, self.content_offset, node) env.note_versionchange(node['type'], node['version'], node, self.lineno) return ret diff --git a/docs/internals/contributing/writing-documentation.txt b/docs/internals/contributing/writing-documentation.txt index 469f8614b9..2944dea504 100644 --- a/docs/internals/contributing/writing-documentation.txt +++ b/docs/internals/contributing/writing-documentation.txt @@ -188,8 +188,8 @@ Our policy for new features is: release, not the development version. Our preferred way for marking new features is by prefacing the features' -documentation with: "``.. versionadded:: X.Y``", followed by an optional one -line comment and a mandatory blank line. +documentation with: "``.. versionadded:: X.Y``", followed by a a mandatory +blank line and an optional content (indented). General improvements, or other changes to the APIs that should be emphasized should use the "``.. versionchanged:: X.Y``" directive (with the same format From 1ddeeb5b8ed5b2cf28302c8aca0a2915a3cfb240 Mon Sep 17 00:00:00 2001 From: Juan Catalano Date: Sun, 24 Mar 2013 22:52:56 -0700 Subject: [PATCH 09/70] Removed unused imports. --- docs/_ext/djangodocs.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py index 80967580ac..5878037f7f 100644 --- a/docs/_ext/djangodocs.py +++ b/docs/_ext/djangodocs.py @@ -5,9 +5,7 @@ import json import os import re -from docutils import nodes, transforms - -from sphinx import addnodes, roles, __version__ as sphinx_ver +from sphinx import addnodes, __version__ as sphinx_ver from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.writers.html import SmartyPantsHTMLTranslator from sphinx.util.console import bold From 78c842a3230f026ad678d243e5459cd6b314d99a Mon Sep 17 00:00:00 2001 From: Juan Catalano Date: Sun, 24 Mar 2013 22:53:48 -0700 Subject: [PATCH 10/70] Adapted uses of versionchanged/versionadded to the new form. Refs #20104. --- docs/_ext/djangodocs.py | 2 +- docs/howto/custom-management-commands.txt | 2 +- docs/howto/legacy-databases.txt | 4 +- docs/intro/contributing.txt | 4 +- docs/ref/class-based-views/base.txt | 1 + .../class-based-views/generic-date-based.txt | 2 +- .../class-based-views/mixins-date-based.txt | 1 + docs/ref/class-based-views/mixins-editing.txt | 8 ++-- docs/ref/class-based-views/mixins-simple.txt | 1 - docs/ref/clickjacking.txt | 1 + docs/ref/contrib/admin/index.txt | 7 +++- docs/ref/contrib/contenttypes.txt | 24 +++++------ docs/ref/contrib/gis/geoquerysets.txt | 1 + docs/ref/contrib/sites.txt | 1 + docs/ref/databases.txt | 1 + docs/ref/django-admin.txt | 19 +++++---- docs/ref/exceptions.txt | 1 + docs/ref/forms/fields.txt | 3 ++ docs/ref/forms/models.txt | 4 +- docs/ref/forms/validation.txt | 8 ++-- docs/ref/forms/widgets.txt | 1 + docs/ref/middleware.txt | 1 + docs/ref/models/fields.txt | 5 ++- docs/ref/models/instances.txt | 5 ++- docs/ref/models/querysets.txt | 32 ++++++++------- docs/ref/request-response.txt | 19 +++++---- docs/ref/settings.txt | 23 ++++++----- docs/ref/template-response.txt | 14 +++---- docs/ref/templates/api.txt | 1 + docs/ref/templates/builtins.txt | 41 ++++++++++--------- docs/ref/unicode.txt | 28 ++++++------- docs/releases/1.0.txt | 1 + docs/topics/auth/customizing.txt | 6 +-- docs/topics/auth/default.txt | 10 +++-- docs/topics/db/managers.txt | 1 + docs/topics/db/multi-db.txt | 1 + docs/topics/db/queries.txt | 2 + docs/topics/db/sql.txt | 1 + docs/topics/db/transactions.txt | 34 ++++++++------- docs/topics/forms/formsets.txt | 2 + docs/topics/http/middleware.txt | 1 + docs/topics/http/sessions.txt | 2 + docs/topics/http/shortcuts.txt | 2 + docs/topics/i18n/translation.txt | 1 + docs/topics/pagination.txt | 4 +- docs/topics/serialization.txt | 12 +++--- docs/topics/signals.txt | 2 +- docs/topics/testing/overview.txt | 7 ++++ 48 files changed, 206 insertions(+), 148 deletions(-) diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py index 5878037f7f..3ae770da20 100644 --- a/docs/_ext/djangodocs.py +++ b/docs/_ext/djangodocs.py @@ -66,7 +66,7 @@ class VersionDirective(Directive): msg = """Only one argument accepted for directive '{directive_name}::'. Comments should be provided as content, not as an extra argument.""".format(directive_name=self.name) - raise ValueError(msg) + raise self.error(msg) env = self.state.document.settings.env ret = [] diff --git a/docs/howto/custom-management-commands.txt b/docs/howto/custom-management-commands.txt index 7a31fc44e3..34e68d3700 100644 --- a/docs/howto/custom-management-commands.txt +++ b/docs/howto/custom-management-commands.txt @@ -256,7 +256,7 @@ All attributes can be set in your derived class and can be used in .. versionadded:: 1.6 -The ``leave_locale_alone`` option was added in Django 1.6. + The ``leave_locale_alone`` option was added in Django 1.6. Methods ------- diff --git a/docs/howto/legacy-databases.txt b/docs/howto/legacy-databases.txt index 6846e4b2df..4522ca01c0 100644 --- a/docs/howto/legacy-databases.txt +++ b/docs/howto/legacy-databases.txt @@ -75,8 +75,8 @@ access to your precious data on a model by model basis. .. versionchanged:: 1.6 -The behavior by which introspected models are created as unmanaged ones is new -in Django 1.6. + The behavior by which introspected models are created as unmanaged ones is new + in Django 1.6. Install the core Django tables ============================== diff --git a/docs/intro/contributing.txt b/docs/intro/contributing.txt index 8747375b0a..dd7136c877 100644 --- a/docs/intro/contributing.txt +++ b/docs/intro/contributing.txt @@ -389,8 +389,8 @@ This is a new feature, so it should be documented. Add the following on line .. versionadded:: 1.5 - The current value of the field will be displayed as a clickable link above the - input widget. + The current value of the field will be displayed as a clickable link above the + input widget. For more information on writing documentation, including an explanation of what the ``versionadded`` bit is all about, see diff --git a/docs/ref/class-based-views/base.txt b/docs/ref/class-based-views/base.txt index 2073458314..3ba7c38c43 100644 --- a/docs/ref/class-based-views/base.txt +++ b/docs/ref/class-based-views/base.txt @@ -105,6 +105,7 @@ TemplateView in the URL. .. versionchanged:: 1.5 + The context used to be populated with a ``{{ params }}`` dictionary of the parameters captured in the URL. Now those parameters are first-level context variables. diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt index 4144c382f8..4dcb788779 100644 --- a/docs/ref/class-based-views/generic-date-based.txt +++ b/docs/ref/class-based-views/generic-date-based.txt @@ -142,7 +142,7 @@ YearArchiveView .. versionchanged:: 1.5 - Previously, this returned a string. + Previously, this returned a string. * ``next_year``: A :class:`~datetime.date` object representing the first day of the next year, according to diff --git a/docs/ref/class-based-views/mixins-date-based.txt b/docs/ref/class-based-views/mixins-date-based.txt index 75f2a77615..1a1a4d531b 100644 --- a/docs/ref/class-based-views/mixins-date-based.txt +++ b/docs/ref/class-based-views/mixins-date-based.txt @@ -330,5 +330,6 @@ BaseDateListView :meth:`QuerySet.dates()`. .. versionchanged:: 1.5 + The ``ordering`` parameter was added, and the default order was changed to ascending. diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt index a4175369aa..3f32269742 100644 --- a/docs/ref/class-based-views/mixins-editing.txt +++ b/docs/ref/class-based-views/mixins-editing.txt @@ -206,10 +206,10 @@ ProcessFormView .. versionadded:: 1.6 - ``success_url`` may contain dictionary string formatting, which - will be interpolated against the object's field attributes. For - example, you could use ``success_url="/parent/%(parent_id)s/"`` to - redirect to a URL composed out of the ``parent_id`` field on a model. + ``success_url`` may contain dictionary string formatting, which + will be interpolated against the object's field attributes. For + example, you could use ``success_url="/parent/%(parent_id)s/"`` to + redirect to a URL composed out of the ``parent_id`` field on a model. .. method:: get_success_url() diff --git a/docs/ref/class-based-views/mixins-simple.txt b/docs/ref/class-based-views/mixins-simple.txt index 51b0386654..6796675529 100644 --- a/docs/ref/class-based-views/mixins-simple.txt +++ b/docs/ref/class-based-views/mixins-simple.txt @@ -67,7 +67,6 @@ TemplateResponseMixin .. attribute:: content_type .. versionadded:: 1.5 - The ``content_type`` attribute was added. The content type to use for the response. ``content_type`` is passed as a keyword argument to ``response_class``. Default is ``None`` -- diff --git a/docs/ref/clickjacking.txt b/docs/ref/clickjacking.txt index ce27148ad3..0b81243b92 100644 --- a/docs/ref/clickjacking.txt +++ b/docs/ref/clickjacking.txt @@ -62,6 +62,7 @@ To set the same ``X-Frame-Options`` value for all responses in your site, put ) .. versionchanged:: 1.6 + This middleware is enabled in the settings file generated by :djadmin:`startproject`. diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index c567bc1db4..43f0398566 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -18,6 +18,7 @@ The admin is enabled in the default project template used by :djadmin:`startproject`. .. versionchanged:: 1.6 + In previous versions, the admin wasn't enabled by default. For reference, here are the requirements: @@ -1341,6 +1342,7 @@ templates used by the :class:`ModelAdmin` views: return qs.filter(author=request.user) .. versionchanged:: 1.6 + The ``get_queryset`` method was previously named ``queryset``. .. method:: ModelAdmin.message_user(request, message, level=messages.INFO, extra_tags='', fail_silently=False) @@ -1348,7 +1350,9 @@ templates used by the :class:`ModelAdmin` views: Sends a message to the user using the :mod:`django.contrib.messages` backend. See the :ref:`custom ModelAdmin example `. - .. versionadded:: 1.5 + .. versionchanged:: 1.5 + + Keyword arguments were added in Django 1.5. Keyword arguments allow you to change the message level, add extra CSS tags, or fail silently if the ``contrib.messages`` framework is not @@ -1451,6 +1455,7 @@ in your own admin JavaScript without including a second copy, you can use the ``django.jQuery`` object on changelist and add/edit views. .. versionchanged:: 1.6 + The embedded jQuery has been upgraded from 1.4.2 to 1.9.1. The :class:`ModelAdmin` class requires jQuery by default, so there is no need diff --git a/docs/ref/contrib/contenttypes.txt b/docs/ref/contrib/contenttypes.txt index 388172c43e..4fa119bc70 100644 --- a/docs/ref/contrib/contenttypes.txt +++ b/docs/ref/contrib/contenttypes.txt @@ -234,18 +234,18 @@ lookup:: .. versionadded:: 1.5 -Prior to Django 1.5, -:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model` and -:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_models` -always returned the :class:`~django.contrib.contenttypes.models.ContentType` -associated with the concrete model of the specified one(s). That means there -was no way to retrieve the -:class:`~django.contrib.contenttypes.models.ContentType` of a proxy model -using those methods. As of Django 1.5 you can now pass a boolean flag – -``for_concrete_model`` and ``for_concrete_models`` respectively – to specify -wether or not you want to retrieve the -:class:`~django.contrib.contenttypes.models.ContentType` for the concrete or -direct model. + Prior to Django 1.5, + :meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model` and + :meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_models` + always returned the :class:`~django.contrib.contenttypes.models.ContentType` + associated with the concrete model of the specified one(s). That means there + was no way to retrieve the + :class:`~django.contrib.contenttypes.models.ContentType` of a proxy model + using those methods. As of Django 1.5 you can now pass a boolean flag – + ``for_concrete_model`` and ``for_concrete_models`` respectively – to specify + wether or not you want to retrieve the + :class:`~django.contrib.contenttypes.models.ContentType` for the concrete or + direct model. Generic relations ================= diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt index 97217e3c38..5e51e4cbf3 100644 --- a/docs/ref/contrib/gis/geoquerysets.txt +++ b/docs/ref/contrib/gis/geoquerysets.txt @@ -950,6 +950,7 @@ __ http://geohash.org/ *Availability*: PostGIS, SpatiaLite .. versionchanged:: 1.5 + ``geojson`` support for Spatialite > 3.0 has been added. Attaches a ``geojson`` attribute to every model in the queryset that contains the diff --git a/docs/ref/contrib/sites.txt b/docs/ref/contrib/sites.txt index 139a9b377f..65838dfa3e 100644 --- a/docs/ref/contrib/sites.txt +++ b/docs/ref/contrib/sites.txt @@ -252,6 +252,7 @@ Enabling the sites framework ============================ .. versionchanged:: 1.6 + In previous versions, the sites framework was enabled by default. To enable the sites framework, follow these steps: diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 395abd90dd..35f0cc6b41 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -182,6 +182,7 @@ Django supports MySQL 5.0.3 and higher. data on all database schema. Django's ``inspectdb`` feature uses it. .. versionchanged:: 1.5 + The minimum version requirement of MySQL 5.0.3 was set in Django 1.5. Django expects the database to support Unicode (UTF-8 encoding) and delegates to diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 1d3f1b8d1d..ec49705add 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -98,6 +98,7 @@ Can be run as a cronjob or directly to clean out old data from the database (only expired sessions at the moment). .. versionchanged:: 1.5 + :djadmin:`cleanup` is deprecated. Use :djadmin:`clearsessions` instead. compilemessages @@ -122,7 +123,7 @@ Example usage:: .. versionchanged:: 1.6 -Added the ability to specify multiple locales. + Added the ability to specify multiple locales. createcachetable ---------------- @@ -173,6 +174,7 @@ The :djadminopt:`--all` option may be provided to display all settings, even if they have Django's default value. Such settings are prefixed by ``"###"``. .. versionadded:: 1.6 + The :djadminopt:`--all` option was added. dumpdata @@ -307,8 +309,8 @@ database to introspect. .. versionchanged:: 1.6 -The behavior by which introspected models are created as unmanaged ones is new -in Django 1.6. + The behavior by which introspected models are created as unmanaged ones is new + in Django 1.6. loaddata ------------------------------ @@ -467,7 +469,7 @@ You can also use commas to separate multiple locales:: .. versionchanged:: 1.6 -Added the ability to specify multiple locales. + Added the ability to specify multiple locales. .. django-admin-option:: --domain @@ -778,8 +780,6 @@ use the ``--plain`` option, like so:: django-admin.py shell --plain -.. versionchanged:: 1.5 - If you would like to specify either IPython or bpython as your interpreter if you have both installed you can specify an alternative interpreter interface with the ``-i`` or ``--interface`` options like so: @@ -807,9 +807,13 @@ behavior you can use the ``--no-startup`` option. e.g.:: django-admin.py shell --plain --no-startup +.. versionadded:: 1.5 + + The ``--interface`` option was added in Django 1.5. + .. versionadded:: 1.6 -The ``--no-startup`` option was added in Django 1.6. + The ``--no-startup`` option was added in Django 1.6. sql ------------------------- @@ -1353,6 +1357,7 @@ for any other exception. If you specify ``--traceback``, ``django-admin.py`` will also output a full stack trace when a ``CommandError`` is raised. .. versionchanged:: 1.6 + Previously, Django didn't show a full stack trace by default for exceptions other than ``CommandError``. diff --git a/docs/ref/exceptions.txt b/docs/ref/exceptions.txt index 93bb9ed251..f9a1715180 100644 --- a/docs/ref/exceptions.txt +++ b/docs/ref/exceptions.txt @@ -138,6 +138,7 @@ the underlying database exceptions. See :pep:`249`, the Python Database API Specification v2.0, for further information. .. versionchanged:: 1.6 + Previous version of Django only wrapped ``DatabaseError`` and ``IntegrityError``. diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt index 03898e60ea..29f889445d 100644 --- a/docs/ref/forms/fields.txt +++ b/docs/ref/forms/fields.txt @@ -468,6 +468,7 @@ For each field, we describe the default widget used if you don't specify ``%(limit_value)s``, which will be substituted by the appropriate limit. .. versionchanged:: 1.6 + Similarly, the ``max_digits``, ``max_decimal_places`` and ``max_whole_digits`` error messages may contain ``%(max)s``. @@ -1014,10 +1015,12 @@ objects (in the case of ``ModelMultipleChoiceField``) into the ``invalid_pk_value`` .. versionchanged:: 1.5 + The empty and normalized values were changed to be consistently ``QuerySets`` instead of ``[]`` and ``QuerySet`` respectively. .. versionchanged:: 1.6 + The ``invalid_choice`` message may contain ``%(value)s`` and the ``invalid_pk_value`` message may contain ``%(pk)s``, which will be substituted by the appropriate values. diff --git a/docs/ref/forms/models.txt b/docs/ref/forms/models.txt index dd0a422fd0..7e3a1470b6 100644 --- a/docs/ref/forms/models.txt +++ b/docs/ref/forms/models.txt @@ -42,7 +42,7 @@ Model Form Functions .. versionchanged:: 1.6 - The ``widgets`` and the ``validate_max`` parameters were added. + The ``widgets`` and the ``validate_max`` parameters were added. .. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False) @@ -57,4 +57,4 @@ Model Form Functions .. versionchanged:: 1.6 - The ``widgets`` and the ``validate_max`` parameters were added. + The ``widgets`` and the ``validate_max`` parameters were added. diff --git a/docs/ref/forms/validation.txt b/docs/ref/forms/validation.txt index 978c985b55..3aaa69b6ea 100644 --- a/docs/ref/forms/validation.txt +++ b/docs/ref/forms/validation.txt @@ -359,7 +359,7 @@ considering aren't valid, we must remember to remove them from the .. versionchanged:: 1.5 -Django used to remove the ``cleaned_data`` attribute entirely if there were -any errors in the form. Since version 1.5, ``cleaned_data`` is present even if -the form doesn't validate, but it contains only field values that did -validate. + Django used to remove the ``cleaned_data`` attribute entirely if there were + any errors in the form. Since version 1.5, ``cleaned_data`` is present even if + the form doesn't validate, but it contains only field values that did + validate. diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt index 514e8b3dc0..678f2e6949 100644 --- a/docs/ref/forms/widgets.txt +++ b/docs/ref/forms/widgets.txt @@ -522,6 +522,7 @@ Selector and checkbox widgets ``True`` if the checkbox should be checked for that value. .. versionchanged:: 1.5 + Exceptions from ``check_test`` used to be silenced by its caller, this is no longer the case, they will propagate upwards. diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt index 20bb2fb751..03885a2215 100644 --- a/docs/ref/middleware.txt +++ b/docs/ref/middleware.txt @@ -206,6 +206,7 @@ Transaction middleware .. class:: TransactionMiddleware .. versionchanged:: 1.6 + ``TransactionMiddleware`` is deprecated. The documentation of transactions contains :ref:`upgrade instructions `. diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 7ef251c907..d322904ec9 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -378,6 +378,7 @@ If you need to accept :attr:`~Field.null` values then use :class:`NullBooleanField` instead. .. versionchanged:: 1.6 + The default value of ``BooleanField`` was changed from ``False`` to ``None`` when :attr:`Field.default` isn't defined. @@ -956,8 +957,8 @@ Like all :class:`CharField` subclasses, :class:`URLField` takes the optional .. versionadded:: 1.5 -The current value of the field will be displayed as a clickable link above the -input widget. + The current value of the field will be displayed as a clickable link above the + input widget. Relationship fields diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index 9f583c42ac..b4b162a9ea 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -297,8 +297,9 @@ follows this algorithm: didn't update anything, Django executes an ``INSERT``. .. versionchanged:: 1.6 - Previously Django used ``SELECT`` - if not found ``INSERT`` else ``UPDATE`` - algorithm. The old algorithm resulted in one more query in ``UPDATE`` case. + + Previously Django used ``SELECT`` - if not found ``INSERT`` else ``UPDATE`` + algorithm. The old algorithm resulted in one more query in ``UPDATE`` case. The one gotcha here is that you should be careful not to specify a primary-key value explicitly when saving new objects, if you cannot guarantee the diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 9c1337d59f..3dde0d5411 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -554,11 +554,13 @@ Returns a ``DateQuerySet`` — a ``QuerySet`` that evaluates to a list of particular kind within the contents of the ``QuerySet``. .. versionchanged:: 1.6 + ``dates`` used to return a list of :class:`datetime.datetime` objects. ``field`` should be the name of a ``DateField`` of your model. .. versionchanged:: 1.6 + ``dates`` used to accept operating on a ``DateTimeField``. ``kind`` should be either ``"year"``, ``"month"`` or ``"day"``. Each @@ -1121,11 +1123,11 @@ to ``defer()``:: .. versionchanged:: 1.5 -Some fields in a model won't be deferred, even if you ask for them. You can -never defer the loading of the primary key. If you are using -:meth:`select_related()` to retrieve related models, you shouldn't defer the -loading of the field that connects from the primary model to the related -one, doing so will result in an error. + Some fields in a model won't be deferred, even if you ask for them. You can + never defer the loading of the primary key. If you are using + :meth:`select_related()` to retrieve related models, you shouldn't defer the + loading of the field that connects from the primary model to the related + one, doing so will result in an error. .. note:: @@ -1193,20 +1195,20 @@ logically:: # existing set of fields). Entry.objects.defer("body").only("headline", "body") -.. versionchanged:: 1.5 - All of the cautions in the note for the :meth:`defer` documentation apply to ``only()`` as well. Use it cautiously and only after exhausting your other -options. Also note that using :meth:`only` and omitting a field requested -using :meth:`select_related` is an error as well. +options. .. versionchanged:: 1.5 -.. note:: + Using :meth:`only` and omitting a field requested using + :meth:`select_related` is an error as well. - When calling :meth:`~django.db.models.Model.save()` for instances with - deferred fields, only the loaded fields will be saved. See - :meth:`~django.db.models.Model.save()` for more details. + .. note:: + + When calling :meth:`~django.db.models.Model.save()` for instances with + deferred fields, only the loaded fields will be saved. See + :meth:`~django.db.models.Model.save()` for more details. using ~~~~~ @@ -1424,6 +1426,7 @@ query. The default is to create all objects in one batch, except for SQLite where the default is such that at maximum 999 variables per query is used. .. versionadded:: 1.5 + The ``batch_size`` parameter was added in version 1.5. count @@ -1725,7 +1728,8 @@ methods on your models. It does, however, emit the (including cascaded deletions). .. versionadded:: 1.5 - Allow fast-path deletion of objects + + Allow fast-path deletion of objects. Django needs to fetch objects into memory to send signals and handle cascades. However, if there are no cascades and no signals, then Django may take a diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 0f62741c5d..2fac7f2f9c 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -94,6 +94,7 @@ All attributes should be considered read-only, unless stated otherwise below. :attr:`HttpRequest.body` attribute instead. .. versionchanged:: 1.5 + Before Django 1.5, HttpRequest.POST contained non-form data. It's possible that a request can come in via POST with an empty ``POST`` @@ -563,19 +564,19 @@ streaming response if (and only if) no middleware accesses the .. versionchanged:: 1.5 -This technique is fragile and was deprecated in Django 1.5. If you need the -response to be streamed from the iterator to the client, you should use the -:class:`StreamingHttpResponse` class instead. + This technique is fragile and was deprecated in Django 1.5. If you need the + response to be streamed from the iterator to the client, you should use the + :class:`StreamingHttpResponse` class instead. -As of Django 1.7, when :class:`HttpResponse` is instantiated with an -iterator, it will consume it immediately, store the response content as a -string, and discard the iterator. + As of Django 1.7, when :class:`HttpResponse` is instantiated with an + iterator, it will consume it immediately, store the response content as a + string, and discard the iterator. .. versionchanged:: 1.5 -You can now use :class:`HttpResponse` as a file-like object even if it was -instantiated with an iterator. Django will consume and save the content of -the iterator on first access. + You can now use :class:`HttpResponse` as a file-like object even if it was + instantiated with an iterator. Django will consume and save the content of + the iterator on first access. Setting headers ~~~~~~~~~~~~~~~ diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 1fc9d2ff92..0d8b5bfd56 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1486,6 +1486,7 @@ randomly-generated ``SECRET_KEY`` to each new project. execution vulnerabilities. .. versionchanged:: 1.5 + Django will now refuse to start if :setting:`SECRET_KEY` is not set. .. setting:: SECURE_PROXY_SSL_HEADER @@ -1771,7 +1772,7 @@ See also :setting:`DATE_INPUT_FORMATS` and :setting:`DATETIME_INPUT_FORMATS`. .. versionchanged:: 1.6 -Input format with microseconds has been added. + Input format with microseconds has been added. .. _datetime: http://docs.python.org/library/datetime.html#strftime-strptime-behavior @@ -2055,11 +2056,11 @@ decorator, for example. .. versionchanged:: 1.5 -This setting now also accepts view function names and -:ref:`named URL patterns ` which can be used to reduce -configuration duplication since you no longer have to define the URL in two -places (``settings`` and URLconf). -For backward compatibility reasons the default remains unchanged. + This setting now also accepts view function names and + :ref:`named URL patterns ` which can be used to reduce + configuration duplication since you no longer have to define the URL in two + places (``settings`` and URLconf). + For backward compatibility reasons the default remains unchanged. .. setting:: LOGIN_URL @@ -2073,11 +2074,11 @@ The URL where requests are redirected for login, especially when using the .. versionchanged:: 1.5 -This setting now also accepts view function names and -:ref:`named URL patterns ` which can be used to reduce -configuration duplication since you no longer have to define the URL in two -places (``settings`` and URLconf). -For backward compatibility reasons the default remains unchanged. + This setting now also accepts view function names and + :ref:`named URL patterns ` which can be used to reduce + configuration duplication since you no longer have to define the URL in two + places (``settings`` and URLconf). + For backward compatibility reasons the default remains unchanged. .. setting:: LOGOUT_URL diff --git a/docs/ref/template-response.txt b/docs/ref/template-response.txt index 5c13ec7d96..cdefe2fae8 100644 --- a/docs/ref/template-response.txt +++ b/docs/ref/template-response.txt @@ -78,13 +78,13 @@ Methods .. versionchanged:: 1.5 - Historically, this parameter was only called ``mimetype`` (now - deprecated), but since this is actually the value included in the HTTP - ``Content-Type`` header, it can also include the character set - encoding, which makes it more than just a MIME type specification. If - ``mimetype`` is specified (not ``None``), that value is used. - Otherwise, ``content_type`` is used. If neither is given, - :setting:`DEFAULT_CONTENT_TYPE` is used. + Historically, this parameter was only called ``mimetype`` (now + deprecated), but since this is actually the value included in the HTTP + ``Content-Type`` header, it can also include the character set + encoding, which makes it more than just a MIME type specification. If + ``mimetype`` is specified (not ``None``), that value is used. + Otherwise, ``content_type`` is used. If neither is given, + :setting:`DEFAULT_CONTENT_TYPE` is used. .. method:: SimpleTemplateResponse.resolve_context(context) diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index 0162f78eed..677aa13cbb 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -272,6 +272,7 @@ Every context contains ``True``, ``False`` and ``None``. As you would expect, these variables resolve to the corresponding Python objects. .. versionadded:: 1.5 + Before Django 1.5, these variables weren't a special case, and they resolved to ``None`` unless you defined them in the context. diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 123e114c4a..474fb4d84a 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -191,19 +191,19 @@ call to ``{% cycle %}`` doesn't specify silent:: .. versionchanged:: 1.6 -To improve safety, future versions of ``cycle`` will automatically escape -their output. You're encouraged to activate this behavior by loading -``cycle`` from the ``future`` template library:: + To improve safety, future versions of ``cycle`` will automatically escape + their output. You're encouraged to activate this behavior by loading + ``cycle`` from the ``future`` template library:: - {% load cycle from future %} + {% load cycle from future %} -When using the ``future`` version, you can disable auto-escaping with:: + When using the ``future`` version, you can disable auto-escaping with:: - {% for o in some_list %} - - ... - - {% endfor %} + {% for o in some_list %} + + ... + + {% endfor %} .. templatetag:: debug @@ -294,21 +294,21 @@ to escape the variables in the firstof tag, you must do so explicitly:: .. versionchanged:: 1.6 -To improve safety, future versions of ``firstof`` will automatically escape -their output. You're encouraged to activate this behavior by loading -``firstof`` from the ``future`` template library:: + To improve safety, future versions of ``firstof`` will automatically escape + their output. You're encouraged to activate this behavior by loading + ``firstof`` from the ``future`` template library:: - {% load firstof from future %} + {% load firstof from future %} -When using the ``future`` version, you can disable auto-escaping with:: + When using the ``future`` version, you can disable auto-escaping with:: - {% autoescape off %} - {% firstof var1 var2 var3 "fallback value" %} - {% endautoescape %} + {% autoescape off %} + {% firstof var1 var2 var3 "fallback value" %} + {% endautoescape %} -Or if only some variables should be escaped, you can use:: + Or if only some variables should be escaped, you can use:: - {% firstof var1 var2|safe var3 "fallback value"|safe %} + {% firstof var1 var2|safe var3 "fallback value"|safe %} .. templatetag:: for @@ -1065,6 +1065,7 @@ by the context as to the current application. Don't forget to put quotes around the function path or pattern name! .. versionchanged:: 1.5 + The first parameter used not to be quoted, which was inconsistent with other template tags. Since Django 1.5, it is evaluated according to the usual rules: it can be a quoted string or a variable that will be diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt index bd5bdc96a9..e5074285e4 100644 --- a/docs/ref/unicode.txt +++ b/docs/ref/unicode.txt @@ -47,26 +47,26 @@ You can use Unicode strings, or you can use normal strings (sometimes called .. versionchanged:: 1.5 -In Python 3, the logic is reversed, that is normal strings are Unicode, and -when you want to specifically create a bytestring, you have to prefix the -string with a 'b'. As we are doing in Django code from version 1.5, -we recommend that you import ``unicode_literals`` from the __future__ library -in your code. Then, when you specifically want to create a bytestring literal, -prefix the string with 'b'. + In Python 3, the logic is reversed, that is normal strings are Unicode, and + when you want to specifically create a bytestring, you have to prefix the + string with a 'b'. As we are doing in Django code from version 1.5, + we recommend that you import ``unicode_literals`` from the __future__ library + in your code. Then, when you specifically want to create a bytestring literal, + prefix the string with 'b'. -Python 2 legacy:: + Python 2 legacy:: - my_string = "This is a bytestring" - my_unicode = u"This is an Unicode string" + my_string = "This is a bytestring" + my_unicode = u"This is an Unicode string" -Python 2 with unicode literals or Python 3:: + Python 2 with unicode literals or Python 3:: - from __future__ import unicode_literals + from __future__ import unicode_literals - my_string = b"This is a bytestring" - my_unicode = "This is an Unicode string" + my_string = b"This is a bytestring" + my_unicode = "This is an Unicode string" -See also :doc:`Python 3 compatibility `. + See also :doc:`Python 3 compatibility `. .. warning:: diff --git a/docs/releases/1.0.txt b/docs/releases/1.0.txt index be61311232..65ecde2fea 100644 --- a/docs/releases/1.0.txt +++ b/docs/releases/1.0.txt @@ -66,6 +66,7 @@ We can't possibly document everything that's new in 1.0, but the documentation will be your definitive guide. Anywhere you see something like: .. versionadded:: 1.0 + This feature is new in Django 1.0 You'll know that you're looking at something new or changed. diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt index 143a729f37..a5d7d3f9a1 100644 --- a/docs/topics/auth/customizing.txt +++ b/docs/topics/auth/customizing.txt @@ -83,9 +83,9 @@ processing at the first positive match. .. versionadded:: 1.6 -If a backend raises a :class:`~django.core.exceptions.PermissionDenied` -exception, authentication will immediately fail. Django won't check the -backends that follow. + If a backend raises a :class:`~django.core.exceptions.PermissionDenied` + exception, authentication will immediately fail. Django won't check the + backends that follow. Writing an authentication backend --------------------------------- diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt index a38ee84841..e666cded75 100644 --- a/docs/topics/auth/default.txt +++ b/docs/topics/auth/default.txt @@ -435,10 +435,10 @@ The login_required decorator .. versionchanged:: 1.5 - The :setting:`settings.LOGIN_URL ` also accepts - view function names and :ref:`named URL patterns `. - This allows you to freely remap your login view within your URLconf - without having to update the setting. + The :setting:`settings.LOGIN_URL ` also accepts + view function names and :ref:`named URL patterns `. + This allows you to freely remap your login view within your URLconf + without having to update the setting. .. note:: @@ -759,6 +759,7 @@ patterns. mail will be sent either. .. versionchanged:: 1.6 + Previously, error messages indicated whether a given email was registered. @@ -1041,6 +1042,7 @@ Thus, you can check permissions in template ``{% if %}`` statements: {% endif %} .. versionadded:: 1.5 + Permission lookup by "if in". It is possible to also look permissions up by ``{% if in %}`` statements. diff --git a/docs/topics/db/managers.txt b/docs/topics/db/managers.txt index 56bdd16e84..2a0f7e4ce0 100644 --- a/docs/topics/db/managers.txt +++ b/docs/topics/db/managers.txt @@ -176,6 +176,7 @@ your choice of default manager in order to avoid a situation where overriding work with. .. versionchanged:: 1.6 + The ``get_queryset`` method was previously named ``get_query_set``. .. _managers-for-related-objects: diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt index 182099cc3a..ac329cc4fc 100644 --- a/docs/topics/db/multi-db.txt +++ b/docs/topics/db/multi-db.txt @@ -681,6 +681,7 @@ In addition, some objects are automatically created just after database). .. versionchanged:: 1.5 + Previously, ``ContentType`` and ``Permission`` instances were created only in the default database. diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index f19302974d..c4f7ee59ae 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -638,6 +638,7 @@ that were modified more than 3 days after they were published:: >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3)) .. versionadded:: 1.5 + ``.bitand()`` and ``.bitor()`` The ``F()`` objects now support bitwise operations by ``.bitand()`` and @@ -646,6 +647,7 @@ The ``F()`` objects now support bitwise operations by ``.bitand()`` and >>> F('somefield').bitand(16) .. versionchanged:: 1.5 + The previously undocumented operators ``&`` and ``|`` no longer produce bitwise operations, use ``.bitand()`` and ``.bitor()`` instead. diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt index 34cfa382d3..3bf0684b29 100644 --- a/docs/topics/db/sql.txt +++ b/docs/topics/db/sql.txt @@ -222,6 +222,7 @@ For example:: return row .. versionchanged:: 1.6 + In Django 1.5 and earlier, after performing a data changing operation, you had to call ``transaction.commit_unless_managed()`` to ensure your changes were committed to the database. Since Django now defaults to database-level diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt index d48365dc9e..e6f20c4255 100644 --- a/docs/topics/db/transactions.txt +++ b/docs/topics/db/transactions.txt @@ -22,6 +22,7 @@ integrity of ORM operations that require multiple queries, especially ` queries. .. versionchanged:: 1.6 + Previous version of Django featured :ref:`a more complicated default behavior `. @@ -78,6 +79,7 @@ Middleware run outside of the transaction, and so does the rendering of template responses. .. versionchanged:: 1.6 + Django used to provide this feature via ``TransactionMiddleware``, which is now deprecated. @@ -204,6 +206,7 @@ To avoid this, you can :ref:`deactivate the transaction management `, but it isn't recommended. .. versionchanged:: 1.6 + Before Django 1.6, autocommit was turned off, and it was emulated by forcing a commit after write operations in the ORM. @@ -224,6 +227,7 @@ where you want to run your own transaction-controlling middleware or do something really strange. .. versionchanged:: 1.6 + This used to be controlled by the ``TRANSACTIONS_MANAGED`` setting. Low-level APIs @@ -312,10 +316,10 @@ rollback that would be performed by ``transaction.rollback()``. .. versionchanged:: 1.6 -When the :func:`atomic` decorator is nested, it creates a savepoint to allow -partial commit or rollback. You're strongly encouraged to use :func:`atomic` -rather than the functions described below, but they're still part of the -public API, and there's no plan to deprecate them. + When the :func:`atomic` decorator is nested, it creates a savepoint to allow + partial commit or rollback. You're strongly encouraged to use :func:`atomic` + rather than the functions described below, but they're still part of the + public API, and there's no plan to deprecate them. Each of these functions takes a ``using`` argument which should be the name of a database for which the behavior applies. If no ``using`` argument is @@ -354,20 +358,20 @@ The following example demonstrates the use of savepoints:: @transaction.atomic def viewfunc(request): - a.save() - # transaction now contains a.save() + a.save() + # transaction now contains a.save() - sid = transaction.savepoint() + sid = transaction.savepoint() - b.save() - # transaction now contains a.save() and b.save() + b.save() + # transaction now contains a.save() and b.save() - if want_to_keep_b: - transaction.savepoint_commit(sid) - # open transaction still contains a.save() and b.save() - else: - transaction.savepoint_rollback(sid) - # open transaction now contains only a.save() + if want_to_keep_b: + transaction.savepoint_commit(sid) + # open transaction still contains a.save() and b.save() + else: + transaction.savepoint_rollback(sid) + # open transaction now contains only a.save() Database-specific notes ======================= diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index 269ac5b4b6..9d77cd5274 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -111,6 +111,7 @@ affect validation. If ``validate_max=True`` is passed to the validation. See :ref:`validate_max`. .. versionchanged:: 1.6 + The ``validate_max`` parameter was added to :func:`~django.forms.formsets.formset_factory`. Also, the behavior of ``FormSet`` was brought in line with that of ``ModelFormSet`` so that it @@ -310,6 +311,7 @@ should use custom formset validation. using forged POST requests. .. versionchanged:: 1.6 + The ``validate_max`` parameter was added to :func:`~django.forms.formsets.formset_factory`. diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt index 18243c77ce..503d4322e0 100644 --- a/docs/topics/http/middleware.txt +++ b/docs/topics/http/middleware.txt @@ -204,6 +204,7 @@ Dealing with streaming responses ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionchanged:: 1.5 + ``response`` may also be an :class:`~django.http.StreamingHttpResponse` object. diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt index f5c688e254..acad61eb2a 100644 --- a/docs/topics/http/sessions.txt +++ b/docs/topics/http/sessions.txt @@ -71,6 +71,7 @@ default cache. To use another cache, set :setting:`SESSION_CACHE_ALIAS` to the name of that cache. .. versionchanged:: 1.5 + The :setting:`SESSION_CACHE_ALIAS` setting was added. Once your cache is configured, you've got two choices for how to store data in @@ -451,6 +452,7 @@ Similarly, the ``expires`` part of a session cookie is updated each time the session cookie is sent. .. versionchanged:: 1.5 + The session is not saved if the response's status code is 500. .. _browser-length-vs-persistent-sessions: diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt index 961f0b9d96..52a2935977 100644 --- a/docs/topics/http/shortcuts.txt +++ b/docs/topics/http/shortcuts.txt @@ -51,6 +51,7 @@ Optional arguments the :setting:`DEFAULT_CONTENT_TYPE` setting. .. versionchanged:: 1.5 + This parameter used to be called ``mimetype``. ``status`` @@ -129,6 +130,7 @@ Optional arguments the :setting:`DEFAULT_CONTENT_TYPE` setting. .. versionchanged:: 1.5 + This parameter used to be called ``mimetype``. diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index ed0dd33a0f..2ce9d8d2bc 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -1529,6 +1529,7 @@ selection based on data from the request. It customizes content for each user. ``'django.middleware.locale.LocaleMiddleware'``. .. versionchanged:: 1.6 + In previous versions, ``LocaleMiddleware`` wasn't enabled by default. Because middleware order matters, you should follow these guidelines: diff --git a/docs/topics/pagination.txt b/docs/topics/pagination.txt index 17747c22ff..9da71563c3 100644 --- a/docs/topics/pagination.txt +++ b/docs/topics/pagination.txt @@ -252,7 +252,7 @@ Methods .. versionchanged:: 1.5 - Raises :exc:`InvalidPage` if next page doesn't exist. + Raises :exc:`InvalidPage` if next page doesn't exist. .. method:: Page.previous_page_number() @@ -260,7 +260,7 @@ Methods .. versionchanged:: 1.5 - Raises :exc:`InvalidPage` if previous page doesn't exist. + Raises :exc:`InvalidPage` if previous page doesn't exist. .. method:: Page.start_index() diff --git a/docs/topics/serialization.txt b/docs/topics/serialization.txt index ce39f6cd28..cb34117997 100644 --- a/docs/topics/serialization.txt +++ b/docs/topics/serialization.txt @@ -124,8 +124,8 @@ Calling ``DeserializedObject.save()`` saves the object to the database. .. versionchanged:: 1.6 -In previous versions of Django, the ``pk`` attribute had to be present -on the serialized data or a ``DeserializationError`` would be raised. + In previous versions of Django, the ``pk`` attribute had to be present + on the serialized data or a ``DeserializationError`` would be raised. This ensures that deserializing is a non-destructive operation even if the data in your serialized representation doesn't match what's currently in the @@ -144,11 +144,11 @@ The Django object itself can be inspected as ``deserialized_object.object``. .. versionadded:: 1.5 -If fields in the serialized data do not exist on a model, -a ``DeserializationError`` will be raised unless the ``ignorenonexistent`` -argument is passed in as True:: + If fields in the serialized data do not exist on a model, + a ``DeserializationError`` will be raised unless the ``ignorenonexistent`` + argument is passed in as True:: - serializers.deserialize("xml", data, ignorenonexistent=True) + serializers.deserialize("xml", data, ignorenonexistent=True) .. _serialization-formats: diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt index d611da4a37..a97fb2f14f 100644 --- a/docs/topics/signals.txt +++ b/docs/topics/signals.txt @@ -134,7 +134,7 @@ to. .. versionchanged:: 1.5 -The ability to pass a list of signals was added. + The ability to pass a list of signals was added. .. admonition:: Where should this code live? diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index 259c39618b..cd7c98c85c 100644 --- a/docs/topics/testing/overview.txt +++ b/docs/topics/testing/overview.txt @@ -235,6 +235,7 @@ the Django test runner reorders tests in the following way: restoring it to its original state are run. .. versionchanged:: 1.5 + Before Django 1.5, the only guarantee was that :class:`~django.test.TestCase` tests were always ran first, before any other tests. @@ -612,6 +613,7 @@ Use the ``django.test.client.Client`` class to make requests. a ``Content-Type`` header is set to ``content_type``. .. versionchanged:: 1.5 + :meth:`Client.options` used to process ``data`` like :meth:`Client.get`. @@ -627,6 +629,7 @@ Use the ``django.test.client.Client`` class to make requests. a ``Content-Type`` header is set to ``content_type``. .. versionchanged:: 1.5 + :meth:`Client.put` used to process ``data`` like :meth:`Client.post`. @@ -650,6 +653,7 @@ Use the ``django.test.client.Client`` class to make requests. a ``Content-Type`` header is set to ``content_type``. .. versionchanged:: 1.5 + :meth:`Client.delete` used to process ``data`` like :meth:`Client.get`. @@ -940,6 +944,7 @@ to test the effects of commit and rollback: the test has been properly updated. .. versionchanged:: 1.5 + The order in which tests are run has changed. See `Order in which tests are executed`_. @@ -990,6 +995,7 @@ additions, including: errors. .. versionchanged:: 1.5 + The order in which tests are run has changed. See `Order in which tests are executed`_. @@ -1581,6 +1587,7 @@ your test suite. ``False``, which turns the comparison into a Python set comparison. .. versionchanged:: 1.6 + The method now checks for undefined order and raises ``ValueError`` if undefined order is spotted. The ordering is seen as undefined if the given ``qs`` isn't ordered and the comparison is against more From 70d3adf9f2ebfdfc9e64376b71b596ebb12d77f8 Mon Sep 17 00:00:00 2001 From: jktravis Date: Sun, 21 Apr 2013 09:52:02 -0400 Subject: [PATCH 11/70] Added 'polls' sub directory under static and reorganized to more accurately represent the project; compared using 'tree' command. --- docs/intro/reusable-apps.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt index 1e0f1da0c5..b2debf6908 100644 --- a/docs/intro/reusable-apps.txt +++ b/docs/intro/reusable-apps.txt @@ -66,16 +66,17 @@ After the previous tutorials, our project should look like this:: __init__.py admin.py models.py - tests.py static/ - style.css - images/ - background.gif + polls + images/ + background.gif + style.css templates/ polls/ detail.html index.html results.html + tests.py urls.py views.py templates/ From 7cc3acbb70c8f66060b377ed402df3085288b006 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 22 Apr 2013 19:50:17 +0200 Subject: [PATCH 12/70] Fixed #19211 -- Adapted tutorial for Python 3 --- docs/intro/tutorial01.txt | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index 55e85dcbe3..7c21041ed3 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -578,27 +578,33 @@ Wait a minute. ```` is, utterly, an unhelpful representation of this object. Let's fix that by editing the polls model (in the ``polls/models.py`` file) and adding a :meth:`~django.db.models.Model.__unicode__` method to both ``Poll`` and -``Choice``:: +``Choice``. On Python 3, simply replace ``__unicode__`` by ``__str__`` in the +following example:: class Poll(models.Model): # ... - def __unicode__(self): + def __unicode__(self): # Python 3: def __str__(self): return self.question class Choice(models.Model): # ... - def __unicode__(self): + def __unicode__(self): # Python 3: def __str__(self): return self.choice_text -It's important to add :meth:`~django.db.models.Model.__unicode__` methods to -your models, not only for your own sanity when dealing with the interactive -prompt, but also because objects' representations are used throughout Django's -automatically-generated admin. +It's important to add :meth:`~django.db.models.Model.__unicode__` methods (or +:meth:`~django.db.models.Model.__str__` on Python 3) to your models, not only +for your own sanity when dealing with the interactive prompt, but also because +objects' representations are used throughout Django's automatically-generated +admin. -.. admonition:: Why :meth:`~django.db.models.Model.__unicode__` and not +.. admonition:: :meth:`~django.db.models.Model.__unicode__` or :meth:`~django.db.models.Model.__str__`? - If you're familiar with Python, you might be in the habit of adding + On Python 3, things are simpler, just use + :meth:`~django.db.models.Model.__str__` and forget about + :meth:`~django.db.models.Model.__unicode__`. + + If you're familiar with Python 2, you might be in the habit of adding :meth:`~django.db.models.Model.__str__` methods to your classes, not :meth:`~django.db.models.Model.__unicode__` methods. We use :meth:`~django.db.models.Model.__unicode__` here because Django models deal From 73c26f0c95e83582e933e0ef2f71a013f7a9740e Mon Sep 17 00:00:00 2001 From: Baptiste Mispelon Date: Mon, 22 Apr 2013 19:04:28 +0200 Subject: [PATCH 13/70] Fixed #20270 -- Fixed error in AjaxResponseMixin documentation --- docs/topics/class-based-views/generic-editing.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt index 8cd34f8ad9..66ba36fd87 100644 --- a/docs/topics/class-based-views/generic-editing.txt +++ b/docs/topics/class-based-views/generic-editing.txt @@ -237,19 +237,24 @@ works for AJAX requests as well as 'normal' form POSTs:: return HttpResponse(data, **response_kwargs) def form_invalid(self, form): + response = super(AjaxableResponseMixin, self).form_invalid(form) if self.request.is_ajax(): return self.render_to_json_response(form.errors, status=400) else: - return super(AjaxableResponseMixin, self).form_invalid(form) + return response def form_valid(self, form): + # We make sure to call the parent's form_valid() method because + # it might do some processing (in the case of CreateView, it will + # call form.save() for example). + response = super(AjaxableResponseMixin, self).form_valid(form) if self.request.is_ajax(): data = { - 'pk': form.instance.pk, + 'pk': self.object.pk, } return self.render_to_json_response(data) else: - return super(AjaxableResponseMixin, self).form_valid(form) + return response class AuthorCreate(AjaxableResponseMixin, CreateView): model = Author From f17aa998be1f197004ac5d708cc7bcc0c3362bf0 Mon Sep 17 00:00:00 2001 From: Thomas Thurman Date: Tue, 23 Apr 2013 16:10:25 +0100 Subject: [PATCH 14/70] Fixed #20307 -- Word repeated in tutorial. Replaced "the the" in the tutorial with "the". --- docs/intro/tutorial06.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/intro/tutorial06.txt b/docs/intro/tutorial06.txt index 6b3d0f35e2..d5db3ae232 100644 --- a/docs/intro/tutorial06.txt +++ b/docs/intro/tutorial06.txt @@ -108,7 +108,7 @@ loaded in the bottom right of the screen. These are the **basics**. For more details on settings and other bits included with the framework see -:doc:`the static files howto ` and the +:doc:`the static files howto ` and :doc:`the staticfiles reference `. :doc:`Deploying static files ` discusses how to use static files on a real server. From 949b4e94588e73067f64b5b6d22e569a105baaf7 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Wed, 24 Apr 2013 09:27:03 +0200 Subject: [PATCH 15/70] Fixed the reusable apps docs to use a rst file extension for the README and follow PEP8 a bit more. --- docs/intro/reusable-apps.txt | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt index b2debf6908..5c79ffcc56 100644 --- a/docs/intro/reusable-apps.txt +++ b/docs/intro/reusable-apps.txt @@ -130,7 +130,7 @@ this. For a small app like polls, this process isn't too difficult. 2. Move the ``polls`` directory into the ``django-polls`` directory. -3. Create a file ``django-polls/README.txt`` with the following contents:: +3. Create a file ``django-polls/README.rst`` with the following contents:: ===== Polls @@ -178,23 +178,23 @@ Create a file ``django-polls/setup.py`` with the following contents:: import os from setuptools import setup - README = open(os.path.join(os.path.dirname(__file__), 'README.txt')).read() + README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() # allow setup.py to be run from any path os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) setup( - name = 'django-polls', - version = '0.1', - packages = ['polls'], - include_package_data = True, - license = 'BSD License', # example license - description = 'A simple Django app to conduct Web-based polls.', - long_description = README, - url = 'http://www.example.com/', - author = 'Your Name', - author_email = 'yourname@example.com', - classifiers = [ + name='django-polls', + version='0.1', + packages=['polls'], + include_package_data=True, + license='BSD License', # example license + description='A simple Django app to conduct Web-based polls.', + long_description=README, + url='http://www.example.com/', + author='Your Name', + author_email='yourname@example.com', + classifiers=[ 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', @@ -217,10 +217,12 @@ Create a file ``django-polls/setup.py`` with the following contents:: 6. Only Python modules and packages are included in the package by default. To include additional files, we'll need to create a ``MANIFEST.in`` file. The distribute docs referred to in the previous step discuss this file in more - details. To include the templates and our LICENSE file, create a file - ``django-polls/MANIFEST.in`` with the following contents:: + details. To include the templates, the ``README.rst`` and our ``LICENSE`` + file, create a file ``django-polls/MANIFEST.in`` with the following + contents:: include LICENSE + include README.rst recursive-include polls/templates * 7. It's optional, but recommended, to include detailed documentation with your From db394e6ab2c1d3d53b8b8d691e995d0c3584b944 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Wed, 24 Apr 2013 09:41:06 +0200 Subject: [PATCH 16/70] Fixed a rST bug in the README template in the reusable apps docs. --- docs/intro/reusable-apps.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt index 5c79ffcc56..a19478fc3a 100644 --- a/docs/intro/reusable-apps.txt +++ b/docs/intro/reusable-apps.txt @@ -158,7 +158,7 @@ this. For a small app like polls, this process isn't too difficult. 3. Run `python manage.py syncdb` to create the polls models. 4. Start the development server and visit http://127.0.0.1:8000/admin/ - to create a poll (you'll need the Admin app enabled). + to create a poll (you'll need the Admin app enabled). 5. Visit http://127.0.0.1:8000/polls/ to participate in the poll. From 9e80663c95a1d2c17b0d6cf9400d882e4fa89721 Mon Sep 17 00:00:00 2001 From: Evan Carmi Date: Wed, 24 Apr 2013 15:02:19 -0300 Subject: [PATCH 17/70] Fix CharField typo in legacy-databases.txt docs The example Person model in the legacy databases HowTo had a typo referring to ChaField instead of Charfield --- docs/howto/legacy-databases.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/howto/legacy-databases.txt b/docs/howto/legacy-databases.txt index 4522ca01c0..0bea8b41c4 100644 --- a/docs/howto/legacy-databases.txt +++ b/docs/howto/legacy-databases.txt @@ -61,7 +61,7 @@ this generated model definition: class Person(models.Model): id = models.IntegerField(primary_key=True) - first_name = models.ChaField(max_length=70) + first_name = models.CharField(max_length=70) class Meta: **managed = False** db_table = 'CENSUS_PERSONS' From 8f7276b782e4e10f3e361f9d833f4e415e70bb28 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 25 Apr 2013 10:32:27 +0200 Subject: [PATCH 18/70] Updated Transifex links to the Django project Translations for the Django framework are now hosted on the django-core Transifex project, and the django project is only dedicated to a team-only hub project. --- .tx/config | 30 +++++++++++----------- docs/internals/contributing/localizing.txt | 2 +- scripts/manage_translations.py | 4 +-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.tx/config b/.tx/config index cb617326b9..fdd7a576f7 100644 --- a/.tx/config +++ b/.tx/config @@ -2,77 +2,77 @@ host = https://www.transifex.com lang_map = sr@latin:sr_Latn -[django.core] +[django-core.core] file_filter = django/conf/locale//LC_MESSAGES/django.po source_file = django/conf/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-admin] +[django-core.contrib-admin] file_filter = django/contrib/admin/locale//LC_MESSAGES/django.po source_file = django/contrib/admin/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-admin-js] +[django-core.contrib-admin-js] file_filter = django/contrib/admin/locale//LC_MESSAGES/djangojs.po source_file = django/contrib/admin/locale/en/LC_MESSAGES/djangojs.po source_lang = en -[django.contrib-admindocs] +[django-core.contrib-admindocs] file_filter = django/contrib/admindocs/locale//LC_MESSAGES/django.po source_file = django/contrib/admindocs/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-auth] +[django-core.contrib-auth] file_filter = django/contrib/auth/locale//LC_MESSAGES/django.po source_file = django/contrib/auth/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-comments] +[django-core.contrib-comments] file_filter = django/contrib/comments/locale//LC_MESSAGES/django.po source_file = django/contrib/comments/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-contenttypes] +[django-core.contrib-contenttypes] file_filter = django/contrib/contenttypes/locale//LC_MESSAGES/django.po source_file = django/contrib/contenttypes/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-flatpages] +[django-core.contrib-flatpages] file_filter = django/contrib/flatpages/locale//LC_MESSAGES/django.po source_file = django/contrib/flatpages/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-formtools] +[django-core.contrib-formtools] file_filter = django/contrib/formtools/locale//LC_MESSAGES/django.po source_file = django/contrib/formtools/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-gis] +[django-core.contrib-gis] file_filter = django/contrib/gis/locale//LC_MESSAGES/django.po source_file = django/contrib/gis/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-humanize] +[django-core.contrib-humanize] file_filter = django/contrib/humanize/locale//LC_MESSAGES/django.po source_file = django/contrib/humanize/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-messages] +[django-core.contrib-messages] file_filter = django/contrib/messages/locale//LC_MESSAGES/django.po source_file = django/contrib/messages/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-redirects] +[django-core.contrib-redirects] file_filter = django/contrib/redirects/locale//LC_MESSAGES/django.po source_file = django/contrib/redirects/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-sessions] +[django-core.contrib-sessions] file_filter = django/contrib/sessions/locale//LC_MESSAGES/django.po source_file = django/contrib/sessions/locale/en/LC_MESSAGES/django.po source_lang = en -[django.contrib-sites] +[django-core.contrib-sites] file_filter = django/contrib/sites/locale//LC_MESSAGES/django.po source_file = django/contrib/sites/locale/en/LC_MESSAGES/django.po source_lang = en diff --git a/docs/internals/contributing/localizing.txt b/docs/internals/contributing/localizing.txt index 0cde77882c..01b88d8d9a 100644 --- a/docs/internals/contributing/localizing.txt +++ b/docs/internals/contributing/localizing.txt @@ -62,5 +62,5 @@ Django source tree, as for any code change: .. _Transifex: https://www.transifex.com/ .. _Django i18n mailing list: http://groups.google.com/group/django-i18n/ -.. _Django project page: https://www.transifex.com/projects/p/django/ +.. _Django project page: https://www.transifex.com/projects/p/django-core/ .. _Transifex User Guide: http://help.transifex.com/ diff --git a/scripts/manage_translations.py b/scripts/manage_translations.py index b90b26e1f8..3e58040187 100644 --- a/scripts/manage_translations.py +++ b/scripts/manage_translations.py @@ -47,9 +47,9 @@ def _get_locale_dirs(include_core=True): def _tx_resource_for_name(name): """ Return the Transifex resource name """ if name == 'core': - return "django.core" + return "django-core.core" else: - return "django.contrib-%s" % name + return "django-core.contrib-%s" % name def _check_diff(cat_name, base_path): """ From 86243d2e57e450b52ae063d23653e74bf9b85e4d Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Thu, 25 Apr 2013 11:41:57 -0500 Subject: [PATCH 19/70] Removed an errant ipdb import from commit 9777442 --- django/db/models/query.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/django/db/models/query.py b/django/db/models/query.py index c36d81cc31..337049e2ff 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -312,11 +312,7 @@ class QuerySet(object): if skip: obj = model_cls(**dict(zip(init_list, row_data))) else: - try: - obj = model(*row_data) - except IndexError: - import ipdb; ipdb.set_trace() - pass + obj = model(*row_data) # Store the source database of the object obj._state.db = db From 5a5e1ac24d3fedca3c3f085b1bd9b13ebbef2d45 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Thu, 25 Apr 2013 11:42:08 -0500 Subject: [PATCH 20/70] Negligible formatting fixes in query_utils.py --- django/db/models/query_utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index a33c44833c..a80a1b9c13 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -102,7 +102,7 @@ class DeferredAttribute(object): f = [f for f in opts.fields if f.attname == self.field_name][0] name = f.name - # Lets see if the field is part of the parent chain. If so we + # Let's see if the field is part of the parent chain. If so we # might be able to reuse the already loaded value. Refs #18343. val = self._check_parent_chain(instance, name) if val is None: @@ -194,8 +194,7 @@ def deferred_class_factory(model, attrs): name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs)))) name = util.truncate_name(name, 80, 32) - overrides = dict([(attr, DeferredAttribute(attr, model)) - for attr in attrs]) + overrides = dict([(attr, DeferredAttribute(attr, model)) for attr in attrs]) overrides["Meta"] = Meta overrides["__module__"] = model.__module__ overrides["_deferred"] = True From 6bccbc05a443b97e8651a36df10de6ac5283aee4 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Thu, 25 Apr 2013 12:22:16 -0500 Subject: [PATCH 21/70] Converted a list comprehension into a generator expression in query_utils.py --- django/db/models/query_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index a80a1b9c13..ee7a56a26c 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -194,7 +194,7 @@ def deferred_class_factory(model, attrs): name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs)))) name = util.truncate_name(name, 80, 32) - overrides = dict([(attr, DeferredAttribute(attr, model)) for attr in attrs]) + overrides = dict((attr, DeferredAttribute(attr, model)) for attr in attrs) overrides["Meta"] = Meta overrides["__module__"] = model.__module__ overrides["_deferred"] = True From 4769db6b5fddaed93a8d8d03d0c36f7262e9ac8b Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 26 Apr 2013 08:57:39 +0200 Subject: [PATCH 22/70] Fixed #20321 -- Added missing key name in MergeDict KeyError message Thanks mark.harviston et gmail.com for the report. --- django/utils/datastructures.py | 2 +- tests/utils_tests/test_datastructures.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index b3060202be..a211323320 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -26,7 +26,7 @@ class MergeDict(object): return dict_[key] except KeyError: pass - raise KeyError + raise KeyError(key) def __copy__(self): return self.__class__(*self.dicts) diff --git a/tests/utils_tests/test_datastructures.py b/tests/utils_tests/test_datastructures.py index 91111cc2ed..5829e7c2d7 100644 --- a/tests/utils_tests/test_datastructures.py +++ b/tests/utils_tests/test_datastructures.py @@ -209,6 +209,14 @@ class MergeDictTests(SimpleTestCase): self.assertFalse(empty) self.assertTrue(not_empty) + def test_key_error(self): + """ + Test that the message of KeyError contains the missing key name. + """ + d1 = MergeDict({'key1': 42}) + with six.assertRaisesRegex(self, KeyError, 'key2'): + d1['key2'] + class MultiValueDictTests(SimpleTestCase): From 4ff682c1ba1cd051243842bbd2feb19e75b559f3 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 28 Apr 2013 10:47:07 +0200 Subject: [PATCH 23/70] Fixed #18336 -- Doubled request_queue_size. This reduces random failures to load static files with Google Chrome. --- django/core/servers/basehttp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index d329221ce4..387da6b3cd 100644 --- a/django/core/servers/basehttp.py +++ b/django/core/servers/basehttp.py @@ -108,6 +108,8 @@ class ServerHandler(simple_server.ServerHandler, object): class WSGIServer(simple_server.WSGIServer, object): """BaseHTTPServer that implements the Python WSGI protocol""" + request_queue_size = 10 + def __init__(self, *args, **kwargs): if kwargs.pop('ipv6', False): self.address_family = socket.AF_INET6 From f7f69cf7dd2730f3cf07f8dc71fc60b6c15bbf64 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 28 Apr 2013 15:09:22 +0200 Subject: [PATCH 24/70] Pointed to the docs version switcher in tutorial 1. Refs #20324. Thanks dwisehart for the suggestion. --- docs/intro/tutorial01.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index 7c21041ed3..d1d2242411 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -23,7 +23,8 @@ If Django is installed, you should see the version of your installation. If it isn't, you'll get an error telling "No module named django". This tutorial is written for Django |version| and Python 2.x. If the Django -version doesn't match, you can refer to the tutorial for your version of Django +version doesn't match, you can refer to the tutorial for your version of +Django by using the version switcher at the bottom right corner of this page, or update Django to the newest version. If you are using Python 3.x, be aware that your code may need to differ from what is in the tutorial and you should continue using the tutorial only if you know what you are doing with Python From 90fe9141ded9f7005546e9a66f31fd196f60e7e4 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 28 Apr 2013 16:45:05 +0200 Subject: [PATCH 25/70] Fixed #18986 -- Improved error message for missing files in CachedStaticFilesStorage. Thanks zyegfryed for his work on the patch. --- .../management/commands/collectstatic.py | 6 ++++++ django/contrib/staticfiles/storage.py | 5 ++++- .../staticfiles_tests/project/faulty/faulty.css | 1 + tests/staticfiles_tests/tests.py | 17 ++++++++++++++++- 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/staticfiles_tests/project/faulty/faulty.css diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py index 6116f31efc..22fdac7c76 100644 --- a/django/contrib/staticfiles/management/commands/collectstatic.py +++ b/django/contrib/staticfiles/management/commands/collectstatic.py @@ -116,6 +116,12 @@ class Command(NoArgsCommand): processor = self.storage.post_process(found_files, dry_run=self.dry_run) for original_path, processed_path, processed in processor: + if isinstance(processed, Exception): + self.stderr.write("Post-processing '%s' failed!" % original_path) + # Add a blank line before the traceback, otherwise it's + # too easy to miss the relevant part of the error message. + self.stderr.write("") + raise processed if processed: self.log("Post-processed '%s' as '%s'" % (original_path, processed_path), level=1) diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index f444e12c19..d085cf723f 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -251,7 +251,10 @@ class CachedFilesMixin(object): for patterns in self._patterns.values(): for pattern, template in patterns: converter = self.url_converter(name, template) - content = pattern.sub(converter, content) + try: + content = pattern.sub(converter, content) + except ValueError as exc: + yield name, None, exc if hashed_file_exists: self.delete(hashed_name) # then save the processed result diff --git a/tests/staticfiles_tests/project/faulty/faulty.css b/tests/staticfiles_tests/project/faulty/faulty.css new file mode 100644 index 0000000000..ca57c77e55 --- /dev/null +++ b/tests/staticfiles_tests/project/faulty/faulty.css @@ -0,0 +1 @@ +@import url("missing.css"); diff --git a/tests/staticfiles_tests/tests.py b/tests/staticfiles_tests/tests.py index 0391b8b018..912dcffe83 100644 --- a/tests/staticfiles_tests/tests.py +++ b/tests/staticfiles_tests/tests.py @@ -244,7 +244,7 @@ class TestCollection(CollectionTestCase, TestDefaults): class TestCollectionClear(CollectionTestCase): """ - Test the ``--clear`` option of the ``collectstatic`` managemenet command. + Test the ``--clear`` option of the ``collectstatic`` management command. """ def run_collectstatic(self, **kwargs): clear_filepath = os.path.join(settings.STATIC_ROOT, 'cleared.txt') @@ -550,6 +550,21 @@ class TestCollectionCachedStorage(BaseCollectionTestCase, self.assertNotIn(b"cached/other.css", content) self.assertIn(b"other.d41d8cd98f00.css", content) + @override_settings( + STATICFILES_DIRS=(os.path.join(TEST_ROOT, 'project', 'faulty'),), + STATICFILES_FINDERS=('django.contrib.staticfiles.finders.FileSystemFinder',), + ) + def test_post_processing_failure(self): + """ + Test that post_processing indicates the origin of the error when it + fails. Regression test for #18986. + """ + finders._finders.clear() + err = six.StringIO() + with self.assertRaises(Exception) as cm: + call_command('collectstatic', interactive=False, verbosity=0, stderr=err) + self.assertEqual("Post-processing 'faulty.css' failed!\n\n", err.getvalue()) + # we set DEBUG to False here since the template tag wouldn't work otherwise @override_settings(**dict(TEST_SETTINGS, From 3a4276ffc35326f20e95890b50a67aebeabc9ad0 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 28 Apr 2013 17:15:41 +0200 Subject: [PATCH 26/70] Tested that get_or_create raises IntegrityError. It used to raise "DatabaseError: no such savepoint" with the old transaction management. Closes #15117. --- tests/get_or_create/models.py | 4 ++++ tests/get_or_create/tests.py | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/get_or_create/models.py b/tests/get_or_create/models.py index 678f5a401c..82905de4f8 100644 --- a/tests/get_or_create/models.py +++ b/tests/get_or_create/models.py @@ -24,3 +24,7 @@ class Person(models.Model): class ManualPrimaryKeyTest(models.Model): id = models.IntegerField(primary_key=True) data = models.CharField(max_length=100) + + +class Profile(models.Model): + person = models.ForeignKey(Person, primary_key=True) diff --git a/tests/get_or_create/tests.py b/tests/get_or_create/tests.py index 1e300fbb4d..e9cce9bbde 100644 --- a/tests/get_or_create/tests.py +++ b/tests/get_or_create/tests.py @@ -4,9 +4,9 @@ from datetime import date import traceback from django.db import IntegrityError -from django.test import TestCase +from django.test import TestCase, TransactionTestCase -from .models import Person, ManualPrimaryKeyTest +from .models import Person, ManualPrimaryKeyTest, Profile class GetOrCreateTests(TestCase): @@ -64,3 +64,16 @@ class GetOrCreateTests(TestCase): formatted_traceback = traceback.format_exc() self.assertIn('obj.save', formatted_traceback) + +class GetOrCreateTransactionTests(TransactionTestCase): + + def test_get_or_create_integrityerror(self): + # Regression test for #15117. Requires a TransactionTestCase on + # databases that delay integrity checks until the end of transactions, + # otherwise the exception is never raised. + try: + Profile.objects.get_or_create(person=Person(id=1)) + except IntegrityError: + pass + else: + self.skipTest("This backend does not support integrity checks.") From b47b0211f52dad9d816db587a33f2d7055a226b3 Mon Sep 17 00:00:00 2001 From: Thomas Thurman Date: Mon, 29 Apr 2013 14:30:51 +0100 Subject: [PATCH 27/70] Corrected "it's" to "its" when used possessively. Fixed #20327. --- docs/ref/contrib/gis/geos.txt | 2 +- docs/ref/contrib/gis/install/geolibs.txt | 2 +- docs/ref/templates/builtins.txt | 2 +- docs/releases/1.3-alpha-1.txt | 2 +- docs/topics/forms/media.txt | 2 +- docs/topics/logging.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ref/contrib/gis/geos.txt b/docs/ref/contrib/gis/geos.txt index 4d44638488..12e4e55165 100644 --- a/docs/ref/contrib/gis/geos.txt +++ b/docs/ref/contrib/gis/geos.txt @@ -845,7 +845,7 @@ include the SRID value (in other words, EWKB). .. class:: WKBWriter ``WKBWriter`` provides the most control over its output. By default it -returns OGC-compliant WKB when it's ``write`` method is called. However, +returns OGC-compliant WKB when its ``write`` method is called. However, it has properties that allow for the creation of EWKB, a superset of the WKB standard that includes additional information. diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt index 74ebf6a35f..c9a1b405a3 100644 --- a/docs/ref/contrib/gis/install/geolibs.txt +++ b/docs/ref/contrib/gis/install/geolibs.txt @@ -194,7 +194,7 @@ Configure, make and install:: .. note:: - Because GeoDjango has it's own Python interface, the preceding instructions + Because GeoDjango has its own Python interface, the preceding instructions do not build GDAL's own Python bindings. The bindings may be built by adding the ``--with-python`` flag when running ``configure``. See `GDAL/OGR In Python`__ for more information on GDAL's bindings. diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 474fb4d84a..e0800037df 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -176,7 +176,7 @@ just declare the cycle, but not output the first value, you can add a This will output a list of ```` elements with ``class`` alternating between ``row1`` and ``row2``; the subtemplate will have -access to ``rowcolors`` in it's context that matches the class of the +access to ``rowcolors`` in its context that matches the class of the ```` that encloses it. If the ``silent`` keyword were to be omitted, ``row1`` would be emitted as normal text, outside the ```` element. diff --git a/docs/releases/1.3-alpha-1.txt b/docs/releases/1.3-alpha-1.txt index c71736dc60..42947d9a44 100644 --- a/docs/releases/1.3-alpha-1.txt +++ b/docs/releases/1.3-alpha-1.txt @@ -87,7 +87,7 @@ To access this library, Django provides the ``django.utils.unittest`` module alias. If you are using Python 2.7, or you have installed unittest2 locally, Django will map the alias to the installed version of the unittest library. Otherwise, -Django will use it's own bundled version of unittest2. +Django will use its own bundled version of unittest2. To use this alias, simply use:: diff --git a/docs/topics/forms/media.txt b/docs/topics/forms/media.txt index 98e70e5e77..c0d63bb8cf 100644 --- a/docs/topics/forms/media.txt +++ b/docs/topics/forms/media.txt @@ -146,7 +146,7 @@ basic Calendar widget from the example above:: -The FancyCalendar widget inherits all the media from it's parent widget. If +The FancyCalendar widget inherits all the media from its parent widget. If you don't want media to be inherited in this way, add an ``extend=False`` declaration to the media declaration:: diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt index cb22a57e84..a31dc01cc5 100644 --- a/docs/topics/logging.txt +++ b/docs/topics/logging.txt @@ -169,7 +169,7 @@ issued on the ``project.interesting`` and ``project.interesting.stuff`` loggers. This propagation can be controlled on a per-logger basis. If -you don't want a particular logger to propagate to it's parents, you +you don't want a particular logger to propagate to its parents, you can turn off this behavior. Making logging calls From 1267d2d9bc8bbb38406a676de31c861ec40b5567 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 29 Apr 2013 19:40:03 +0200 Subject: [PATCH 28/70] Fixed #20330 -- Normalized spelling of "web server". Thanks Baptiste Mispelon for the report. --- django/core/handlers/base.py | 4 ++-- docs/howto/deployment/fastcgi.txt | 2 +- docs/howto/static-files/index.txt | 2 +- docs/intro/tutorial01.txt | 2 +- docs/ref/settings.txt | 8 ++++---- docs/ref/views.txt | 2 +- docs/topics/security.txt | 8 ++++---- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 900ea8e6b7..acc74db6f5 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -237,7 +237,7 @@ def get_path_info(environ): """ path_info = environ.get('PATH_INFO', str('/')) # Under Python 3, strings in environ are decoded with ISO-8859-1; - # re-encode to recover the original bytestring provided by the webserver. + # re-encode to recover the original bytestring provided by the web server. if six.PY3: path_info = path_info.encode('iso-8859-1') # It'd be better to implement URI-to-IRI decoding, see #19508. @@ -266,7 +266,7 @@ def get_script_name(environ): else: script_name = environ.get('SCRIPT_NAME', str('')) # Under Python 3, strings in environ are decoded with ISO-8859-1; - # re-encode to recover the original bytestring provided by the webserver. + # re-encode to recover the original bytestring provided by the web server. if six.PY3: script_name = script_name.encode('iso-8859-1') # It'd be better to implement URI-to-IRI decoding, see #19508. diff --git a/docs/howto/deployment/fastcgi.txt b/docs/howto/deployment/fastcgi.txt index 6a5acfb7cc..507e50d1a2 100644 --- a/docs/howto/deployment/fastcgi.txt +++ b/docs/howto/deployment/fastcgi.txt @@ -112,7 +112,7 @@ Running a preforked server on a Unix domain socket:: .. admonition:: Socket security - Django's default umask requires that the webserver and the Django fastcgi + Django's default umask requires that the web server and the Django fastcgi process be run with the same group **and** user. For increased security, you can run them under the same group but as different users. If you do this, you will need to set the umask to 0002 using the ``umask`` argument diff --git a/docs/howto/static-files/index.txt b/docs/howto/static-files/index.txt index a26fc04cc9..1fdad94143 100644 --- a/docs/howto/static-files/index.txt +++ b/docs/howto/static-files/index.txt @@ -106,7 +106,7 @@ for gathering static files in a single directory so you can serve them easily. This will copy all files from your static folders into the :setting:`STATIC_ROOT` directory. -3. Use a webserver of your choice to serve the +3. Use a web server of your choice to serve the files. :doc:`/howto/static-files/deployment` covers some common deployment strategies for static files. diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index d1d2242411..a0e776ae69 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -123,7 +123,7 @@ These files are: "table of contents" of your Django-powered site. You can read more about URLs in :doc:`/topics/http/urls`. -* :file:`mysite/wsgi.py`: An entry-point for WSGI-compatible webservers to +* :file:`mysite/wsgi.py`: An entry-point for WSGI-compatible web servers to serve your project. See :doc:`/howto/deployment/wsgi/index` for more details. .. _more about packages: http://docs.python.org/tutorial/modules.html#packages diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 0d8b5bfd56..01c9089028 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -67,7 +67,7 @@ A list of strings representing the host/domain names that this Django site can serve. This is a security measure to prevent an attacker from poisoning caches and password reset emails with links to malicious hosts by submitting requests with a fake HTTP ``Host`` header, which is possible even under many -seemingly-safe webserver configurations. +seemingly-safe web server configurations. Values in this list can be fully qualified names (e.g. ``'www.example.com'``), in which case they will be matched against the request's ``Host`` header @@ -1265,9 +1265,9 @@ see the current list of translated languages by looking in .. _online source: https://github.com/django/django/blob/master/django/conf/global_settings.py -The list is a tuple of two-tuples in the format -(:term:`language code`, ``language name``) -- for example, -``('ja', 'Japanese')``. +The list is a tuple of two-tuples in the format +(:term:`language code`, ``language name``) -- for example, +``('ja', 'Japanese')``. This specifies which languages are available for language selection. See :doc:`/topics/i18n/index`. diff --git a/docs/ref/views.txt b/docs/ref/views.txt index 3753f83f07..8c9c3e3ed8 100644 --- a/docs/ref/views.txt +++ b/docs/ref/views.txt @@ -18,7 +18,7 @@ convenience, you'd like to have Django serve for you in local development. The :func:`~django.views.static.serve` view can be used to serve any directory you give it. (This view is **not** hardened for production use and should be used only as a development aid; you should serve these files in production -using a real front-end webserver). +using a real front-end web server). The most likely example is user-uploaded content in :setting:`MEDIA_ROOT`. ``django.contrib.staticfiles`` is intended for static assets and has no diff --git a/docs/topics/security.txt b/docs/topics/security.txt index 566202eefa..22135a72ea 100644 --- a/docs/topics/security.txt +++ b/docs/topics/security.txt @@ -168,7 +168,7 @@ certain cases. While these values are sanitized to prevent Cross Site Scripting attacks, a fake ``Host`` value can be used for Cross-Site Request Forgery, cache poisoning attacks, and poisoning links in emails. -Because even seemingly-secure webserver configurations are susceptible to fake +Because even seemingly-secure web server configurations are susceptible to fake ``Host`` headers, Django validates ``Host`` headers against the :setting:`ALLOWED_HOSTS` setting in the :meth:`django.http.HttpRequest.get_host()` method. @@ -181,15 +181,15 @@ For more details see the full :setting:`ALLOWED_HOSTS` documentation. .. warning:: - Previous versions of this document recommended configuring your webserver to + Previous versions of this document recommended configuring your web server to ensure it validates incoming HTTP ``Host`` headers. While this is still - recommended, in many common webservers a configuration that seems to + recommended, in many common web servers a configuration that seems to validate the ``Host`` header may not in fact do so. For instance, even if Apache is configured such that your Django site is served from a non-default virtual host with the ``ServerName`` set, it is still possible for an HTTP request to match this virtual host and supply a fake ``Host`` header. Thus, Django now requires that you set :setting:`ALLOWED_HOSTS` explicitly rather - than relying on webserver configuration. + than relying on web server configuration. Additionally, as of 1.3.1, Django requires you to explicitly enable support for the ``X-Forwarded-Host`` header (via the :setting:`USE_X_FORWARDED_HOST` From cee96b87c04d365c7649726da86d86cb655b05da Mon Sep 17 00:00:00 2001 From: Ana Krivokapic Date: Tue, 30 Apr 2013 18:38:59 +0200 Subject: [PATCH 29/70] Fix two typos in database transactions docs --- docs/topics/db/transactions.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt index e6f20c4255..255584c68b 100644 --- a/docs/topics/db/transactions.txt +++ b/docs/topics/db/transactions.txt @@ -74,8 +74,8 @@ To disable this behavior for a specific view, you must set the In practice, this feature simply wraps every view function in the :func:`atomic` decorator described below. -Note that only the execution of your view in enclosed in the transactions. -Middleware run outside of the transaction, and so does the rendering of +Note that only the execution of your view is enclosed in the transactions. +Middleware runs outside of the transaction, and so does the rendering of template responses. .. versionchanged:: 1.6 From ce45240df4e2b424ff4852b1cc71d01a031457c9 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 30 Apr 2013 21:39:41 +0200 Subject: [PATCH 30/70] Fixed #20336 -- Removed obsolete paragraph from the docs. Thanks Baptiste Mispelon. --- docs/ref/templates/builtins.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index e0800037df..287fd4f59e 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -657,11 +657,6 @@ The arguments can be hard-coded strings, so the following is valid:: ... {% endifequal %} -It is only possible to compare an argument to template variables or strings. -You cannot check for equality with Python objects such as ``True`` or -``False``. If you need to test if something is true or false, use the -:ttag:`if` tag instead. - An alternative to the ``ifequal`` tag is to use the :ttag:`if` tag and the ``==`` operator. From a5becad9094e5c5403b692b9a7b3a6ffaabf64a3 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Wed, 3 Apr 2013 12:42:33 +0200 Subject: [PATCH 31/70] Fixed #19252 -- Added support for wheel packages. Signed-off-by: Jannis Leidel --- MANIFEST.in | 2 + django/bin/__init__.py | 0 docs/internals/howto-release-django.txt | 13 ++-- extras/Makefile | 9 +++ setup.cfg | 5 ++ setup.py | 96 ++++++++++++------------- 6 files changed, 69 insertions(+), 56 deletions(-) delete mode 100644 django/bin/__init__.py create mode 100644 extras/Makefile diff --git a/MANIFEST.in b/MANIFEST.in index 0e0aba1268..e9c5a409be 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,7 +10,9 @@ recursive-include docs * recursive-include scripts * recursive-include extras * recursive-include tests * +recursive-include django/conf/app_template * recursive-include django/conf/locale * +recursive-include django/conf/project_template * recursive-include django/contrib/*/locale * recursive-include django/contrib/admin/templates * recursive-include django/contrib/admin/static * diff --git a/django/bin/__init__.py b/django/bin/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/internals/howto-release-django.txt b/docs/internals/howto-release-django.txt index a49251da76..fd985ddafc 100644 --- a/docs/internals/howto-release-django.txt +++ b/docs/internals/howto-release-django.txt @@ -175,13 +175,13 @@ OK, this is the fun part, where we actually push out a release! #. Make sure you have an absolutely clean tree by running ``git clean -dfx``. -#. Run ``python setup.py sdist`` to generate the release package. This will - create the release package in a ``dist/`` directory. +#. Run ``make -f extras/Makefile`` to generate the release packages. This will + create the release packages in a ``dist/`` directory. -#. Generate the hashes of the release package:: +#. Generate the hashes of the release packages:: - $ md5sum dist/Django-.tar.gz - $ sha1sum dist/Django-.tar.gz + $ md5sum dist/Django-* + $ sha1sum dist/Django-* *FIXME: perhaps we should switch to sha256?* @@ -221,6 +221,9 @@ Now you're ready to actually put the release out there. To do this: $ mktmpenv $ pip install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1.tar.gz $ deactivate + $ mktmpenv + $ pip install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1-py2.py3-none-any.whl + $ deactivate This just tests that the tarballs are available (i.e. redirects are up) and that they install correctly, but it'll catch silly mistakes. diff --git a/extras/Makefile b/extras/Makefile new file mode 100644 index 0000000000..ff14f404e2 --- /dev/null +++ b/extras/Makefile @@ -0,0 +1,9 @@ +all: sdist bdist_wheel + +sdist: + python setup.py sdist + +bdist_wheel: + python -c "import setuptools;__file__='setup.py';exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" bdist_wheel + +.PHONY : sdist bdist_wheel diff --git a/setup.cfg b/setup.cfg index e189ffb47b..330eff6977 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,3 +2,8 @@ doc_files = docs extras AUTHORS INSTALL LICENSE README.rst install-script = scripts/rpm-install.sh +[metadata] +license-file = LICENSE + +[wheel] +universal = 1 # use py2.py3 tag for pure-python dist diff --git a/setup.py b/setup.py index 006cba4137..7f848a56ff 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,9 @@ -from distutils.core import setup -from distutils.command.install_data import install_data -from distutils.command.install import INSTALL_SCHEMES -from distutils.sysconfig import get_python_lib import os import sys +from distutils.core import setup +from distutils.sysconfig import get_python_lib + # Warn if we are installing over top of an existing installation. This can # cause issues where files that were deleted from a more recent Django are # still present in site-packages. See #18115. @@ -23,28 +22,11 @@ if "install" in sys.argv: overlay_warning = True break -class osx_install_data(install_data): - # On MacOS, the platform-specific lib dir is /System/Library/Framework/Python/.../ - # which is wrong. Python 2.5 supplied with MacOS 10.5 has an Apple-specific fix - # for this in distutils.command.install_data#306. It fixes install_lib but not - # install_data, which is why we roll our own install_data class. - - def finalize_options(self): - # By the time finalize_options is called, install.install_lib is set to the - # fixed directory, so we set the installdir to install_lib. The - # install_data class uses ('install_data', 'install_dir') instead. - self.set_undefined_options('install', ('install_lib', 'install_dir')) - install_data.finalize_options(self) - -if sys.platform == "darwin": - cmdclasses = {'install_data': osx_install_data} -else: - cmdclasses = {'install_data': install_data} def fullsplit(path, result=None): """ - Split a pathname into components (the opposite of os.path.join) in a - platform-neutral way. + Split a pathname into components (the opposite of os.path.join) + in a platform-neutral way. """ if result is None: result = [] @@ -55,15 +37,23 @@ def fullsplit(path, result=None): return result return fullsplit(head, [tail] + result) -# Tell distutils not to put the data_files in platform-specific installation -# locations. See here for an explanation: -# http://groups.google.com/group/comp.lang.python/browse_thread/thread/35ec7b2fed36eaec/2105ee4d9e8042cb -for scheme in INSTALL_SCHEMES.values(): - scheme['data'] = scheme['purelib'] + +EXCLUDE_FROM_PACKAGES = ['django.conf.project_template', + 'django.conf.app_template', + 'django.bin'] + + +def is_package(package_name): + for pkg in EXCLUDE_FROM_PACKAGES: + if package_name.startswith(pkg): + return False + return True + # Compile the list of packages available, because distutils doesn't have # an easy way to do this. -packages, data_files = [], [] +packages, package_data = [], {} + root_dir = os.path.dirname(__file__) if root_dir != '': os.chdir(root_dir) @@ -72,33 +62,37 @@ django_dir = 'django' for dirpath, dirnames, filenames in os.walk(django_dir): # Ignore PEP 3147 cache dirs and those whose names start with '.' dirnames[:] = [d for d in dirnames if not d.startswith('.') and d != '__pycache__'] - if '__init__.py' in filenames: - packages.append('.'.join(fullsplit(dirpath))) + parts = fullsplit(dirpath) + package_name = '.'.join(parts) + if '__init__.py' in filenames and is_package(package_name): + packages.append(package_name) elif filenames: - data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]]) + relative_path = [] + while '.'.join(parts) not in packages: + relative_path.append(parts.pop()) + relative_path.reverse() + path = os.path.join(*relative_path) + package_files = package_data.setdefault('.'.join(parts), []) + package_files.extend([os.path.join(path, f) for f in filenames]) -# Small hack for working with bdist_wininst. -# See http://mail.python.org/pipermail/distutils-sig/2004-August/004134.html -if len(sys.argv) > 1 and sys.argv[1] == 'bdist_wininst': - for file_info in data_files: - file_info[0] = '\\PURELIB\\%s' % file_info[0] # Dynamically calculate the version based on django.VERSION. version = __import__('django').get_version() + setup( - name = "Django", - version = version, - url = 'http://www.djangoproject.com/', - author = 'Django Software Foundation', - author_email = 'foundation@djangoproject.com', - description = 'A high-level Python Web framework that encourages rapid development and clean, pragmatic design.', - license = "BSD", - packages = packages, - cmdclass = cmdclasses, - data_files = data_files, - scripts = ['django/bin/django-admin.py'], - classifiers = [ + name='Django', + version=version, + url='http://www.djangoproject.com/', + author='Django Software Foundation', + author_email='foundation@djangoproject.com', + description=('A high-level Python Web framework that encourages ' + 'rapid development and clean, pragmatic design.'), + license='BSD', + packages=packages, + package_data=package_data, + scripts=['django/bin/django-admin.py'], + classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', @@ -115,7 +109,7 @@ setup( 'Topic :: Internet :: WWW/HTTP :: WSGI', 'Topic :: Software Development :: Libraries :: Application Frameworks', 'Topic :: Software Development :: Libraries :: Python Modules', - ], + ], ) if overlay_warning: @@ -136,4 +130,4 @@ should manually remove the directory and re-install Django. -""" % { "existing_path": existing_path }) +""" % {"existing_path": existing_path}) From 780fa48f5fb81b2f0f58de95167abff84a6149aa Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Wed, 1 May 2013 16:40:49 +0200 Subject: [PATCH 32/70] Fixed test failures introduced in a5becad9094e5c5403b692b9a7b3a6ffaabf64a3. --- tests/admin_scripts/tests.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index baec16820e..5071977b2d 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -14,7 +14,8 @@ import subprocess import sys import codecs -from django import conf, bin, get_version +import django +from django import conf, get_version from django.conf import settings from django.core.management import BaseCommand, CommandError from django.db import connection @@ -149,8 +150,8 @@ class AdminScriptTestCase(unittest.TestCase): return out, err def run_django_admin(self, args, settings_file=None): - bin_dir = os.path.abspath(os.path.dirname(upath(bin.__file__))) - return self.run_test(os.path.join(bin_dir, 'django-admin.py'), args, settings_file) + script_dir = os.path.abspath(os.path.join(os.path.dirname(upath(django.__file__)), 'bin')) + return self.run_test(os.path.join(script_dir, 'django-admin.py'), args, settings_file) def run_manage(self, args, settings_file=None): def safe_remove(path): From 6bdeed1b811ddf9a920e925ad05d82cffbf13c3a Mon Sep 17 00:00:00 2001 From: Michael Manfre Date: Wed, 1 May 2013 12:13:09 -0600 Subject: [PATCH 33/70] Fixed #20338 - Document FQDN behavior with ALLOWED_HOSTS --- docs/ref/settings.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 01c9089028..f2d418d4d9 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -79,6 +79,20 @@ responsible to provide your own validation of the ``Host`` header (perhaps in a middleware; if so this middleware must be listed first in :setting:`MIDDLEWARE_CLASSES`). +.. note:: + + If you want to also allow the `fully qualified domain name (FQDN)`_, which + some browsers can send in the Host header, you must explicitly add another + ALLOWED_HOSTS entry that includes a trailing period. This entry can also be + a subdomain wildcard:: + + ALLOWED_HOSTS = [ + '.example.com', # Allow domain and subdomains + '.example.com.', # Also allow FQDN and subdomains + ] + +.. _`fully qualified domain name (FQDN)`: http://en.wikipedia.org/wiki/Fully_qualified_domain_name + If the ``Host`` header (or ``X-Forwarded-Host`` if :setting:`USE_X_FORWARDED_HOST` is enabled) does not match any value in this list, the :meth:`django.http.HttpRequest.get_host()` method will raise From 9f7a01ef2bed8c0395a970286e8f87fd7d344b3b Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 2 May 2013 16:16:25 +0200 Subject: [PATCH 34/70] Updated translation templates and removed en translations "en" translations have been mistakenly committed in 87cc3da81. --- django/conf/locale/en/LC_MESSAGES/django.po | 89 +++-- .../admin/locale/en/LC_MESSAGES/django.mo | Bin 14815 -> 356 bytes .../admin/locale/en/LC_MESSAGES/django.po | 366 ++++++++---------- .../admin/locale/en/LC_MESSAGES/djangojs.mo | Bin 3400 -> 356 bytes .../admin/locale/en/LC_MESSAGES/djangojs.po | 125 +++--- .../admindocs/locale/en/LC_MESSAGES/django.mo | Bin 3564 -> 356 bytes .../admindocs/locale/en/LC_MESSAGES/django.po | 82 ++-- .../auth/locale/en/LC_MESSAGES/django.mo | Bin 5464 -> 356 bytes .../auth/locale/en/LC_MESSAGES/django.po | 246 +++++------- .../comments/locale/en/LC_MESSAGES/django.mo | Bin 4854 -> 356 bytes .../comments/locale/en/LC_MESSAGES/django.po | 158 +++----- .../locale/en/LC_MESSAGES/django.mo | Bin 988 -> 356 bytes .../locale/en/LC_MESSAGES/django.po | 21 +- .../flatpages/locale/en/LC_MESSAGES/django.mo | Bin 2012 -> 356 bytes .../flatpages/locale/en/LC_MESSAGES/django.po | 43 +- .../formtools/locale/en/LC_MESSAGES/django.mo | Bin 746 -> 356 bytes .../formtools/locale/en/LC_MESSAGES/django.po | 22 +- .../gis/locale/en/LC_MESSAGES/django.mo | Bin 1678 -> 356 bytes .../gis/locale/en/LC_MESSAGES/django.po | 43 +- .../humanize/locale/en/LC_MESSAGES/django.mo | Bin 3992 -> 356 bytes .../humanize/locale/en/LC_MESSAGES/django.po | 165 ++++---- .../messages/locale/en/LC_MESSAGES/django.mo | Bin 472 -> 356 bytes .../messages/locale/en/LC_MESSAGES/django.po | 13 +- .../redirects/locale/en/LC_MESSAGES/django.mo | Bin 988 -> 356 bytes .../redirects/locale/en/LC_MESSAGES/django.po | 21 +- .../sessions/locale/en/LC_MESSAGES/django.mo | Bin 642 -> 356 bytes .../sessions/locale/en/LC_MESSAGES/django.po | 19 +- .../sites/locale/en/LC_MESSAGES/django.mo | Bin 574 -> 356 bytes .../sites/locale/en/LC_MESSAGES/django.po | 17 +- 29 files changed, 618 insertions(+), 812 deletions(-) diff --git a/django/conf/locale/en/LC_MESSAGES/django.po b/django/conf/locale/en/LC_MESSAGES/django.po index 2980c02ef7..4c94d5a00a 100644 --- a/django/conf/locale/en/LC_MESSAGES/django.po +++ b/django/conf/locale/en/LC_MESSAGES/django.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" +"POT-Creation-Date: 2013-05-02 16:17+0200\n" "PO-Revision-Date: 2010-05-13 15:35+0200\n" "Last-Translator: Django team\n" "Language-Team: English \n" @@ -337,7 +337,7 @@ msgstr "" msgid "Enter a valid value." msgstr "" -#: core/validators.py:53 forms/fields.py:627 +#: core/validators.py:53 forms/fields.py:640 msgid "Enter a valid URL." msgstr "" @@ -408,7 +408,7 @@ msgstr[1] "" msgid "%(field_name)s must be unique for %(date_field)s %(lookup)s." msgstr "" -#: db/models/base.py:905 forms/models.py:590 +#: db/models/base.py:905 forms/models.py:605 msgid "and" msgstr "" @@ -630,46 +630,54 @@ msgstr "" msgid "Enter a number." msgstr "" -#: forms/fields.py:295 -#, python-format -msgid "Ensure that there are no more than %s digits in total." -msgstr "" - #: forms/fields.py:296 #, python-format -msgid "Ensure that there are no more than %s decimal places." -msgstr "" +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" -#: forms/fields.py:297 +#: forms/fields.py:300 #, python-format -msgid "Ensure that there are no more than %s digits before the decimal point." -msgstr "" +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" -#: forms/fields.py:393 forms/fields.py:1045 +#: forms/fields.py:304 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" + +#: forms/fields.py:406 forms/fields.py:1058 msgid "Enter a valid date." msgstr "" -#: forms/fields.py:417 forms/fields.py:1046 +#: forms/fields.py:430 forms/fields.py:1059 msgid "Enter a valid time." msgstr "" -#: forms/fields.py:438 +#: forms/fields.py:451 msgid "Enter a valid date/time." msgstr "" -#: forms/fields.py:512 +#: forms/fields.py:525 msgid "No file was submitted. Check the encoding type on the form." msgstr "" -#: forms/fields.py:513 +#: forms/fields.py:526 msgid "No file was submitted." msgstr "" -#: forms/fields.py:514 +#: forms/fields.py:527 msgid "The submitted file is empty." msgstr "" -#: forms/fields.py:516 +#: forms/fields.py:529 #, python-format msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." msgid_plural "" @@ -677,22 +685,22 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: forms/fields.py:519 +#: forms/fields.py:532 msgid "Please either submit a file or check the clear checkbox, not both." msgstr "" -#: forms/fields.py:580 +#: forms/fields.py:593 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: forms/fields.py:733 forms/fields.py:811 +#: forms/fields.py:746 forms/fields.py:824 forms/models.py:1042 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "" -#: forms/fields.py:812 forms/fields.py:915 forms/models.py:1026 +#: forms/fields.py:825 forms/fields.py:928 forms/models.py:1041 msgid "Enter a list of values." msgstr "" @@ -714,43 +722,38 @@ msgstr "" msgid "Delete" msgstr "" -#: forms/models.py:584 +#: forms/models.py:599 #, python-format msgid "Please correct the duplicate data for %(field)s." msgstr "" -#: forms/models.py:588 +#: forms/models.py:603 #, python-format msgid "Please correct the duplicate data for %(field)s, which must be unique." msgstr "" -#: forms/models.py:594 +#: forms/models.py:609 #, python-format msgid "" "Please correct the duplicate data for %(field_name)s which must be unique " "for the %(lookup)s in %(date_field)s." msgstr "" -#: forms/models.py:602 +#: forms/models.py:617 msgid "Please correct the duplicate values below." msgstr "" -#: forms/models.py:868 +#: forms/models.py:883 msgid "The inline foreign key did not match the parent instance primary key." msgstr "" -#: forms/models.py:932 +#: forms/models.py:947 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "" -#: forms/models.py:1027 +#: forms/models.py:1044 #, python-format -msgid "Select a valid choice. %s is not one of the available choices." -msgstr "" - -#: forms/models.py:1029 -#, python-format -msgid "\"%s\" is not a valid value for a primary key." +msgid "\"%(pk)s\" is not a valid value for a primary key." msgstr "" #: forms/util.py:84 @@ -760,27 +763,27 @@ msgid "" "may be ambiguous or it may not exist." msgstr "" -#: forms/widgets.py:326 +#: forms/widgets.py:327 msgid "Currently" msgstr "" -#: forms/widgets.py:327 +#: forms/widgets.py:328 msgid "Change" msgstr "" -#: forms/widgets.py:328 +#: forms/widgets.py:329 msgid "Clear" msgstr "" -#: forms/widgets.py:546 +#: forms/widgets.py:547 msgid "Unknown" msgstr "" -#: forms/widgets.py:547 +#: forms/widgets.py:548 msgid "Yes" msgstr "" -#: forms/widgets.py:548 +#: forms/widgets.py:549 msgid "No" msgstr "" diff --git a/django/contrib/admin/locale/en/LC_MESSAGES/django.mo b/django/contrib/admin/locale/en/LC_MESSAGES/django.mo index 7b60cfe7a9f054f009a258a7de625abdc588c768..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 173 zcmca#{DjHko)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$+F5mA*Q+pMhb>z zR)!YZ21W)3Tmk;NL8)b##hLkex-N+&sa6U`28IT@2Bx}(#tMd}R>r0fbv}v3CAuL+ viFw62i6!|(RthdziFxVy3MHwDxsxv|tMK>$xuuEeKm$_qCVx|oW@G>W+C?Sx literal 14815 zcmeI1dyHJwdB6__3^4?VA>iggj~y?&i+9GmHV*FKCu;)^Ufc1mZ3rQoyEAuouJ_Kp z<9qMydPq&21Of@In*b%x-Kq&{X|ruJ)VXJ@?-8`p)-#=dnLtde(8p^$7A7#A>ly*D`kHBxkJ7DP|rD(Ex3SJ7Ihf@Dfq2&7o zTnc~fcrJ}5t9mmu?Sm^wuZ64NekkqwAe8Tp!4dc%QV8kHeMlY?KwkE8!w|Ka}=;2A&1K01v<~LCOCL zl<`|kr{uefq0u9h_p6}jaSasx?1S?Cd!e+q4yB&=K^f=Up|tZ8Q0jXWN`JlzCEvGP z`g>5u{RfaCR6mAN@4v%0!B?Qa(cfyCEjBmeI>L$1bNHo^7qdbkcAg<=n1hceF3LMi9(T>77(=;H*GcKr&99{$6b0Gy-@o5D3to2gj?WW zLn-gfA=}>-a4qR|P}&o~eXs!;TJ=2`!e78e@TP)o-!1Sg(q(u6)}Z8n8lDfIfzpo` zq0ysze*%ggUxuQeON;jV0A-vvLm8KGDD_q01#kvRJ-4~^UGQAepM(+@ zKM$qeC!qMl)9?cL4^Z^|b0~JZXtj+;mqSUfhp&SZ@I3e)xCI`B5*NP)B`#`$!d>tX zEW!VT5*M$+2&A4FDEhb!Nicj4o`F-3!TnI4e-TPt`~hsj6Yx$rg)(G({}oDHd>KkVc3y4mf4}3q zq3CN0ioG0y7s3`4{d@#U|BgZa)C2q+g)czq=LEq<{Qp)c_TxcJT*dGpd;m&ZT>K}t zpW7fVrY522`EJMipp^3vl=ghlJ%18PyPtA=&OLtt5*E#v-VEP~1jvKPD6$0+JD2OT z7V2(C0ol#){&38bP8`k zKJC&E!w(~31D7E0N2LF|ksZi;5V<5~F@$O}B6cFzZHSLxI%*opuL3t;Kv2EWOX zclUn*KZA(<9zu>FF(TJaWFN8;Nf7aYdyxZ(jO#UsjI;P&e#y9H$Sm?k_RGtTpzMfp5wKSm&1=BVxJ#$ z&z8d(Kt`r4N68>j|h<|mU`kcIwW zs#V`WIc`Q7w|xVVSC?mVUaHGAFPio%I&GHAewtRBVc063^kYBqPye-_P-fz%%`nRg z=z5g$OiG`$Urgq$$>Pild1u?yzR~g2LBE{oxge|QMiSJ$q@`zk=^4L+iLS(cs-rm5 z{^20aq{*hFcDbn^lHaeWtzk%}Q@rd;wO6SJ5k(|k7Q~Sn*cC_qpc>Y3m273Bu})cK z^UYR+lG<9K_)3NET8p`5xV`=o_%T9ZXjgB?Hg$ zA{V|4L(`#(m-*BawY1!+t&B}8ZZeAQX3mQ;oyEF>7BXMc5=yj{xVoiaKP`I=zcSgb zeX|yA(R0aAjl(dW3!-TTHKdk`4l=)v*#vwu<-4*gsNwghJy&778L4Z>DXO$hVsxU4 zG$u%1O(Qm|Q7;nPaNW<9sYZ>pJLtYNzpF9+andSvJ*u~K!%Ne-IH}}cEl8K#^Z5g) z3v|(XRXUhgAmbTQ2M$t=6B5n?=a0N?9p9!!H5&R}qOI1%EJwqjJfkrv1~Ycg%_y}M zRZij*eK9r-ZyMtmjiYLi)Z4wFxy>Z;qbzKd)HW~kro7bGev-tAB>mM{Q1{IpuZ`I_ z=sF%9G(z9`WST$k8q;pOo@|88#0%-XEoQUhJUtaxw4a_r8z!?@-^q-(|96eIU(dzO zu!5Z&Y6hsujD4QJW6O;s&P-rv8vf)Ep}U;6Mu~Ykh&pxV9^=X$(o@Y$JT%c> z*+f1QzoPz3*SuL@H~gd?q$vZvz-c?}&RPuu9Wr?=~Z8rtYS5Uu->q9_3uclbqmMaXnqprE5xQ%d$dZv9hKJL73Q*g<>Ko=e2dOdirT79hLLqJ(rj(`=}*m>83a-^&QL!=6KiAxLmx}VN8u($Qy-Jfuk#ByqH;eJgaH`z|y?mh59%8>) z7FAj8RYmRY8ah18FTKEW(@>{@LrH7?Q}33N6WlriU6 z&q>xu8=r5PMV&-`S$m10xt7Wr{dWtDy9~ibj?^zVlOSvLrv#W8!iif>MJ{gq0oy_N z#q~s+ojFSuBBsi6EoRlbAht59b|gdNde-NdDgClXtg!(oB{IvbSR*3(ha1f6m7?0~ zmr-NevG)3PLM2ZHx|+mw^q5E-;TZ<2yO(l%o7P*~fx`v~HO@3_eoT9r6QEtd%NyH? zIHJ%d)<*D@((M~|A^w~iXSr6c0oirPGO~RqYOzr!e32i=7=w&d0(%L&DwM!X0d~gGW0;QJTFCGc-jbLd8B0dleAo;iWO>pRR}){h4A$c6^9pEZ z3S_(JRm_|;)iSozUARm*MKx}RQuctcm@;ehf&6|lYSyP@kEzDHwy*idgb(c|Q&!(* z>oUj;H^CNQ#?3YjKdQ)dlcE!SsOg22uVoo*bt1hz;7@6@fXHHk&%Er-RPxH2PCi&p z$z79xUD>mtGrMf;2^a1}Hu2wA_qo2A?VW^{F4Lr2ZXaRh*VkO6SquDn$d=f7_$e7y4@ZWOfUu7$X_pd^MfX zjs;VpOa?ZfpiWv7CPYqv9a5(wo?lSn{XMsd~WjnlZmw zl5O>~lJSFCi6>Y|J_vH3i)^$|kwHK7myyet_PwXxrUjb=WK79jS-eIarI(fz1jkV$-Sh=)WLKsU0 zuZ9npS=CMc4CTVyUB{E|mCOhd@rKM&$g_(C86%o!w=E@Q=W|^Rz3Kek9Bg3j%7zSG zXHmh*L`l_HOcJ2o{Jy1}K?jxm4!26wo+Os@$ij|FVZWcqnxv%d{&LA)<93CybULUM zu5C`Ig^9SNm+aX+Q5cn3kZ%iez*5p{hDX*GhDZ3*BX2K_46hm<86M`t!d`zikTMs3 zw?+@IFRfd%YM5)uPS(JMiNuRiCgwOP>AhY(NMbz}bK1h>yP=$T_4@FZY1w)eF`P|g ziKd%$wJ<^0DzQJC<`k;NdLr5q21S&%Y021*vF)8MjTDEMaBw7!RG47hS<;z*I9uHa z*$!PJUY#(M8~06IS6JWqPMTEplfw3>9Ls!K((9*!tX+8tB`?`y&V&l=FY9SZNA`#) z-MHo&ZErUYM0)u~Ju-OBlHLLKf^x$;_Jj1^0e0^IyLW)yK2q)-V6*hi&+kuv*8Cce diff --git a/django/contrib/admin/locale/en/LC_MESSAGES/django.po b/django/contrib/admin/locale/en/LC_MESSAGES/django.po index 53f64941f4..964b0a509b 100644 --- a/django/contrib/admin/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/admin/locale/en/LC_MESSAGES/django.po @@ -1,75 +1,72 @@ # This file is distributed under the same license as the Django package. # -# Translators: -# Jannis Leidel , 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2013-01-02 08:52+0000\n" -"Last-Translator: Ramiro Morales \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: actions.py:48 +#: actions.py:49 #, python-format msgid "Successfully deleted %(count)d %(items)s." -msgstr "Successfully deleted %(count)d %(items)s." +msgstr "" -#: actions.py:60 options.py:1361 +#: actions.py:61 options.py:1365 #, python-format msgid "Cannot delete %(name)s" -msgstr "Cannot delete %(name)s" +msgstr "" -#: actions.py:62 options.py:1363 +#: actions.py:63 options.py:1367 msgid "Are you sure?" -msgstr "Are you sure?" +msgstr "" -#: actions.py:83 +#: actions.py:84 #, python-format msgid "Delete selected %(verbose_name_plural)s" -msgstr "Delete selected %(verbose_name_plural)s" +msgstr "" #: filters.py:101 filters.py:197 filters.py:237 filters.py:274 filters.py:380 msgid "All" -msgstr "All" +msgstr "" #: filters.py:238 msgid "Yes" -msgstr "Yes" +msgstr "" #: filters.py:239 msgid "No" -msgstr "No" +msgstr "" #: filters.py:253 msgid "Unknown" -msgstr "Unknown" +msgstr "" #: filters.py:308 msgid "Any date" -msgstr "Any date" +msgstr "" #: filters.py:309 msgid "Today" -msgstr "Today" +msgstr "" #: filters.py:313 msgid "Past 7 days" -msgstr "Past 7 days" +msgstr "" #: filters.py:317 msgid "This month" -msgstr "This month" +msgstr "" #: filters.py:321 msgid "This year" -msgstr "This year" +msgstr "" #: forms.py:9 #, python-format @@ -77,102 +74,99 @@ msgid "" "Please enter the correct %(username)s and password for a staff account. Note " "that both fields may be case-sensitive." msgstr "" -"Please enter the correct %(username)s and password for a staff account. Note " -"that both fields may be case-sensitive." #: forms.py:19 msgid "Please log in again, because your session has expired." -msgstr "Please log in again, because your session has expired." +msgstr "" #: helpers.py:23 msgid "Action:" -msgstr "Action:" +msgstr "" #: models.py:25 msgid "action time" -msgstr "action time" +msgstr "" #: models.py:28 msgid "object id" -msgstr "object id" +msgstr "" #: models.py:29 msgid "object repr" -msgstr "object repr" +msgstr "" #: models.py:30 msgid "action flag" -msgstr "action flag" +msgstr "" #: models.py:31 msgid "change message" -msgstr "change message" +msgstr "" #: models.py:36 msgid "log entry" -msgstr "log entry" +msgstr "" #: models.py:37 msgid "log entries" -msgstr "log entries" +msgstr "" #: models.py:46 #, python-format msgid "Added \"%(object)s\"." -msgstr "Added \"%(object)s\"." +msgstr "" #: models.py:48 #, python-format msgid "Changed \"%(object)s\" - %(changes)s" -msgstr "Changed \"%(object)s\" - %(changes)s" +msgstr "" #: models.py:53 #, python-format msgid "Deleted \"%(object)s.\"" -msgstr "Deleted \"%(object)s.\"" +msgstr "" #: models.py:55 msgid "LogEntry Object" -msgstr "LogEntry Object" +msgstr "" #: options.py:163 options.py:192 msgid "None" -msgstr "None" +msgstr "" #: options.py:710 #, python-format msgid "Changed %s." -msgstr "Changed %s." +msgstr "" -#: options.py:710 options.py:720 options.py:1510 +#: options.py:710 options.py:720 options.py:1514 msgid "and" -msgstr "and" +msgstr "" #: options.py:715 #, python-format msgid "Added %(name)s \"%(object)s\"." -msgstr "Added %(name)s \"%(object)s\"." +msgstr "" #: options.py:719 #, python-format msgid "Changed %(list)s for %(name)s \"%(object)s\"." -msgstr "Changed %(list)s for %(name)s \"%(object)s\"." +msgstr "" #: options.py:724 #, python-format msgid "Deleted %(name)s \"%(object)s\"." -msgstr "Deleted %(name)s \"%(object)s\"." +msgstr "" #: options.py:728 msgid "No fields changed." -msgstr "No fields changed." +msgstr "" #: options.py:831 options.py:874 #, python-format msgid "" "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." msgstr "" -"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." #: options.py:849 #, python-format @@ -180,13 +174,11 @@ msgid "" "The %(name)s \"%(obj)s\" was added successfully. You may add another " "%(name)s below." msgstr "" -"The %(name)s \"%(obj)s\" was added successfully. You may add another " -"%(name)s below." #: options.py:853 #, python-format msgid "The %(name)s \"%(obj)s\" was added successfully." -msgstr "The %(name)s \"%(obj)s\" was added successfully." +msgstr "" #: options.py:867 #, python-format @@ -194,8 +186,6 @@ msgid "" "The %(name)s \"%(obj)s\" was changed successfully. You may edit it again " "below." msgstr "" -"The %(name)s \"%(obj)s\" was changed successfully. You may edit it again " -"below." #: options.py:881 #, python-format @@ -203,135 +193,129 @@ msgid "" "The %(name)s \"%(obj)s\" was changed successfully. You may add another " "%(name)s below." msgstr "" -"The %(name)s \"%(obj)s\" was changed successfully. You may add another " -"%(name)s below." #: options.py:887 #, python-format msgid "The %(name)s \"%(obj)s\" was changed successfully." -msgstr "The %(name)s \"%(obj)s\" was changed successfully." +msgstr "" #: options.py:965 options.py:1225 msgid "" "Items must be selected in order to perform actions on them. No items have " "been changed." msgstr "" -"Items must be selected in order to perform actions on them. No items have " -"been changed." #: options.py:984 msgid "No action selected." -msgstr "No action selected." +msgstr "" #: options.py:1064 #, python-format msgid "Add %s" -msgstr "Add %s" +msgstr "" #: options.py:1088 options.py:1333 #, python-format msgid "%(name)s object with primary key %(key)r does not exist." -msgstr "%(name)s object with primary key %(key)r does not exist." +msgstr "" #: options.py:1154 #, python-format msgid "Change %s" -msgstr "Change %s" +msgstr "" #: options.py:1204 msgid "Database error" -msgstr "Database error" +msgstr "" #: options.py:1267 #, python-format msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." -msgstr[0] "%(count)s %(name)s was changed successfully." -msgstr[1] "%(count)s %(name)s were changed successfully." +msgstr[0] "" +msgstr[1] "" #: options.py:1294 #, python-format msgid "%(total_count)s selected" msgid_plural "All %(total_count)s selected" -msgstr[0] "%(total_count)s selected" -msgstr[1] "All %(total_count)s selected" +msgstr[0] "" +msgstr[1] "" #: options.py:1299 #, python-format msgid "0 of %(cnt)s selected" -msgstr "0 of %(cnt)s selected" +msgstr "" -#: options.py:1349 +#: options.py:1350 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." -msgstr "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "" -#: options.py:1402 +#: options.py:1406 #, python-format msgid "Change history: %s" -msgstr "Change history: %s" +msgstr "" #. Translators: Model verbose name and instance representation, suitable to be an item in a list -#: options.py:1504 +#: options.py:1508 #, python-format msgid "%(class_name)s %(instance)s" msgstr "" -#: options.py:1511 -#, fuzzy, python-format +#: options.py:1515 +#, python-format msgid "" "Deleting %(class_name)s %(instance)s would require deleting the following " "protected related objects: %(related_objects)s" msgstr "" -"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " -"following protected related objects:" #: sites.py:324 tests.py:71 templates/admin/login.html:48 #: templates/registration/password_reset_complete.html:19 #: views/decorators.py:24 msgid "Log in" -msgstr "Log in" +msgstr "" #: sites.py:392 msgid "Site administration" -msgstr "Site administration" +msgstr "" #: sites.py:446 #, python-format msgid "%s administration" -msgstr "%s administration" +msgstr "" #: widgets.py:90 msgid "Date:" -msgstr "Date:" +msgstr "" #: widgets.py:91 msgid "Time:" -msgstr "Time:" +msgstr "" #: widgets.py:165 msgid "Lookup" -msgstr "Lookup" +msgstr "" #: widgets.py:260 msgid "Add Another" -msgstr "Add Another" +msgstr "" #: widgets.py:302 msgid "Currently:" -msgstr "Currently:" +msgstr "" #: widgets.py:303 msgid "Change:" -msgstr "Change:" +msgstr "" #: templates/admin/404.html:4 templates/admin/404.html.py:8 msgid "Page not found" -msgstr "Page not found" +msgstr "" #: templates/admin/404.html:10 msgid "We're sorry, but the requested page could not be found." -msgstr "We're sorry, but the requested page could not be found." +msgstr "" #: templates/admin/500.html:6 templates/admin/app_index.html:7 #: templates/admin/base.html:47 templates/admin/change_form.html:19 @@ -348,63 +332,61 @@ msgstr "We're sorry, but the requested page could not be found." #: templates/registration/password_reset_done.html:6 #: templates/registration/password_reset_form.html:6 msgid "Home" -msgstr "Home" +msgstr "" #: templates/admin/500.html:7 msgid "Server error" -msgstr "Server error" +msgstr "" #: templates/admin/500.html:11 msgid "Server error (500)" -msgstr "Server error (500)" +msgstr "" #: templates/admin/500.html:14 msgid "Server Error (500)" -msgstr "Server Error (500)" +msgstr "" #: templates/admin/500.html:15 msgid "" "There's been an error. It's been reported to the site administrators via " "email and should be fixed shortly. Thanks for your patience." msgstr "" -"There's been an error. It's been reported to the site administrators via " -"email and should be fixed shortly. Thanks for your patience." #: templates/admin/actions.html:4 msgid "Run the selected action" -msgstr "Run the selected action" +msgstr "" #: templates/admin/actions.html:4 msgid "Go" -msgstr "Go" +msgstr "" #: templates/admin/actions.html:11 msgid "Click here to select the objects across all pages" -msgstr "Click here to select the objects across all pages" +msgstr "" #: templates/admin/actions.html:11 #, python-format msgid "Select all %(total_count)s %(module_name)s" -msgstr "Select all %(total_count)s %(module_name)s" +msgstr "" #: templates/admin/actions.html:13 msgid "Clear selection" -msgstr "Clear selection" +msgstr "" #: templates/admin/app_index.html:10 templates/admin/index.html:21 #, python-format msgid "%(name)s" -msgstr "%(name)s" +msgstr "" #: templates/admin/base.html:28 msgid "Welcome," -msgstr "Welcome," +msgstr "" #: templates/admin/base.html:33 #: templates/registration/password_change_done.html:3 #: templates/registration/password_change_form.html:4 msgid "Documentation" -msgstr "Documentation" +msgstr "" #: templates/admin/base.html:36 #: templates/admin/auth/user/change_password.html:17 @@ -412,35 +394,35 @@ msgstr "Documentation" #: templates/registration/password_change_done.html:3 #: templates/registration/password_change_form.html:4 msgid "Change password" -msgstr "Change password" +msgstr "" #: templates/admin/base.html:38 #: templates/registration/password_change_done.html:3 #: templates/registration/password_change_form.html:4 msgid "Log out" -msgstr "Log out" +msgstr "" #: templates/admin/base_site.html:4 msgid "Django site admin" -msgstr "Django site admin" +msgstr "" #: templates/admin/base_site.html:7 msgid "Django administration" -msgstr "Django administration" +msgstr "" #: templates/admin/change_form.html:22 templates/admin/index.html:33 msgid "Add" -msgstr "Add" +msgstr "" #: templates/admin/change_form.html:32 templates/admin/object_history.html:11 msgid "History" -msgstr "History" +msgstr "" #: templates/admin/change_form.html:33 #: templates/admin/edit_inline/stacked.html:9 #: templates/admin/edit_inline/tabular.html:30 msgid "View on site" -msgstr "View on site" +msgstr "" #: templates/admin/change_form.html:44 templates/admin/change_list.html:67 #: templates/admin/login.html:17 @@ -448,35 +430,35 @@ msgstr "View on site" #: templates/registration/password_change_form.html:20 msgid "Please correct the error below." msgid_plural "Please correct the errors below." -msgstr[0] "Please correct the error below." -msgstr[1] "Please correct the errors below." +msgstr[0] "" +msgstr[1] "" #: templates/admin/change_list.html:58 #, python-format msgid "Add %(name)s" -msgstr "Add %(name)s" +msgstr "" #: templates/admin/change_list.html:78 msgid "Filter" -msgstr "Filter" +msgstr "" #: templates/admin/change_list_results.html:17 msgid "Remove from sorting" -msgstr "Remove from sorting" +msgstr "" #: templates/admin/change_list_results.html:18 #, python-format msgid "Sorting priority: %(priority_number)s" -msgstr "Sorting priority: %(priority_number)s" +msgstr "" #: templates/admin/change_list_results.html:19 msgid "Toggle sorting" -msgstr "Toggle sorting" +msgstr "" #: templates/admin/delete_confirmation.html:11 #: templates/admin/submit_line.html:4 msgid "Delete" -msgstr "Delete" +msgstr "" #: templates/admin/delete_confirmation.html:18 #, python-format @@ -485,9 +467,6 @@ msgid "" "related objects, but your account doesn't have permission to delete the " "following types of objects:" msgstr "" -"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " -"related objects, but your account doesn't have permission to delete the " -"following types of objects:" #: templates/admin/delete_confirmation.html:26 #, python-format @@ -495,8 +474,6 @@ msgid "" "Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " "following protected related objects:" msgstr "" -"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " -"following protected related objects:" #: templates/admin/delete_confirmation.html:34 #, python-format @@ -504,17 +481,15 @@ msgid "" "Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " "All of the following related items will be deleted:" msgstr "" -"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " -"All of the following related items will be deleted:" #: templates/admin/delete_confirmation.html:39 #: templates/admin/delete_selected_confirmation.html:44 msgid "Yes, I'm sure" -msgstr "Yes, I'm sure" +msgstr "" #: templates/admin/delete_selected_confirmation.html:10 msgid "Delete multiple objects" -msgstr "Delete multiple objects" +msgstr "" #: templates/admin/delete_selected_confirmation.html:17 #, python-format @@ -523,9 +498,6 @@ msgid "" "objects, but your account doesn't have permission to delete the following " "types of objects:" msgstr "" -"Deleting the selected %(objects_name)s would result in deleting related " -"objects, but your account doesn't have permission to delete the following " -"types of objects:" #: templates/admin/delete_selected_confirmation.html:25 #, python-format @@ -533,8 +505,6 @@ msgid "" "Deleting the selected %(objects_name)s would require deleting the following " "protected related objects:" msgstr "" -"Deleting the selected %(objects_name)s would require deleting the following " -"protected related objects:" #: templates/admin/delete_selected_confirmation.html:33 #, python-format @@ -542,42 +512,40 @@ msgid "" "Are you sure you want to delete the selected %(objects_name)s? All of the " "following objects and their related items will be deleted:" msgstr "" -"Are you sure you want to delete the selected %(objects_name)s? All of the " -"following objects and their related items will be deleted:" #: templates/admin/filter.html:2 #, python-format msgid " By %(filter_title)s " -msgstr " By %(filter_title)s " +msgstr "" #: templates/admin/index.html:20 #, python-format msgid "Models in the %(name)s application" -msgstr "Models in the %(name)s application" +msgstr "" #: templates/admin/index.html:39 msgid "Change" -msgstr "Change" +msgstr "" #: templates/admin/index.html:49 msgid "You don't have permission to edit anything." -msgstr "You don't have permission to edit anything." +msgstr "" #: templates/admin/index.html:57 msgid "Recent Actions" -msgstr "Recent Actions" +msgstr "" #: templates/admin/index.html:58 msgid "My Actions" -msgstr "My Actions" +msgstr "" #: templates/admin/index.html:62 msgid "None available" -msgstr "None available" +msgstr "" #: templates/admin/index.html:76 msgid "Unknown content" -msgstr "Unknown content" +msgstr "" #: templates/admin/invalid_setup.html:12 msgid "" @@ -585,163 +553,154 @@ msgid "" "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" -"Something's wrong with your database installation. Make sure the appropriate " -"database tables have been created, and make sure the database is readable by " -"the appropriate user." #: templates/admin/login.html:37 msgid "Password:" -msgstr "Password:" +msgstr "" #: templates/admin/login.html:44 msgid "Forgotten your password or username?" -msgstr "Forgotten your password or username?" +msgstr "" #: templates/admin/object_history.html:23 msgid "Date/time" -msgstr "Date/time" +msgstr "" #: templates/admin/object_history.html:24 msgid "User" -msgstr "User" +msgstr "" #: templates/admin/object_history.html:25 msgid "Action" -msgstr "Action" +msgstr "" #: templates/admin/object_history.html:39 msgid "" "This object doesn't have a change history. It probably wasn't added via this " "admin site." msgstr "" -"This object doesn't have a change history. It probably wasn't added via this " -"admin site." #: templates/admin/pagination.html:10 msgid "Show all" -msgstr "Show all" +msgstr "" #: templates/admin/pagination.html:11 templates/admin/submit_line.html:3 msgid "Save" -msgstr "Save" +msgstr "" #: templates/admin/search_form.html:7 msgid "Search" -msgstr "Search" +msgstr "" #: templates/admin/search_form.html:9 #, python-format msgid "%(counter)s result" msgid_plural "%(counter)s results" -msgstr[0] "%(counter)s result" -msgstr[1] "%(counter)s results" +msgstr[0] "" +msgstr[1] "" #: templates/admin/search_form.html:9 #, python-format msgid "%(full_result_count)s total" -msgstr "%(full_result_count)s total" +msgstr "" #: templates/admin/submit_line.html:5 msgid "Save as new" -msgstr "Save as new" +msgstr "" #: templates/admin/submit_line.html:6 msgid "Save and add another" -msgstr "Save and add another" +msgstr "" #: templates/admin/submit_line.html:7 msgid "Save and continue editing" -msgstr "Save and continue editing" +msgstr "" #: templates/admin/auth/user/add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" -"First, enter a username and password. Then, you'll be able to edit more user " -"options." #: templates/admin/auth/user/add_form.html:8 msgid "Enter a username and password." -msgstr "Enter a username and password." +msgstr "" #: templates/admin/auth/user/change_password.html:31 #, python-format msgid "Enter a new password for the user %(username)s." -msgstr "Enter a new password for the user %(username)s." +msgstr "" #: templates/admin/auth/user/change_password.html:38 msgid "Password" -msgstr "Password" +msgstr "" #: templates/admin/auth/user/change_password.html:44 #: templates/registration/password_change_form.html:42 msgid "Password (again)" -msgstr "Password (again)" +msgstr "" #: templates/admin/auth/user/change_password.html:45 msgid "Enter the same password as above, for verification." -msgstr "Enter the same password as above, for verification." +msgstr "" #: templates/admin/edit_inline/stacked.html:26 #: templates/admin/edit_inline/tabular.html:76 msgid "Remove" -msgstr "Remove" +msgstr "" #: templates/admin/edit_inline/stacked.html:27 #: templates/admin/edit_inline/tabular.html:75 #, python-format msgid "Add another %(verbose_name)s" -msgstr "Add another %(verbose_name)s" +msgstr "" #: templates/admin/edit_inline/tabular.html:17 msgid "Delete?" -msgstr "Delete?" +msgstr "" #: templates/registration/logged_out.html:8 msgid "Thanks for spending some quality time with the Web site today." -msgstr "Thanks for spending some quality time with the Web site today." +msgstr "" #: templates/registration/logged_out.html:10 msgid "Log in again" -msgstr "Log in again" +msgstr "" #: templates/registration/password_change_done.html:7 #: templates/registration/password_change_form.html:8 #: templates/registration/password_change_form.html:12 #: templates/registration/password_change_form.html:24 msgid "Password change" -msgstr "Password change" +msgstr "" #: templates/registration/password_change_done.html:11 #: templates/registration/password_change_done.html:15 msgid "Password change successful" -msgstr "Password change successful" +msgstr "" #: templates/registration/password_change_done.html:17 msgid "Your password was changed." -msgstr "Your password was changed." +msgstr "" #: templates/registration/password_change_form.html:26 msgid "" "Please enter your old password, for security's sake, and then enter your new " "password twice so we can verify you typed it in correctly." msgstr "" -"Please enter your old password, for security's sake, and then enter your new " -"password twice so we can verify you typed it in correctly." #: templates/registration/password_change_form.html:32 msgid "Old password" -msgstr "Old password" +msgstr "" #: templates/registration/password_change_form.html:37 msgid "New password" -msgstr "New password" +msgstr "" #: templates/registration/password_change_form.html:48 #: templates/registration/password_reset_confirm.html:26 msgid "Change my password" -msgstr "Change my password" +msgstr "" #: templates/registration/password_reset_complete.html:7 #: templates/registration/password_reset_confirm.html:11 @@ -750,66 +709,59 @@ msgstr "Change my password" #: templates/registration/password_reset_form.html:11 #: templates/registration/password_reset_form.html:15 msgid "Password reset" -msgstr "Password reset" +msgstr "" #: templates/registration/password_reset_complete.html:11 #: templates/registration/password_reset_complete.html:15 msgid "Password reset complete" -msgstr "Password reset complete" +msgstr "" #: templates/registration/password_reset_complete.html:17 msgid "Your password has been set. You may go ahead and log in now." -msgstr "Your password has been set. You may go ahead and log in now." +msgstr "" #: templates/registration/password_reset_confirm.html:7 msgid "Password reset confirmation" -msgstr "Password reset confirmation" +msgstr "" #: templates/registration/password_reset_confirm.html:17 msgid "Enter new password" -msgstr "Enter new password" +msgstr "" #: templates/registration/password_reset_confirm.html:19 msgid "" "Please enter your new password twice so we can verify you typed it in " "correctly." msgstr "" -"Please enter your new password twice so we can verify you typed it in " -"correctly." #: templates/registration/password_reset_confirm.html:23 msgid "New password:" -msgstr "New password:" +msgstr "" #: templates/registration/password_reset_confirm.html:25 msgid "Confirm password:" -msgstr "Confirm password:" +msgstr "" #: templates/registration/password_reset_confirm.html:31 msgid "Password reset unsuccessful" -msgstr "Password reset unsuccessful" +msgstr "" #: templates/registration/password_reset_confirm.html:33 msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" -"The password reset link was invalid, possibly because it has already been " -"used. Please request a new password reset." #: templates/registration/password_reset_done.html:11 #: templates/registration/password_reset_done.html:15 msgid "Password reset successful" -msgstr "Password reset successful" +msgstr "" #: templates/registration/password_reset_done.html:17 -#, fuzzy msgid "" "We've emailed you instructions for setting your password. You should be " "receiving them shortly." msgstr "" -"We've emailed you instructions for setting your password to the email " -"address you submitted. You should be receiving it shortly." #: templates/registration/password_reset_done.html:19 msgid "" @@ -823,56 +775,52 @@ msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." msgstr "" -"You're receiving this email because you requested a password reset for your " -"user account at %(site_name)s." #: templates/registration/password_reset_email.html:4 msgid "Please go to the following page and choose a new password:" -msgstr "Please go to the following page and choose a new password:" +msgstr "" #: templates/registration/password_reset_email.html:8 msgid "Your username, in case you've forgotten:" -msgstr "Your username, in case you've forgotten:" +msgstr "" #: templates/registration/password_reset_email.html:10 msgid "Thanks for using our site!" -msgstr "Thanks for using our site!" +msgstr "" #: templates/registration/password_reset_email.html:12 #, python-format msgid "The %(site_name)s team" -msgstr "The %(site_name)s team" +msgstr "" #: templates/registration/password_reset_form.html:17 msgid "" "Forgotten your password? Enter your email address below, and we'll email " "instructions for setting a new one." msgstr "" -"Forgotten your password? Enter your email address below, and we'll email " -"instructions for setting a new one." #: templates/registration/password_reset_form.html:21 msgid "Email address:" -msgstr "Email address:" +msgstr "" #: templates/registration/password_reset_form.html:21 msgid "Reset my password" -msgstr "Reset my password" +msgstr "" #: templatetags/admin_list.py:348 msgid "All dates" -msgstr "All dates" +msgstr "" #: views/main.py:37 msgid "(None)" -msgstr "(None)" +msgstr "" #: views/main.py:86 #, python-format msgid "Select %s" -msgstr "Select %s" +msgstr "" #: views/main.py:88 #, python-format msgid "Select %s to change" -msgstr "Select %s to change" +msgstr "" diff --git a/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.mo b/django/contrib/admin/locale/en/LC_MESSAGES/djangojs.mo index 9851b29c1023bd892427a5d77e2e306e527d7cae..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 176 zcmX>h^@Pddo)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$${*i;l{cKrn&}3 z3WjD@h8EfeMg|640sgu{sb!hPnfZCTE{P?nRtiQ2h6W&YhQ?42nh)ULSe~{BWISiO~6{mQ5=7OgEzM94G@V-?@oDl;@<96 zPp_SQ2QDB$0s$8e;mQ-h5qJS^NW23QCnEkeGyWi0Mu@9N+w<$`>8koybx-fFXO4WT za1q)|(hey#B0P*6t{=fS!JojR;Grj!Itd;JkAvqx8NXC;5IlwFt6&MNf=_{S;49z~ zI0QQIN$@jJ?7sw$fZu?3!EZtF|EL&0hL`B7lc3B$4T}Hspu`&h@lzGtJ^`Q)?jNA!{|FR+C*VZJ&lbE0 zW_m!;R{`-;lekI#bKnrjAVR7SL9zcB6#burlKYN}QDwxxe>8$zOwTslEaw|A(O1-9mDb|1TiY&osOMigvhCXV4^n zxt__9#h2JrJYOm(IwkhgXwRXYLwgZTt_wM`J%`Zhc{IsSuG45|(L^80uZpdRUVdFH z_%e7N?OAyq98TF>Ny)9z;BeChbTPoq!12EC-XLA>l-2ZxPK<6CromLrXu~$v9v$0c zqh{L9rCfkCg`~6dWU}`u+sx7=&2(sPwwrRTS6GJuKr`tWtxS2jp_gcL)&?|pW3Wz+lN;Rs}dua2ts#e>WRBtCK;TPIW zCP6F|UGFMjneMqBb)c#!L+UzAjefBriEW8#hZ38)Xt)Q|VBd=Q0%v+3_q3TnLIa)VnkMg_H3U{3kQ7vsHadfNKN~5}~QfYaq9?f{xvWC&D4qTGC`x2>g!`86@iu0stT zZ<_3}oY*yY12(qR(=CqbtUFbjvn}kzHeF|X(&f9NqKi_*wU+0G, 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-03-08 10:42+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: static/admin/js/SelectFilter2.js:45 #, c-format msgid "Available %s" -msgstr "Available %s" +msgstr "" #: static/admin/js/SelectFilter2.js:46 #, c-format @@ -27,39 +24,37 @@ msgid "" "This is the list of available %s. You may choose some by selecting them in " "the box below and then clicking the \"Choose\" arrow between the two boxes." msgstr "" -"This is the list of available %s. You may choose some by selecting them in " -"the box below and then clicking the \"Choose\" arrow between the two boxes." #: static/admin/js/SelectFilter2.js:53 #, c-format msgid "Type into this box to filter down the list of available %s." -msgstr "Type into this box to filter down the list of available %s." +msgstr "" #: static/admin/js/SelectFilter2.js:57 msgid "Filter" -msgstr "Filter" +msgstr "" #: static/admin/js/SelectFilter2.js:61 msgid "Choose all" -msgstr "Choose all" +msgstr "" #: static/admin/js/SelectFilter2.js:61 #, c-format msgid "Click to choose all %s at once." -msgstr "Click to choose all %s at once." +msgstr "" #: static/admin/js/SelectFilter2.js:67 msgid "Choose" -msgstr "Choose" +msgstr "" #: static/admin/js/SelectFilter2.js:69 msgid "Remove" -msgstr "Remove" +msgstr "" #: static/admin/js/SelectFilter2.js:75 #, c-format msgid "Chosen %s" -msgstr "Chosen %s" +msgstr "" #: static/admin/js/SelectFilter2.js:76 #, c-format @@ -67,31 +62,27 @@ msgid "" "This is the list of chosen %s. You may remove some by selecting them in the " "box below and then clicking the \"Remove\" arrow between the two boxes." msgstr "" -"This is the list of chosen %s. You may remove some by selecting them in the " -"box below and then clicking the \"Remove\" arrow between the two boxes." #: static/admin/js/SelectFilter2.js:80 msgid "Remove all" -msgstr "Remove all" +msgstr "" #: static/admin/js/SelectFilter2.js:80 #, c-format msgid "Click to remove all chosen %s at once." -msgstr "Click to remove all chosen %s at once." +msgstr "" #: static/admin/js/actions.js:18 static/admin/js/actions.min.js:1 msgid "%(sel)s of %(cnt)s selected" msgid_plural "%(sel)s of %(cnt)s selected" -msgstr[0] "%(sel)s of %(cnt)s selected" -msgstr[1] "%(sel)s of %(cnt)s selected" +msgstr[0] "" +msgstr[1] "" #: static/admin/js/actions.js:109 static/admin/js/actions.min.js:5 msgid "" "You have unsaved changes on individual editable fields. If you run an " "action, your unsaved changes will be lost." msgstr "" -"You have unsaved changes on individual editable fields. If you run an " -"action, your unsaved changes will be lost." #: static/admin/js/actions.js:121 static/admin/js/actions.min.js:5 msgid "" @@ -99,9 +90,6 @@ msgid "" "individual fields yet. Please click OK to save. You'll need to re-run the " "action." msgstr "" -"You have selected an action, but you haven't saved your changes to " -"individual fields yet. Please click OK to save. You'll need to re-run the " -"action." #: static/admin/js/actions.js:123 static/admin/js/actions.min.js:6 msgid "" @@ -109,74 +97,69 @@ msgid "" "fields. You're probably looking for the Go button rather than the Save " "button." msgstr "" -"You have selected an action, and you haven't made any changes on individual " -"fields. You're probably looking for the Go button rather than the Save " -"button." #: static/admin/js/calendar.js:8 msgid "" "January February March April May June July August September October November " "December" msgstr "" -"January February March April May June July August September October November " -"December" #: static/admin/js/calendar.js:9 msgid "S M T W T F S" -msgstr "S M T W T F S" +msgstr "" #: static/admin/js/collapse.js:8 static/admin/js/collapse.js.c:19 #: static/admin/js/collapse.min.js:1 msgid "Show" -msgstr "Show" +msgstr "" #: static/admin/js/collapse.js:16 static/admin/js/collapse.min.js:1 msgid "Hide" -msgstr "Hide" - -#: static/admin/js/admin/DateTimeShortcuts.js:49 -#: static/admin/js/admin/DateTimeShortcuts.js:85 -msgid "Now" -msgstr "Now" - -#: static/admin/js/admin/DateTimeShortcuts.js:53 -msgid "Clock" -msgstr "Clock" - -#: static/admin/js/admin/DateTimeShortcuts.js:81 -msgid "Choose a time" -msgstr "Choose a time" - -#: static/admin/js/admin/DateTimeShortcuts.js:86 -msgid "Midnight" -msgstr "Midnight" - -#: static/admin/js/admin/DateTimeShortcuts.js:87 -msgid "6 a.m." -msgstr "6 a.m." +msgstr "" +#: static/admin/js/admin/DateTimeShortcuts.js:52 #: static/admin/js/admin/DateTimeShortcuts.js:88 +msgid "Now" +msgstr "" + +#: static/admin/js/admin/DateTimeShortcuts.js:56 +msgid "Clock" +msgstr "" + +#: static/admin/js/admin/DateTimeShortcuts.js:84 +msgid "Choose a time" +msgstr "" + +#: static/admin/js/admin/DateTimeShortcuts.js:89 +msgid "Midnight" +msgstr "" + +#: static/admin/js/admin/DateTimeShortcuts.js:90 +msgid "6 a.m." +msgstr "" + +#: static/admin/js/admin/DateTimeShortcuts.js:91 msgid "Noon" -msgstr "Noon" +msgstr "" -#: static/admin/js/admin/DateTimeShortcuts.js:92 -#: static/admin/js/admin/DateTimeShortcuts.js:204 +#: static/admin/js/admin/DateTimeShortcuts.js:95 +#: static/admin/js/admin/DateTimeShortcuts.js:208 msgid "Cancel" -msgstr "Cancel" - -#: static/admin/js/admin/DateTimeShortcuts.js:144 -#: static/admin/js/admin/DateTimeShortcuts.js:197 -msgid "Today" -msgstr "Today" +msgstr "" #: static/admin/js/admin/DateTimeShortcuts.js:148 -msgid "Calendar" -msgstr "Calendar" +#: static/admin/js/admin/DateTimeShortcuts.js:201 +msgid "Today" +msgstr "" -#: static/admin/js/admin/DateTimeShortcuts.js:195 -msgid "Yesterday" -msgstr "Yesterday" +#: static/admin/js/admin/DateTimeShortcuts.js:152 +msgid "Calendar" +msgstr "" #: static/admin/js/admin/DateTimeShortcuts.js:199 +msgid "Yesterday" +msgstr "" + +#: static/admin/js/admin/DateTimeShortcuts.js:203 msgid "Tomorrow" -msgstr "Tomorrow" +msgstr "" diff --git a/django/contrib/admindocs/locale/en/LC_MESSAGES/django.mo b/django/contrib/admindocs/locale/en/LC_MESSAGES/django.mo index 6c452c73928a05bb454ce5baa0216390d4ec7235..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 153 zcmaDO{e;Qlo)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$(z|dd5m=pOmz*6 z6b#L*3@3AN$mkn@1PqN83{9|9&G>s literal 3564 zcmeH|O^X~w7{^PEZ*9~V6Ezymlhwr?vpty|qpZCz*?tq zc5eGXVC=`-i}}J`LL3Aq@5UF#&*1yG_6syk0{#e|1)smKinj{lBd+7?5%4qc z6!;Cuc01tYL2x$+Q?U=^{o^1$Vsf}X1ajUpAlG{wWc#aN1H1;Z{l{R2`acI*hy8HQ zI(!*bI@|jA`W{=(oo2@_HXZXd3XV~vg{u()UB0{0x}wLTuekA0Yr zVsbz6*onzK$Ah(7A5Y@PQ<$9JHXYTUuF5&e9MwH zKFBPiwzZcA(p}0_@RO7}*`v@`lxcH`f~7TExQ#tN3f5+A=_biKa=O|8gI zCMB7sq{Bb0>vYPlvPbGj6M2=X4tBwEBnRp`J0NM+D3?8jEXxgE2URDS2Zgl^2UPN96BI|Bc%C_NOk2u zKr+vjbgUTCl~lMamw}TY+r9{u#F0FwaaZo`+QOvbh$q>$SJWi6i$FG4#+OQ!$!%yS z$*5IA@u^*{i=!h)&5KM)LlcV{B06fh0*X3Xl9^XEv0yvJK$#$ew&vE==yCbmZgt@y z^ieOLo>)w|4R~3VK@*+AL8vfHsdk4Qn+ey%l2%z-9`jnR5*nZA@`dvOtD3Lw!nGkt zSq0#W8FcgFI2uHpC36h zYa+^|6Jd%$8%={J5ciy7gOo0e=Ira^6IG^FiT=XI83ox(1V8uxYM9C(hsV5bCip2% z#A#>wfryvW_#Dm|&6&``MQM6AI-_#yLcHR8I*pGOJs-DhLec5hTk$cc%0`YCkZ3~9 z##A$&YQ)VZHC{`a(+3*OMgtq;GwODSe1rP8yH0txM>HH26QD zlS=c!&LSHu4`=1Fu1(j@!^ZRqoPOL=a*)s>noRpX%_?&^({<~5^U=!k%Hn2CQ}sr4 zYczSam=hRg$UNjw>qLCAtZtJiSJRxPG(^=tU z7B5*h@Cli`T>81@3{^kpCJgPLqp8|VwDsQDdT(sKH%8v5TknlKz9jz-?~T6!WGe_f diff --git a/django/contrib/admindocs/locale/en/LC_MESSAGES/django.po b/django/contrib/admindocs/locale/en/LC_MESSAGES/django.po index c95904e30a..2e4dcdf13d 100644 --- a/django/contrib/admindocs/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/admindocs/locale/en/LC_MESSAGES/django.po @@ -1,77 +1,74 @@ # This file is distributed under the same license as the Django package. # -# Translators: -# Jannis Leidel , 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-10-22 08:46+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: views.py:59 views.py:61 views.py:63 msgid "tag:" -msgstr "tag:" +msgstr "" #: views.py:94 views.py:96 views.py:98 msgid "filter:" -msgstr "filter:" +msgstr "" #: views.py:157 views.py:159 views.py:161 msgid "view:" -msgstr "view:" +msgstr "" #: views.py:189 #, python-format msgid "App %r not found" -msgstr "App %r not found" +msgstr "" #: views.py:196 #, python-format msgid "Model %(model_name)r not found in app %(app_label)r" -msgstr "Model %(model_name)r not found in app %(app_label)r" +msgstr "" #: views.py:208 #, python-format msgid "the related `%(app_label)s.%(data_type)s` object" -msgstr "the related `%(app_label)s.%(data_type)s` object" +msgstr "" #: views.py:208 views.py:227 views.py:232 views.py:246 views.py:260 #: views.py:265 msgid "model:" -msgstr "model:" +msgstr "" #: views.py:223 views.py:255 #, python-format msgid "related `%(app_label)s.%(object_name)s` objects" -msgstr "related `%(app_label)s.%(object_name)s` objects" +msgstr "" #: views.py:227 views.py:260 #, python-format msgid "all %s" -msgstr "all %s" +msgstr "" #: views.py:232 views.py:265 #, python-format msgid "number of %s" -msgstr "number of %s" +msgstr "" #: views.py:270 #, python-format msgid "Fields on %s objects" -msgstr "Fields on %s objects" +msgstr "" #: views.py:362 #, python-format msgid "%s does not appear to be a urlpattern object" -msgstr "%s does not appear to be a urlpattern object" +msgstr "" #: templates/admin_doc/bookmarklets.html:6 templates/admin_doc/index.html:6 #: templates/admin_doc/missing_docutils.html:6 @@ -83,7 +80,7 @@ msgstr "%s does not appear to be a urlpattern object" #: templates/admin_doc/view_detail.html:6 #: templates/admin_doc/view_index.html:7 msgid "Home" -msgstr "Home" +msgstr "" #: templates/admin_doc/bookmarklets.html:7 templates/admin_doc/index.html:7 #: templates/admin_doc/missing_docutils.html:7 @@ -95,15 +92,15 @@ msgstr "Home" #: templates/admin_doc/view_detail.html:7 #: templates/admin_doc/view_index.html:8 msgid "Documentation" -msgstr "Documentation" +msgstr "" #: templates/admin_doc/bookmarklets.html:8 msgid "Bookmarklets" -msgstr "Bookmarklets" +msgstr "" #: templates/admin_doc/bookmarklets.html:11 msgid "Documentation bookmarklets" -msgstr "Documentation bookmarklets" +msgstr "" #: templates/admin_doc/bookmarklets.html:15 msgid "" @@ -115,81 +112,70 @@ msgid "" "as \"internal\" (talk to your system administrator if you aren't sure if\n" "your computer is \"internal\").

\n" msgstr "" -"\n" -"

To install bookmarklets, drag the link to your bookmarks\n" -"toolbar, or right-click the link and add it to your bookmarks. Now you can\n" -"select the bookmarklet from any page in the site. Note that some of these\n" -"bookmarklets require you to be viewing the site from a computer designated\n" -"as \"internal\" (talk to your system administrator if you aren't sure if\n" -"your computer is \"internal\").

\n" #: templates/admin_doc/bookmarklets.html:25 msgid "Documentation for this page" -msgstr "Documentation for this page" +msgstr "" #: templates/admin_doc/bookmarklets.html:26 msgid "" "Jumps you from any page to the documentation for the view that generates " "that page." msgstr "" -"Jumps you from any page to the documentation for the view that generates " -"that page." #: templates/admin_doc/bookmarklets.html:28 msgid "Show object ID" -msgstr "Show object ID" +msgstr "" #: templates/admin_doc/bookmarklets.html:29 msgid "" "Shows the content-type and unique ID for pages that represent a single " "object." msgstr "" -"Shows the content-type and unique ID for pages that represent a single " -"object." #: templates/admin_doc/bookmarklets.html:31 msgid "Edit this object (current window)" -msgstr "Edit this object (current window)" +msgstr "" #: templates/admin_doc/bookmarklets.html:32 msgid "Jumps to the admin page for pages that represent a single object." -msgstr "Jumps to the admin page for pages that represent a single object." +msgstr "" #: templates/admin_doc/bookmarklets.html:34 msgid "Edit this object (new window)" -msgstr "Edit this object (new window)" +msgstr "" #: templates/admin_doc/bookmarklets.html:35 msgid "As above, but opens the admin page in a new window." -msgstr "As above, but opens the admin page in a new window." +msgstr "" #: templates/admin_doc/model_detail.html:16 #: templates/admin_doc/model_index.html:10 msgid "Models" -msgstr "Models" +msgstr "" #: templates/admin_doc/template_detail.html:8 msgid "Templates" -msgstr "Templates" +msgstr "" #: templates/admin_doc/template_filter_index.html:9 msgid "Filters" -msgstr "Filters" +msgstr "" #: templates/admin_doc/template_tag_index.html:9 msgid "Tags" -msgstr "Tags" +msgstr "" #: templates/admin_doc/view_detail.html:8 #: templates/admin_doc/view_index.html:9 msgid "Views" -msgstr "Views" +msgstr "" -#: tests/__init__.py:23 +#: tests/test_fields.py:29 msgid "Boolean (Either True or False)" -msgstr "Boolean (Either True or False)" +msgstr "" -#: tests/__init__.py:33 +#: tests/test_fields.py:39 #, python-format msgid "Field of type: %(field_type)s" -msgstr "Field of type: %(field_type)s" +msgstr "" diff --git a/django/contrib/auth/locale/en/LC_MESSAGES/django.mo b/django/contrib/auth/locale/en/LC_MESSAGES/django.mo index 91fc85a68fa6fdae25a54ea352b8c84bb496fbd2..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 176 zcmcbi^@Pddo)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$rE`z!;N(fOmz*6 z6b#L*3@x+`j0_C80{nG@Qp+-nGxPIwT@p)DtrUz53=Kf)42=~GO|6VgA?kb*i%WDv wiW2jRa}rDPi>wq}vJ&&s^A$=`6LTkf@T&0m0J)`!=|BTg^CoBWMl&)101XW#Pyhe` literal 5464 zcmeH~O>87b6@ZIie%1*D0$CD%lF9~SoAr!;Hk&xJw%Lu>mKMBTYw?1jC`2{gH9fuC zQ)RlkJ@JW1hzKqS5X*HX4%`rtI1zy&A%UVGfjDqLpm0HOAvc7=h48)Z9(x=K5O2iy%Ggb%Gz=cf(iU`{1wOm*DT=_uw)@HsEjI=ivRPlzIjl*o8lXQ&6MSHr#}w&uzE~e*{I3 zf52zqTksNmmQ6W|x&slRegI|OOAUVl`BOjVA$t89vQ+&Mik#)_uR-y%g`)4*pxE=CC`tljYXL2iiWLh9b1pdf&Lj>OB*}(C2YSPX%?^hBR@eu%TXD^wUra z?3;juOz+uzkcNN4m83Lkh+BRcF-OdIP6>cav7*aM#kyPCpvY7 zr1mX<`4v%8@pq@5?OKU8@TYRoIw~wTX&!-?sG)7i?yuqL!JuP;hSTdpf4O z5Zz1^`O=9IHU=im)P^fi%;-X)P?)hBr+dS>1ktr}r zTU{CK`MfZ$(6QK2U2(P4!^JEa;1?E_?w0dIr?G9nQY%Y*j^nV~mQNB+g>}`o9Uks{ ztf3?CdZ`FwW3}xMXX#*zwz`IaMw_ag#>($!0 zBhA}-+ZVW5Lb>Y+>RxKIIOu^n5cfw2YX$4rTzs#tndYDLILDf( zFLxI?^O8L(x$SC;VmKD^O<2TfEJxr@ME!c!xa#9aYBD719k&9$mXN6fd)(V1j@gYB zB!p7eq?~$$dtTZqFu9lom#cD{Cg;?i{a-$5s~gQ7P*3|Z*I%?<-Oc?_^7+U&W^d0L za;UGp6nTFuC0Sc%x|xL6deb41pEp5QB`FeZBEg!HqAKZrEIn`&L`LO4W;B+Ze>OZOGSMf-3sMEMLtHEfmM?I^i7{S8>?d$R^+Tu=xS2sO;yK=k~FAD zB6*e2g31ij&^vYYYqg?!j4qlw`ZXGR4Jw$dP@yor9#`KKB|Rlmt59e<0ay zQSBfjLGinIOZPae&(%)uZ<486SK`(e7$fC&^yQoUg!;+rR#JZJYDm(!^;DUJ)~@g9 z$(?Jvt#$c%Vs7g)=IH39g~g?o{GHcJ=Q>MIoLyL4SYTr7y4_Dj=G*5zt{0X&=NHc| zP$su9NNbn75i)~H9sOBil{nn8DL-v`1#M_v%&KNv((M{+TvD3Ewsx%b752XwHd@;Y5;rI(x?jqx_-q_u2Eg#Jj zoq9HJZ4f&#Vb;;h-LyE|yhCtJ?qp?dveqU^F?6)stD0eT>4L6bS7)4lWK}QDUYI=j viaGg;;X~x)D~9j#ldqVQubAc^rqQRz*oWE4SIp7B3MXGN_5bDn?JMTr7$+ML diff --git a/django/contrib/auth/locale/en/LC_MESSAGES/django.po b/django/contrib/auth/locale/en/LC_MESSAGES/django.po index c2e2cdaef7..36faba74bf 100644 --- a/django/contrib/auth/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/auth/locale/en/LC_MESSAGES/django.po @@ -1,81 +1,79 @@ # This file is distributed under the same license as the Django package. # -# Translators: -# Jannis Leidel , 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-12-16 08:51+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:41 msgid "Personal info" -msgstr "Personal info" +msgstr "" #: admin.py:42 msgid "Permissions" -msgstr "Permissions" +msgstr "" #: admin.py:44 msgid "Important dates" -msgstr "Important dates" +msgstr "" #: admin.py:132 msgid "Password changed successfully." -msgstr "Password changed successfully." +msgstr "" #: admin.py:142 #, python-format msgid "Change password: %s" -msgstr "Change password: %s" +msgstr "" -#: forms.py:33 tests/forms.py:261 tests/forms.py:266 tests/forms.py:407 +#: forms.py:33 tests/test_forms.py:261 tests/test_forms.py:266 +#: tests/test_forms.py:407 msgid "No password set." -msgstr "No password set." +msgstr "" -#: forms.py:39 tests/forms.py:271 tests/forms.py:277 +#: forms.py:39 tests/test_forms.py:271 tests/test_forms.py:277 msgid "Invalid password format or unknown hashing algorithm." -msgstr "Invalid password format or unknown hashing algorithm." +msgstr "" #: forms.py:72 msgid "A user with that username already exists." -msgstr "A user with that username already exists." +msgstr "" #: forms.py:73 forms.py:254 forms.py:314 msgid "The two password fields didn't match." -msgstr "The two password fields didn't match." +msgstr "" #: forms.py:75 forms.py:120 msgid "Username" -msgstr "Username" +msgstr "" #: forms.py:77 forms.py:121 msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only." -msgstr "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only." +msgstr "" #: forms.py:80 forms.py:124 msgid "This value may contain only letters, numbers and @/./+/-/_ characters." -msgstr "This value may contain only letters, numbers and @/./+/-/_ characters." +msgstr "" #: forms.py:82 forms.py:126 forms.py:153 forms.py:316 msgid "Password" -msgstr "Password" +msgstr "" #: forms.py:84 msgid "Password confirmation" -msgstr "Password confirmation" +msgstr "" #: forms.py:86 msgid "Enter the same password as above, for verification." -msgstr "Enter the same password as above, for verification." +msgstr "" #: forms.py:127 msgid "" @@ -83,9 +81,6 @@ msgid "" "password, but you can change the password using this " "form." msgstr "" -"Raw passwords are not stored, so there is no way to see this user's " -"password, but you can change the password using this " -"form." #: forms.py:156 #, python-format @@ -93,208 +88,177 @@ msgid "" "Please enter a correct %(username)s and password. Note that both fields may " "be case-sensitive." msgstr "" -"Please enter a correct %(username)s and password. Note that both fields may " -"be case-sensitive." #: forms.py:158 msgid "This account is inactive." -msgstr "This account is inactive." +msgstr "" #: forms.py:206 msgid "Email" -msgstr "Email" +msgstr "" #: forms.py:256 msgid "New password" -msgstr "New password" +msgstr "" #: forms.py:258 msgid "New password confirmation" -msgstr "New password confirmation" +msgstr "" #: forms.py:287 msgid "Your old password was entered incorrectly. Please enter it again." -msgstr "Your old password was entered incorrectly. Please enter it again." +msgstr "" #: forms.py:290 msgid "Old password" -msgstr "Old password" +msgstr "" #: forms.py:318 msgid "Password (again)" -msgstr "Password (again)" +msgstr "" #: hashers.py:243 hashers.py:317 hashers.py:365 hashers.py:393 hashers.py:426 #: hashers.py:459 hashers.py:493 msgid "algorithm" -msgstr "algorithm" +msgstr "" #: hashers.py:244 msgid "iterations" -msgstr "iterations" +msgstr "" #: hashers.py:245 hashers.py:319 hashers.py:366 hashers.py:394 hashers.py:494 msgid "salt" -msgstr "salt" +msgstr "" #: hashers.py:246 hashers.py:367 hashers.py:395 hashers.py:427 hashers.py:460 #: hashers.py:495 msgid "hash" -msgstr "hash" +msgstr "" #: hashers.py:318 msgid "work factor" -msgstr "work factor" +msgstr "" #: hashers.py:320 msgid "checksum" -msgstr "checksum" +msgstr "" #: models.py:72 models.py:121 msgid "name" -msgstr "name" +msgstr "" #: models.py:74 msgid "codename" -msgstr "codename" +msgstr "" #: models.py:78 msgid "permission" -msgstr "permission" +msgstr "" #: models.py:79 models.py:123 msgid "permissions" -msgstr "permissions" +msgstr "" #: models.py:128 msgid "group" -msgstr "group" +msgstr "" -#: models.py:129 models.py:301 +#: models.py:129 models.py:294 msgid "groups" -msgstr "groups" +msgstr "" #: models.py:200 msgid "password" -msgstr "password" +msgstr "" #: models.py:201 msgid "last login" -msgstr "last login" +msgstr "" -#: models.py:298 +#: models.py:291 msgid "superuser status" -msgstr "superuser status" +msgstr "" + +#: models.py:292 +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "" + +#: models.py:295 +msgid "" +"The groups this user belongs to. A user will get all permissions granted to " +"each of his/her group." +msgstr "" #: models.py:299 -msgid "" -"Designates that this user has all permissions without explicitly assigning " -"them." -msgstr "" -"Designates that this user has all permissions without explicitly assigning " -"them." - -#: models.py:302 -msgid "" -"The groups this user belongs to. A user will get all permissions granted to " -"each of his/her group." -msgstr "" -"The groups this user belongs to. A user will get all permissions granted to " -"each of his/her group." - -#: models.py:306 msgid "user permissions" -msgstr "user permissions" +msgstr "" -#: models.py:377 +#: models.py:366 msgid "username" -msgstr "username" +msgstr "" + +#: models.py:367 +msgid "" +"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" +msgstr "" + +#: models.py:370 +msgid "Enter a valid username." +msgstr "" + +#: models.py:372 +msgid "first name" +msgstr "" + +#: models.py:373 +msgid "last name" +msgstr "" + +#: models.py:374 +msgid "email address" +msgstr "" + +#: models.py:375 +msgid "staff status" +msgstr "" + +#: models.py:376 +msgid "Designates whether the user can log into this admin site." +msgstr "" #: models.py:378 -msgid "" -"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" +msgid "active" +msgstr "" + +#: models.py:379 +msgid "" +"Designates whether this user should be treated as active. Unselect this " +"instead of deleting accounts." msgstr "" -"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" #: models.py:381 -msgid "Enter a valid username." -msgstr "Enter a valid username." - -#: models.py:383 -msgid "first name" -msgstr "first name" - -#: models.py:384 -msgid "last name" -msgstr "last name" - -#: models.py:385 -msgid "email address" -msgstr "email address" - -#: models.py:386 -msgid "staff status" -msgstr "staff status" - -#: models.py:387 -msgid "Designates whether the user can log into this admin site." -msgstr "Designates whether the user can log into this admin site." +msgid "date joined" +msgstr "" #: models.py:389 -msgid "active" -msgstr "active" +msgid "user" +msgstr "" #: models.py:390 -msgid "" -"Designates whether this user should be treated as active. Unselect this " -"instead of deleting accounts." -msgstr "" -"Designates whether this user should be treated as active. Unselect this " -"instead of deleting accounts." - -#: models.py:392 -msgid "date joined" -msgstr "date joined" - -#: models.py:400 -msgid "user" -msgstr "user" - -#: models.py:401 msgid "users" -msgstr "users" +msgstr "" #: views.py:89 msgid "Logged out" -msgstr "Logged out" +msgstr "" #: templates/registration/password_reset_subject.txt:2 #, python-format msgid "Password reset on %(site_name)s" -msgstr "Password reset on %(site_name)s" +msgstr "" -#: tests/forms.py:325 -#, fuzzy +#: tests/test_forms.py:325 msgid "Enter a valid email address." -msgstr "Enter a valid username." - -#~ msgid "" -#~ "Your Web browser doesn't appear to have cookies enabled. Cookies are " -#~ "required for logging in." -#~ msgstr "" -#~ "Your Web browser doesn't appear to have cookies enabled. Cookies are " -#~ "required for logging in." - -#~ msgid "" -#~ "That email address doesn't have an associated user account. Are you sure " -#~ "you've registered?" -#~ msgstr "" -#~ "That email address doesn't have an associated user account. Are you sure " -#~ "you've registered?" - -#~ msgid "" -#~ "The user account associated with this email address cannot reset the " -#~ "password." -#~ msgstr "" -#~ "The user account associated with this email address cannot reset the " -#~ "password." +msgstr "" diff --git a/django/contrib/comments/locale/en/LC_MESSAGES/django.mo b/django/contrib/comments/locale/en/LC_MESSAGES/django.mo index 6a6f6e70b68d273ec3179f66f11f2cda01bc21e2..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 161 zcmeyS`h>~io)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$qC$^7RI^;rn&}3 z3WjD@h8EfeMg|640sgu{sb!hPnfZCTE{P?nRtiQ2h6W&YhQDigZHfHQ~{94N!+hJ_RF&rXPJ6$u|xTkyE zRkQ1rI3q+Z2@6WD`dviIRU`~Y_0CX|zQ4@KTf5K+`Ca1(qLihW*(EUDfq+uwnr_aC9`|L3xP z9o|X(Q^*$TbI7N@F6+16R`hQ!siCY>gEDRp6#WiCK6Q>?d=q*@@yo{uC4T+~6gg)R zPUN10V$btX{Cp8g|36Fq8}g|y`N@78?kLvV2xY#8vi}S`0cW9xFG11!GL(5&q4@dj zvi=2>`Cmct^KB?E`<{WK$8%8RN}z@>L-F&QQ2cxiil0A%;^)6Z@$;8Z=5L`>{QL+M zJ0B?7geR$o@C>{PoA73Y5q~}d#n0zq7yhAa-?+7q_jV|9ZinLMN1@nfHxzx3l z=>05|{eM-~FG56AOHlm$5)}LWrmX+2?0=)=+fdfI24&obQ1tsN6hHq9;^thYX(;I- zviMxmZt}xqvBhI#ktHCD4aHuXJVO>;>SWR7DKgiE+C!E!MP{2(dXkdZ{BiOVqgG;( za)Z3HY?~bGo1iT6V$NLuePy5MwwEk-lq9*Y_UEO-A7az(WbwV&OH!4L+j4tIjO-vw zdW8HF@&T!o>0U^PCA8>L;8embh*$6*i5}_GyB~W4)^<6x@1*JZHD`dTAYn-+uLy1 z;ndMZ+rFUvV&wEfdPzqe%68bTX^j#i%l5O#cw5zn_0EyqBGlL?{KH? zp^WKTy&T0cT{?_hKQ=3D8YRwK6IRvH!V)m?SVYbBOcZMqh8c6!@z`|ppZ>PE(pN}Z z3ZUkjqlxn!y^;JI+2Y-8IX;HAByNRi*lhBZ}d%1oucOaaG29+ zf@8*9sZLvOLgS741+G=i*cvBB%QCE+8_;H1nxTE1E`MjY7%uNmomWkMtAxv2kZD1J zV&H5>92ifTeTk+@MZzNwD5)%Z-)~G$)0<*T!iPCoJmpj~vrCahh=QxPIBSgrU1zOP zf2PWwp1ELGnOpY<3vtvQ4Jf?6YKZhnD3lnB!lM1Zc(t`?k_)--I#_MoMJn%dTH5nC zT*9T}jin9Nw2DUEHEp@5YudcIKuhj1AN5c*)ln}$M0wlAfr%r(qSKB4Nyg5CoaR1$EWBnCZ3Y~s#7FHsy2OcuWysP4m#=U0xMv08FAUNoUa9_{E6P

??b7xPfb4h#kIY-gbw>J*E?Q{s=Xvq>1sEfI+$#f@6d%3WpZ&jT)zP(tusF$MN z4&CCuSWdH0PjFc{oupn9Rp~NTSme6;UsIgSF#ai<*f*gp=z6WOXYa1sOs&Si;4C+QtT{fe z#?alm{zPMD_pTbLa>_U#v@(;p*!VPS=%1S;LHkoS3T>Bf;;)CX*GD2}RW);(A`J%4)chEDZrty13kK?}Duw6~Z1G|xkn?AMqx zbJowEYaI{v-54h_b!-;QCG9liv^Mm xoSjVcj#*uw+F#*W9lOc7wAUZy^+&n5jpfZ=f0Ty@V*OEGf0Wm}, 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-02-14 13:24+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:25 msgid "Content" -msgstr "Content" +msgstr "" #: admin.py:28 msgid "Metadata" -msgstr "Metadata" +msgstr "" #: admin.py:55 -#, fuzzy, python-format +#, python-format msgid "%d comment was successfully flagged" msgid_plural "%d comments were successfully flagged" -msgstr[0] "1 comment was successfully %(action)s." -msgstr[1] "%(count)s comments were successfully %(action)s." +msgstr[0] "" +msgstr[1] "" #: admin.py:57 msgid "Flag selected comments" -msgstr "Flag selected comments" +msgstr "" #: admin.py:61 -#, fuzzy, python-format +#, python-format msgid "%d comment was successfully approved" msgid_plural "%d comments were successfully approved" -msgstr[0] "1 comment was successfully %(action)s." -msgstr[1] "%(count)s comments were successfully %(action)s." +msgstr[0] "" +msgstr[1] "" #: admin.py:63 msgid "Approve selected comments" -msgstr "Approve selected comments" +msgstr "" #: admin.py:67 -#, fuzzy, python-format +#, python-format msgid "%d comment was successfully removed" msgid_plural "%d comments were successfully removed" -msgstr[0] "1 comment was successfully %(action)s." -msgstr[1] "%(count)s comments were successfully %(action)s." +msgstr[0] "" +msgstr[1] "" #: admin.py:69 msgid "Remove selected comments" -msgstr "Remove selected comments" +msgstr "" #: feeds.py:14 #, python-format msgid "%(site_name)s comments" -msgstr "%(site_name)s comments" +msgstr "" #: feeds.py:20 #, python-format msgid "Latest comments on %(site_name)s" -msgstr "Latest comments on %(site_name)s" +msgstr "" #: forms.py:96 msgid "Name" -msgstr "Name" +msgstr "" #: forms.py:97 msgid "Email address" -msgstr "Email address" +msgstr "" #: forms.py:98 msgid "URL" -msgstr "URL" +msgstr "" #: forms.py:99 msgid "Comment" -msgstr "Comment" +msgstr "" #: forms.py:177 #, python-format msgid "Watch your mouth! The word %s is not allowed here." msgid_plural "Watch your mouth! The words %s are not allowed here." -msgstr[0] "Watch your mouth! The word %s is not allowed here." -msgstr[1] "Watch your mouth! The words %s are not allowed here." +msgstr[0] "" +msgstr[1] "" #: forms.py:181 templates/comments/preview.html:16 msgid "and" -msgstr "and" +msgstr "" #: forms.py:186 msgid "" "If you enter anything in this field your comment will be treated as spam" msgstr "" -"If you enter anything in this field your comment will be treated as spam" #: models.py:23 msgid "content type" -msgstr "content type" +msgstr "" #: models.py:25 msgid "object ID" -msgstr "object ID" +msgstr "" #: models.py:53 models.py:177 msgid "user" -msgstr "user" +msgstr "" #: models.py:55 msgid "user's name" -msgstr "user's name" +msgstr "" #: models.py:56 msgid "user's email address" -msgstr "user's email address" +msgstr "" #: models.py:57 msgid "user's URL" -msgstr "user's URL" +msgstr "" #: models.py:59 models.py:79 models.py:178 msgid "comment" -msgstr "comment" +msgstr "" #: models.py:62 msgid "date/time submitted" -msgstr "date/time submitted" +msgstr "" #: models.py:63 msgid "IP address" -msgstr "IP address" +msgstr "" #: models.py:64 msgid "is public" -msgstr "is public" +msgstr "" #: models.py:65 msgid "" "Uncheck this box to make the comment effectively disappear from the site." msgstr "" -"Uncheck this box to make the comment effectively disappear from the site." #: models.py:67 msgid "is removed" -msgstr "is removed" +msgstr "" #: models.py:68 msgid "" "Check this box if the comment is inappropriate. A \"This comment has been " "removed\" message will be displayed instead." msgstr "" -"Check this box if the comment is inappropriate. A \"This comment has been " -"removed\" message will be displayed instead." #: models.py:80 msgid "comments" -msgstr "comments" +msgstr "" #: models.py:124 msgid "" "This comment was posted by an authenticated user and thus the name is read-" "only." msgstr "" -"This comment was posted by an authenticated user and thus the name is read-" -"only." #: models.py:134 msgid "" "This comment was posted by an authenticated user and thus the email is read-" "only." msgstr "" -"This comment was posted by an authenticated user and thus the email is read-" -"only." #: models.py:160 #, python-format @@ -187,128 +176,107 @@ msgid "" "\n" "http://%(domain)s%(url)s" msgstr "" -"Posted by %(user)s at %(date)s\n" -"\n" -"%(comment)s\n" -"\n" -"http://%(domain)s%(url)s" #: models.py:179 msgid "flag" -msgstr "flag" +msgstr "" #: models.py:180 msgid "date" -msgstr "date" +msgstr "" #: models.py:190 msgid "comment flag" -msgstr "comment flag" +msgstr "" #: models.py:191 msgid "comment flags" -msgstr "comment flags" +msgstr "" #: templates/comments/approve.html:4 msgid "Approve a comment" -msgstr "Approve a comment" +msgstr "" #: templates/comments/approve.html:7 msgid "Really make this comment public?" -msgstr "Really make this comment public?" +msgstr "" #: templates/comments/approve.html:12 msgid "Approve" -msgstr "Approve" +msgstr "" #: templates/comments/approved.html:4 msgid "Thanks for approving" -msgstr "Thanks for approving" +msgstr "" #: templates/comments/approved.html:7 templates/comments/deleted.html:7 #: templates/comments/flagged.html:7 msgid "" "Thanks for taking the time to improve the quality of discussion on our site" msgstr "" -"Thanks for taking the time to improve the quality of discussion on our site" #: templates/comments/delete.html:4 msgid "Remove a comment" -msgstr "Remove a comment" +msgstr "" #: templates/comments/delete.html:7 msgid "Really remove this comment?" -msgstr "Really remove this comment?" +msgstr "" #: templates/comments/delete.html:12 msgid "Remove" -msgstr "Remove" +msgstr "" #: templates/comments/deleted.html:4 msgid "Thanks for removing" -msgstr "Thanks for removing" +msgstr "" #: templates/comments/flag.html:4 msgid "Flag this comment" -msgstr "Flag this comment" +msgstr "" #: templates/comments/flag.html:7 msgid "Really flag this comment?" -msgstr "Really flag this comment?" +msgstr "" #: templates/comments/flag.html:12 msgid "Flag" -msgstr "Flag" +msgstr "" #: templates/comments/flagged.html:4 msgid "Thanks for flagging" -msgstr "Thanks for flagging" +msgstr "" #: templates/comments/form.html:17 templates/comments/preview.html:32 msgid "Post" -msgstr "Post" +msgstr "" #: templates/comments/form.html:18 templates/comments/preview.html:33 msgid "Preview" -msgstr "Preview" +msgstr "" #: templates/comments/posted.html:4 msgid "Thanks for commenting" -msgstr "Thanks for commenting" +msgstr "" #: templates/comments/posted.html:7 msgid "Thank you for your comment" -msgstr "Thank you for your comment" +msgstr "" #: templates/comments/preview.html:4 templates/comments/preview.html.py:13 msgid "Preview your comment" -msgstr "Preview your comment" +msgstr "" #: templates/comments/preview.html:11 msgid "Please correct the error below" msgid_plural "Please correct the errors below" -msgstr[0] "Please correct the error below" -msgstr[1] "Please correct the errors below" +msgstr[0] "" +msgstr[1] "" #: templates/comments/preview.html:16 msgid "Post your comment" -msgstr "Post your comment" +msgstr "" #: templates/comments/preview.html:16 msgid "or make changes" -msgstr "or make changes" - -#~ msgid "flagged" -#~ msgid_plural "flagged" -#~ msgstr[0] "flagged" -#~ msgstr[1] "flagged" - -#~ msgid "approved" -#~ msgid_plural "approved" -#~ msgstr[0] "approved" -#~ msgstr[1] "approved" - -#~ msgid "removed" -#~ msgid_plural "removed" -#~ msgstr[0] "removed" -#~ msgstr[1] "removed" +msgstr "" diff --git a/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.mo b/django/contrib/contenttypes/locale/en/LC_MESSAGES/django.mo index 68b8aa62bec368a2b56d938322d85ccf43b9b36b..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 154 zcmcb^{)EZmo)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$&(p9d5v`qOmz*6 z6b#L*3@s+VWR%u70ErkHD;Szu8JlVw7#SFF`6L#X=!O&}<`w58mgE;%DY#@M=B4K= fl%yu+PWEC_;qd`-OB2&mtrSx8Cg(CmGco`G8R#DA literal 988 zcmds#!EO^V5Qeu;_UrPEu90sZ`X6s9Ly2&hB*M${9ztQ?(M0 zz!4sT7eH|5Id}>lfN_G_a^lWNpZ^_e{;@~ipO3abCmi>fCrrWIXFf79zA=*d&g?M1 zm@d=WN|FbxiuEBYu|8(~z-pLJO!RHt-uMQr(dSvC?+a`6ePxZlAFOOiSdwk#E)(O< zM%L>sUgJI2@vKpXDX9fEJ=ob9mcc`1>3M*;I!91YW0O-YS(y$fcK*_|}IFC50#CvpJTas{arl_{*2`;S*V(e>OiPx*zi^57sCvOX}?@$G< z6$gB}gie-sM4pMf3wb8HJFTpfWn3su@Lb29H|w&OBI`k(%U9i2#*rQ?A4K64d82}L z65c2x?co?T4+R6h4X1l%owhZd+FkZgy;3!b0@Xsoh-#z#90o{xMz^i2chm9Fcyv8e z-p, 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-03-08 11:45+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: models.py:130 msgid "python model class name" -msgstr "python model class name" +msgstr "" #: models.py:134 msgid "content type" -msgstr "content type" +msgstr "" #: models.py:135 msgid "content types" -msgstr "content types" +msgstr "" #: views.py:17 #, python-format msgid "Content type %(ct_id)s object has no associated model" -msgstr "Content type %(ct_id)s object has no associated model" +msgstr "" #: views.py:21 #, python-format msgid "Content type %(ct_id)s object %(obj_id)s doesn't exist" -msgstr "Content type %(ct_id)s object %(obj_id)s doesn't exist" +msgstr "" #: views.py:27 #, python-format msgid "%(ct_name)s objects don't have a get_absolute_url() method" -msgstr "%(ct_name)s objects don't have a get_absolute_url() method" +msgstr "" diff --git a/django/contrib/flatpages/locale/en/LC_MESSAGES/django.mo b/django/contrib/flatpages/locale/en/LC_MESSAGES/django.mo index 67acb769d7c63e82e8541af993d32d447059a9e6..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 161 zcmcb^|Afino)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$^0yy7RI^;rn&}3 z3WjD@h8EfeMg|640sgu{sb!hPnfZCTE{P?nRtiQ2h6W&YhQ8%^Bq|yTkZ6$jE0ExOdpTc1PnXfX`R&_%`{sLZcJ7Z$=RQ*y zmtfDszJk2~+k6NU#@FDp;5Xpg;E!MpUU*ok9Q*)$1N;uW4E_VY3toOiDg0Ez^dfiy z68JgzIQTvI4EO{11o#Ws0e=Ud1OEb_2LA>h1v}@IvZ4hPJs;wx==l^BJ->mX=gr4v zdOiY0&(EOf`4dDcwT;C^&kjgn35uSNF^Qf}K+*FBD0;pGMbEdO==lj0J->pGqb|T? zJOvY77h&@Lc^Eb6Q%}L*Jy;!n>r4w7S%#Lw%bmHi0R58tW;OWnCIqk#;4epp6wLS{=t6yZ7W8 zS)C@j;y6$Do_O7Hsx9*Fk0^~zBop0IqDk;mQ*yb2n^^8NCa&p@v6h<1w6sW7@<=x} z-OZx*?yiB@G^Qq4die_G9N`9RSm#&f}PR0{!AjJdcii+g8J$@;3| zGBZxr4sj-9$fSq1ZNk*>omOUBjv7c6Bg3{tEfo0u#sn_Av-?wR8>YI6iQ1kb>1+*7 z3AtCuHTAH)D1Cz4xDsCB1H&oXmafcuODG{3TS}L293P0uinWQc-Hx-3Y4@Uyx&Qh7 zo7GjKh+R_b+R_SN*KnhfZj%(ZxQZ%pg+>M~U(8vR3fnY6G?UXct7MX8brk$9E>gB% zW;am`;&a-$rCsGa$2{>NWrwjcW%hbg#cben>Kwf@$gX3%BzLxhbUCf{`fFLgm-Vkw zzn5Qq^_AXQuZO_wn5RZ^{x@!Y9@j(MfsQE~1ntm@wxY3Ii!Sqrnl=>(z^pQKgv z!?+E6Q#wRWTQ>%K+4>?*G>tf9yRPtZigH>Xnsl~!PV72Iwh7v1dp^`LCpT%`xV5%H cv&*e3j$YcL{_;lW!C8KAmhms~zn$fO0fSdT>;M1& diff --git a/django/contrib/flatpages/locale/en/LC_MESSAGES/django.po b/django/contrib/flatpages/locale/en/LC_MESSAGES/django.po index e671dda5fc..53b82e5793 100644 --- a/django/contrib/flatpages/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/flatpages/locale/en/LC_MESSAGES/django.po @@ -1,97 +1,88 @@ # This file is distributed under the same license as the Django package. # -# Translators: -# Jannis Leidel , 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-10-18 10:56+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:10 msgid "Advanced options" -msgstr "Advanced options" +msgstr "" #: forms.py:7 models.py:11 msgid "URL" -msgstr "URL" +msgstr "" #: forms.py:8 msgid "" "Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" -"Example: '/about/contact/'. Make sure to have leading and trailing slashes." #: forms.py:10 msgid "" "This value must contain only letters, numbers, dots, underscores, dashes, " "slashes or tildes." msgstr "" -"This value must contain only letters, numbers, dots, underscores, dashes, " -"slashes or tildes." #: forms.py:19 msgid "URL is missing a leading slash." -msgstr "URL is missing a leading slash." +msgstr "" #: forms.py:23 msgid "URL is missing a trailing slash." -msgstr "URL is missing a trailing slash." +msgstr "" #: forms.py:38 #, python-format msgid "Flatpage with url %(url)s already exists for site %(site)s" -msgstr "Flatpage with url %(url)s already exists for site %(site)s" +msgstr "" #: models.py:12 msgid "title" -msgstr "title" +msgstr "" #: models.py:13 msgid "content" -msgstr "content" +msgstr "" #: models.py:14 msgid "enable comments" -msgstr "enable comments" +msgstr "" #: models.py:15 msgid "template name" -msgstr "template name" +msgstr "" #: models.py:16 msgid "" "Example: 'flatpages/contact_page.html'. If this isn't provided, the system " "will use 'flatpages/default.html'." msgstr "" -"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " -"will use 'flatpages/default.html'." #: models.py:17 msgid "registration required" -msgstr "registration required" +msgstr "" #: models.py:18 msgid "If this is checked, only logged-in users will be able to view the page." msgstr "" -"If this is checked, only logged-in users will be able to view the page." #: models.py:24 msgid "flat page" -msgstr "flat page" +msgstr "" #: models.py:25 msgid "flat pages" -msgstr "flat pages" +msgstr "" -#: tests/forms.py:98 +#: tests/test_forms.py:98 msgid "This field is required." -msgstr "This field is required." +msgstr "" diff --git a/django/contrib/formtools/locale/en/LC_MESSAGES/django.mo b/django/contrib/formtools/locale/en/LC_MESSAGES/django.mo index 64ae715f31b83ddbb8336b040d3c3e363bb085a8..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 176 zcmaFG`h>~io)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$xMu%;l{cKrn&}3 z3WjD@h8EfeMg|640sgu{sb!hPnfZCTE{P?nRtiQ2h6W&YhQ{Ij#x@Cx_Ht1~?ji-{#3?dRQS9A~eH-oVTD$8g$rHc> z@LKdVG!*pI6#ULfgg0QMPqV-E&(7@l`;*Nt3E}~G3P#`&Frbf5;3N19Qt%Tz2EV{V z@Ee$WNwNWYUweP;`-LVd$paHc@CpE=S$t+`L8dBb(e~la<|;h5$TxpU{rKo(i>$h zhCgGyU_%F(2J*<<%1qnZ9iqlI9UdntRx3$k+ZY{|binq==*;=%FrA!E#&>TjvOGP; z*4U!gb&H`RU&R-#QQ8h@zEnPN9KNeg#QvSHpQ&aq#&+%&ShJ-4nT~&(OJbW&P3M&n eCs=%tWZPRAhUI|P$Ki8J&xTZN4btTQ{pLTmPS_Iw diff --git a/django/contrib/formtools/locale/en/LC_MESSAGES/django.po b/django/contrib/formtools/locale/en/LC_MESSAGES/django.po index e3b7dd7557..048b9fea45 100644 --- a/django/contrib/formtools/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/formtools/locale/en/LC_MESSAGES/django.po @@ -1,36 +1,26 @@ # This file is distributed under the same license as the Django package. # -# Translators: -# Jannis Leidel , 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-02-14 13:42+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: templates/formtools/wizard/wizard_form.html:15 msgid "first step" -msgstr "first step" +msgstr "" #: templates/formtools/wizard/wizard_form.html:16 msgid "prev step" -msgstr "prev step" +msgstr "" #: templates/formtools/wizard/wizard_form.html:18 msgid "submit" -msgstr "submit" - -#~ msgid "" -#~ "We apologize, but your form has expired. Please continue filling out the " -#~ "form from this page." -#~ msgstr "" -#~ "We apologize, but your form has expired. Please continue filling out the " -#~ "form from this page." +msgstr "" diff --git a/django/contrib/gis/locale/en/LC_MESSAGES/django.mo b/django/contrib/gis/locale/en/LC_MESSAGES/django.mo index 19ac4a347044bbd2e5bae63e9805ee6b7f4e4cde..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 154 zcmeCi{tbSOBpWP|^}egVeyl$IljT`dczl4|(!}&sD}~g&$xbZMj0^zJk{$g3 literal 1678 zcmeH`&2AJq5XXD@-dA@F%bZ9s?hOC&6NSegvMy_!;;s_!2w{z6CFV z@4=Y&5j+U~2Ob8K{Yi2TI07z#7eNgT3XFRGIWX394M|bYJQ(#n0;3)W#(kfHQP1=3 z`IlhS^9qc5-holi2QcdS2*$jVhsJu&gHg`}81)En30wwaiy$ZY5x!$Tfg0L4Y709k?nSe&T)_a+GIn)N#pz04U{P; zY%&#W2OQjpg005uYt03+{k>~3f%=Mdua+$DBy_FT+zrZ_q-nOKR=qDa8#CCM$RwA|24sE+)&lAI=7(vIO1o_J7(QPQrCZf^@**TVua zq`a97do*;=%nj+8mYeJ2UXc)lll)F=S4%c=alPcO>Z`7dy`$ZJhN@?6XCFh0X!gW? zb{-KqEj*T{u<06?)&h3bjh!@`S>%_(iKcEr5nYB#YU1LxGp>J3rPM16|tSw3~+ zYSzdy1d27@QgP2OvEVu-$p4$DDKi>A=ZZ_+HATUynuD{iK(W^3_tP4!xq&RY}M;4D<0Q{X4z zTB)TnGt}Lb&U2W%*I5$NpW~{_%T6qsuI=IW)d5S7v`vbWhPC&|0Ms, 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-03-08 12:32+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: views.py:9 msgid "No feeds are registered." -msgstr "No feeds are registered." +msgstr "" #: views.py:19 #, python-format msgid "Slug %r isn't registered." -msgstr "Slug %r isn't registered." +msgstr "" #: db/models/fields.py:51 msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type." -msgstr "The base GIS field -- maps to the OpenGIS Specification Geometry type." +msgstr "" #: db/models/fields.py:270 msgid "Point" -msgstr "Point" +msgstr "" #: db/models/fields.py:274 msgid "Line string" -msgstr "Line string" +msgstr "" #: db/models/fields.py:278 msgid "Polygon" -msgstr "Polygon" +msgstr "" #: db/models/fields.py:282 msgid "Multi-point" -msgstr "Multi-point" +msgstr "" #: db/models/fields.py:286 msgid "Multi-line string" -msgstr "Multi-line string" +msgstr "" #: db/models/fields.py:290 msgid "Multi polygon" -msgstr "Multi polygon" +msgstr "" #: db/models/fields.py:294 msgid "Geometry collection" -msgstr "Geometry collection" +msgstr "" #: forms/fields.py:23 msgid "No geometry value provided." -msgstr "No geometry value provided." +msgstr "" #: forms/fields.py:24 msgid "Invalid geometry value." -msgstr "Invalid geometry value." +msgstr "" #: forms/fields.py:25 msgid "Invalid geometry type." -msgstr "Invalid geometry type." +msgstr "" #: forms/fields.py:26 msgid "" "An error occurred when transforming the geometry to the SRID of the geometry " "form field." msgstr "" -"An error occurred when transforming the geometry to the SRID of the geometry " -"form field." #: sitemaps/views.py:46 #, python-format msgid "No sitemap available for section: %r" -msgstr "No sitemap available for section: %r" +msgstr "" #: sitemaps/views.py:60 #, python-format msgid "Page %s empty" -msgstr "Page %s empty" +msgstr "" #: sitemaps/views.py:62 #, python-format msgid "No page '%s'" -msgstr "No page '%s'" +msgstr "" diff --git a/django/contrib/humanize/locale/en/LC_MESSAGES/django.mo b/django/contrib/humanize/locale/en/LC_MESSAGES/django.mo index 55110ebd7897a79a3ad454e8fafa79285fbe6adf..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 154 zcmbOs|Afino)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$)=p1yvDi)rn&}3 z3WjD@h8B~%Ii>XtKq7|53Wlau#-`c^Mg|64K8eL8x*$IlizTv@c00^rHSdORtl+klli%#85sc7J|1KM literal 3992 zcmeH}KWrRD6vhW)2tF|64F+?k%$D+_-<~!VQ8yAC}@HxOaT(#yPcfv+c{{U^T@NmecyZg zZuY(19shWs?_-7$Vm^iW**%QS(D@!5Fp7PQ4TB|c1bht~0tF!;n&VBd1p6oOIM{!GmOlUoVK0KDuRHxy$8SLT{a+mq@6GbeI5xo{_&))k0dIk$ z;Lpw;cp$Sw@EG*BKw8ID5KCbG&0R1oqe9)8O~uz2F_^{~e?f{o(kh^Rxae z-yV?s{UBX8;JDBE4>}$JaVa|nQhX7_9~;FXK`&kchhbkYWcPm?r2E_iNk95vrq4RA zf>i&{9lr&s{(m`+Je1w{n;_Ns8c2C=fK>nQoc^2B4-I7gX>bI7>G%;?f_=;J7m(Jq z|KY6uC&5wJmz@2+v%dhT{y%_p{U%8DAABUM|FGjIh?J}h9spkksg4WIzX(#jE;+vK z{7WF^lTLrf>Dn=M{+8prAl3gpkn(*1qU7vD5anlMnB=GTgPsRk3zpm-kK*_^=6Ghd zk4N1(<$4m6-YL4z027aH``HBAgYFMyZjXI9QhjMWi}^(REUO{)g=)KfPz|Yn&ul@f;>S3pDU}wFkWud+BVbxF(X8ERB^L zd?`+nSgU{4>aymE^sAB9kxq7`^hixlKA)zv@S>eVJ;Yp#DAFT(8q|>lYNc9MauI2~IW^rU|MHJu1UpAe1#dy>@xmRDVf!Ev4LY# zrzv9Xx@H^FSecSMpX$r9W`pzf;0>7?d}8=MU09GSI<>)^iQ;Y*0;wGKsJ{t?&y%so3y~GDbQ0DXa$OnWSxw#j2rC;~EvUL?nZ%6wL~sRZ$X~ zWqwMkGf6zA)97?z?)=>B);*QS!orMJRx13~#tIf~@r;hIBqCN*ytXV-BklBJ^;|Hy z6-R4o$TXN$HC>NYRN<3LvF-eLg-cbKPpEytIh{65g{zgeX{INpcy>BnRD5unmrGNH z9b3Zg&eOf``o3DUNBh1||D=09Qxx7)&V8z~Xs_J)U~T^=A}9LxneKl3cbVOt$J3_W So#%hqd3NqLTh@QP!~6{$Z5>?z diff --git a/django/contrib/humanize/locale/en/LC_MESSAGES/django.po b/django/contrib/humanize/locale/en/LC_MESSAGES/django.po index a4b6138e7f..fc75b677a0 100644 --- a/django/contrib/humanize/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/humanize/locale/en/LC_MESSAGES/django.po @@ -1,293 +1,290 @@ # This file is distributed under the same license as the Django package. # -# Translators: -# Jannis Leidel , 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-02-14 13:08+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: tests.py:131 templatetags/humanize.py:168 +#: tests.py:135 templatetags/humanize.py:168 msgid "today" -msgstr "today" +msgstr "" -#: tests.py:131 templatetags/humanize.py:172 +#: tests.py:135 templatetags/humanize.py:172 msgid "yesterday" -msgstr "yesterday" +msgstr "" -#: tests.py:131 templatetags/humanize.py:170 +#: tests.py:135 templatetags/humanize.py:170 msgid "tomorrow" -msgstr "tomorrow" +msgstr "" #: templatetags/humanize.py:26 msgid "th" -msgstr "th" +msgstr "" #: templatetags/humanize.py:26 msgid "st" -msgstr "st" +msgstr "" #: templatetags/humanize.py:26 msgid "nd" -msgstr "nd" +msgstr "" #: templatetags/humanize.py:26 msgid "rd" -msgstr "rd" +msgstr "" #: templatetags/humanize.py:55 #, python-format msgid "%(value).1f million" msgid_plural "%(value).1f million" -msgstr[0] "%(value).1f million" -msgstr[1] "%(value).1f million" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:56 #, python-format msgid "%(value)s million" msgid_plural "%(value)s million" -msgstr[0] "%(value)s million" -msgstr[1] "%(value)s million" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:59 #, python-format msgid "%(value).1f billion" msgid_plural "%(value).1f billion" -msgstr[0] "%(value).1f billion" -msgstr[1] "%(value).1f billion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:60 #, python-format msgid "%(value)s billion" msgid_plural "%(value)s billion" -msgstr[0] "%(value)s billion" -msgstr[1] "%(value)s billion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:63 #, python-format msgid "%(value).1f trillion" msgid_plural "%(value).1f trillion" -msgstr[0] "%(value).1f trillion" -msgstr[1] "%(value).1f trillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:64 #, python-format msgid "%(value)s trillion" msgid_plural "%(value)s trillion" -msgstr[0] "%(value)s trillion" -msgstr[1] "%(value)s trillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:67 #, python-format msgid "%(value).1f quadrillion" msgid_plural "%(value).1f quadrillion" -msgstr[0] "%(value).1f quadrillion" -msgstr[1] "%(value).1f quadrillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:68 #, python-format msgid "%(value)s quadrillion" msgid_plural "%(value)s quadrillion" -msgstr[0] "%(value)s quadrillion" -msgstr[1] "%(value)s quadrillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:71 #, python-format msgid "%(value).1f quintillion" msgid_plural "%(value).1f quintillion" -msgstr[0] "%(value).1f quintillion" -msgstr[1] "%(value).1f quintillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:72 #, python-format msgid "%(value)s quintillion" msgid_plural "%(value)s quintillion" -msgstr[0] "%(value)s quintillion" -msgstr[1] "%(value)s quintillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:75 #, python-format msgid "%(value).1f sextillion" msgid_plural "%(value).1f sextillion" -msgstr[0] "%(value).1f sextillion" -msgstr[1] "%(value).1f sextillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:76 #, python-format msgid "%(value)s sextillion" msgid_plural "%(value)s sextillion" -msgstr[0] "%(value)s sextillion" -msgstr[1] "%(value)s sextillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:79 #, python-format msgid "%(value).1f septillion" msgid_plural "%(value).1f septillion" -msgstr[0] "%(value).1f septillion" -msgstr[1] "%(value).1f septillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:80 #, python-format msgid "%(value)s septillion" msgid_plural "%(value)s septillion" -msgstr[0] "%(value)s septillion" -msgstr[1] "%(value)s septillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:83 #, python-format msgid "%(value).1f octillion" msgid_plural "%(value).1f octillion" -msgstr[0] "%(value).1f octillion" -msgstr[1] "%(value).1f octillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:84 #, python-format msgid "%(value)s octillion" msgid_plural "%(value)s octillion" -msgstr[0] "%(value)s octillion" -msgstr[1] "%(value)s octillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:87 #, python-format msgid "%(value).1f nonillion" msgid_plural "%(value).1f nonillion" -msgstr[0] "%(value).1f nonillion" -msgstr[1] "%(value).1f nonillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:88 #, python-format msgid "%(value)s nonillion" msgid_plural "%(value)s nonillion" -msgstr[0] "%(value)s nonillion" -msgstr[1] "%(value)s nonillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:91 #, python-format msgid "%(value).1f decillion" msgid_plural "%(value).1f decillion" -msgstr[0] "%(value).1f decillion" -msgstr[1] "%(value).1f decillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:92 #, python-format msgid "%(value)s decillion" msgid_plural "%(value)s decillion" -msgstr[0] "%(value)s decillion" -msgstr[1] "%(value)s decillion" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:95 #, python-format msgid "%(value).1f googol" msgid_plural "%(value).1f googol" -msgstr[0] "%(value).1f googol" -msgstr[1] "%(value).1f googol" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:96 #, python-format msgid "%(value)s googol" msgid_plural "%(value)s googol" -msgstr[0] "%(value)s googol" -msgstr[1] "%(value)s googol" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:145 msgid "one" -msgstr "one" +msgstr "" #: templatetags/humanize.py:145 msgid "two" -msgstr "two" +msgstr "" #: templatetags/humanize.py:145 msgid "three" -msgstr "three" +msgstr "" #: templatetags/humanize.py:145 msgid "four" -msgstr "four" +msgstr "" #: templatetags/humanize.py:145 msgid "five" -msgstr "five" +msgstr "" #: templatetags/humanize.py:145 msgid "six" -msgstr "six" +msgstr "" #: templatetags/humanize.py:145 msgid "seven" -msgstr "seven" +msgstr "" #: templatetags/humanize.py:145 msgid "eight" -msgstr "eight" +msgstr "" #: templatetags/humanize.py:145 msgid "nine" -msgstr "nine" +msgstr "" #: templatetags/humanize.py:191 #, python-format msgctxt "naturaltime" msgid "%(delta)s ago" -msgstr "%(delta)s ago" +msgstr "" #: templatetags/humanize.py:194 templatetags/humanize.py:216 msgid "now" -msgstr "now" +msgstr "" #: templatetags/humanize.py:197 #, python-format msgid "a second ago" msgid_plural "%(count)s seconds ago" -msgstr[0] "a second ago" -msgstr[1] "%(count)s seconds ago" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:202 #, python-format msgid "a minute ago" msgid_plural "%(count)s minutes ago" -msgstr[0] "a minute ago" -msgstr[1] "%(count)s minutes ago" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:207 #, python-format msgid "an hour ago" msgid_plural "%(count)s hours ago" -msgstr[0] "an hour ago" -msgstr[1] "%(count)s hours ago" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:213 #, python-format msgctxt "naturaltime" msgid "%(delta)s from now" -msgstr "%(delta)s from now" +msgstr "" #: templatetags/humanize.py:219 #, python-format msgid "a second from now" msgid_plural "%(count)s seconds from now" -msgstr[0] "a second from now" -msgstr[1] "%(count)s seconds from now" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:224 #, python-format msgid "a minute from now" msgid_plural "%(count)s minutes from now" -msgstr[0] "a minute from now" -msgstr[1] "%(count)s minutes from now" +msgstr[0] "" +msgstr[1] "" #: templatetags/humanize.py:229 #, python-format msgid "an hour from now" msgid_plural "%(count)s hours from now" -msgstr[0] "an hour from now" -msgstr[1] "%(count)s hours from now" +msgstr[0] "" +msgstr[1] "" diff --git a/django/contrib/messages/locale/en/LC_MESSAGES/django.mo b/django/contrib/messages/locale/en/LC_MESSAGES/django.mo index a0c5646f662ed80a3d6f8db542484d7d3af0e0cb..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 175 zcmcb?{DjHko)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8iLsvH#<~Wkx&}rH zhGtfV7TN|z1_oRK{<=Y_Wtqj9`FXl7i6yC43PuKo1|W5Y#tMd}R>r0fbv}v3CAuL+ uiFw62i6!|(RthdziFxVy3MHwDxsyd0Rd{@W+|tB!paH3Qlg$~U85sb`)g)&C delta 267 zcmaFDbb~qeo)F7a1|VPpVi_RT0b*7lwgF-g2moRpAPxlL97YC)I4J)#kPSp&eLxBU zauTa56>?LHixbmRCt7&&8tEDu=o*?T7#dianosPLb~Dm70E(F?7#dp{nrRyV0hdo= zafxn7QDRyQKH>SgAo<=aiXp)#41G0M#+ zF)zIoXqA;hY93cWPH9nMj;>pNQEsu7LS6xwTWo7&tpH)#YUC*>+A0`oT5~ZVyORL` DNy0!; diff --git a/django/contrib/messages/locale/en/LC_MESSAGES/django.po b/django/contrib/messages/locale/en/LC_MESSAGES/django.po index b1c1f9bea4..7a041aa1a2 100644 --- a/django/contrib/messages/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/messages/locale/en/LC_MESSAGES/django.po @@ -1,21 +1,18 @@ # This file is distributed under the same license as the Django package. # -# Translators: -# Jannis Leidel , 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-02-14 13:16+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: tests/base.py:101 +#: tests/base.py:100 msgid "lazy message" -msgstr "lazy message" +msgstr "" diff --git a/django/contrib/redirects/locale/en/LC_MESSAGES/django.mo b/django/contrib/redirects/locale/en/LC_MESSAGES/django.mo index bf854ed3827cef88e14c50753db32158ed1ab7b1..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 154 zcmcb^{)EZmo)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8$&(p9d5v`qOmz*6 z6b#L*3@s+VWR%u70ErkHD;Szu8JlVw7#SFF`6L#X=!O&}<`w58mgE;%DY#@M=B4K= fl%yu+PWEC_;qd`-OB2&mtrSx8Cg(CmGco`G8R#DA literal 988 zcmdr~&2G~`5O$&b86hOj9EMX`l}+rFs>NxliZ+#sQV~TCJ#XxZz14cx+Fgfm;QOGqEImC3xS~-Iec}*!#W{d*3aw_x&XHzF)*-S;u|K?hv-u zogg5|a?g9dmzKgp#uZEvP`DBeP{F5G%f=zp+?BAyEp_x9cR|x*m^D(uWIP1xxN$-i z@PVArDxIs7PN&sgO|_+NWbOz5JMkspB9l$-t#l;hy5d5C;uW@Gu;5iKF@aVG=ct_R zSmY)v{ak~&FqpY@0yCqlP381Dx53Eh8`84NoV`b5g;oii-*8pvaE!G!j$PS8iX0^z(>7F|%4OLWYNk)fql$(X$Wr800s~b@VM{nh zbt*+$o8lzAy1W`}-YITJ;h9#B?uw-!Yg*dj!gXp%ciMxj, 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-02-14 13:39+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: models.py:9 msgid "redirect from" -msgstr "redirect from" +msgstr "" #: models.py:10 msgid "" "This should be an absolute path, excluding the domain name. Example: '/" "events/search/'." msgstr "" -"This should be an absolute path, excluding the domain name. Example: '/" -"events/search/'." #: models.py:11 msgid "redirect to" -msgstr "redirect to" +msgstr "" #: models.py:12 msgid "" "This can be either an absolute path (as above) or a full URL starting with " "'http://'." msgstr "" -"This can be either an absolute path (as above) or a full URL starting with " -"'http://'." #: models.py:15 msgid "redirect" -msgstr "redirect" +msgstr "" #: models.py:16 msgid "redirects" -msgstr "redirects" +msgstr "" diff --git a/django/contrib/sessions/locale/en/LC_MESSAGES/django.mo b/django/contrib/sessions/locale/en/LC_MESSAGES/django.mo index 1f590af580cd47c68ea9694eeb915f4a93bd81de..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 175 zcmZo-eZpjMPl#nI0}wC*u?!Ha05LNV>i{tbSOBpWP|^}egVeyl#Cx9M#<~Wkx&}rH zhGtfV7TN|z1_oRK{<=Y_Wtqj9`FXl7i6yC43PuKo1|W5Y#tMd}R>r0fbv}v3CAuL+ viFw62i6!|(RthdziFxVy3MHwDxs#_ds_^&#xuuEeKm$_qChuU3W@G>W)$=8m literal 642 zcmbV}&rTaL5XQGH{iCI)o_mqeZ<*gr z>fbPLm|Mm^%d&gc)bBr^=Z{!Z{+KoO$E+zoW##y6?hDpbFXxgi{M95Jn-^X-#v@cZ zViqt2Ak?N3+mkwo(kaM? z2c_8Ys3T{^E>Ba?mPK0>QWR@YNVWEPS+=F*pg6?uCcXJ@PI7b=iWRjgmnB!;*CC41 zYYJ9J=M{X>L?%EVO@$UVcpJ7`c4k|KPTeNop?acg6ea4hf*#e@gfrMc+OnqQe7%_u z_6NPEZz@_c-*FU?c-FMxr6XR(PmR@vIxsrZK49#AE%(IwQ!edvieB_+, 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-02-14 13:40+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: models.py:38 msgid "session key" -msgstr "session key" +msgstr "" #: models.py:40 msgid "session data" -msgstr "session data" +msgstr "" #: models.py:41 msgid "expire date" -msgstr "expire date" +msgstr "" #: models.py:46 msgid "session" -msgstr "session" +msgstr "" #: models.py:47 msgid "sessions" -msgstr "sessions" +msgstr "" diff --git a/django/contrib/sites/locale/en/LC_MESSAGES/django.mo b/django/contrib/sites/locale/en/LC_MESSAGES/django.mo index f8612702dc176e2042f9c68d0ef5f68dea8756b5..08a7b68596a8a494a33644935e4ca6d40be6447f 100644 GIT binary patch delta 175 zcmdnT@`TCao)F7a1|VPrVi_P-0b*t#)&XJ=umEB$prj>`2C0F8iEBK=jdcx7bq$OZ z49%UFSp%sdFYII|>`0Ynr}wD9CL(ls>DH8fQ)G_W!?pV%kuW~6HX6f;pUG`2Fa)HVPD zE}z8W65WuZ#Ju91#FG3XD+RB_yu8d}1)tQ+l++vrn=CNbAqULW%gjs5x0`rFWilsY zRIE>8UV3R_da9K|Y93cWPH9nMj;>pNQEsu7LS6xwTWo7&tpH)#YUC*>+A0`oT5}QO Gd, 2011. msgid "" msgstr "" "Project-Id-Version: Django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-28 10:04+0100\n" -"PO-Revision-Date: 2012-02-14 13:49+0000\n" -"Last-Translator: Jannis Leidel \n" +"POT-Creation-Date: 2013-05-02 16:18+0200\n" +"PO-Revision-Date: 2010-05-13 15:35+0200\n" +"Last-Translator: Django team\n" "Language-Team: English \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: models.py:25 msgid "The domain name cannot contain any spaces or tabs." @@ -22,16 +19,16 @@ msgstr "" #: models.py:58 msgid "domain name" -msgstr "domain name" +msgstr "" #: models.py:60 msgid "display name" -msgstr "display name" +msgstr "" #: models.py:65 msgid "site" -msgstr "site" +msgstr "" #: models.py:66 msgid "sites" -msgstr "sites" +msgstr "" From d48b7230a8b8d44607b19749fb4a0a8a25d50cf1 Mon Sep 17 00:00:00 2001 From: Daniel Lindsley Date: Fri, 3 May 2013 00:46:04 -0700 Subject: [PATCH 35/70] Added myself to the committers list. --- docs/internals/committers.txt | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/docs/internals/committers.txt b/docs/internals/committers.txt index a0649f38a2..f891bc4eb7 100644 --- a/docs/internals/committers.txt +++ b/docs/internals/committers.txt @@ -15,7 +15,7 @@ Journal-World`_ of Lawrence, Kansas, USA. He was lead developer at World Online for 2.5 years, during which time Django was developed and implemented on World Online's sites. He was the - leader and founder of EveryBlock_, a "news feed for your block." He now + leader and founder of EveryBlock_, a "news feed for your block." He now develops for Soundslice_. Adrian lives in Chicago, USA. @@ -41,7 +41,7 @@ Journal-World`_ of Lawrence, Kansas, USA. `Wilson Miner`_ Wilson's design-fu is what makes Django look so nice. He designed the Web site you're looking at right now, as well as Django's acclaimed admin - interface. Wilson was the designer for EveryBlock and Rdio_. He now + interface. Wilson was the designer for EveryBlock and Rdio_. He now designs for Facebook. Wilson lives in San Francisco, USA. @@ -105,8 +105,8 @@ Malcolm Tredinnick .. _russell keith-magee: http://cecinestpasun.com/ Joseph Kocherhans - Joseph was the director of lead development at EveryBlock and previously - developed at the Lawrence Journal-World. He is treasurer of the `Django + Joseph was the director of lead development at EveryBlock and previously + developed at the Lawrence Journal-World. He is treasurer of the `Django Software Foundation`_. He often disappears for several days into the woods, attempts to teach himself computational linguistics, and annoys his neighbors with his Charango_ playing. @@ -386,17 +386,17 @@ Florian Apolloner .. _Ubuntuusers webteam: http://wiki.ubuntuusers.de/ubuntuusers/Webteam Jeremy Dunck - Jeremy was rescued from corporate IT drudgery by Free Software and, in part, + Jeremy was rescued from corporate IT drudgery by Free Software and, in part, Django. Many of Jeremy's interests center around access to information. - Jeremy was the lead developer of Pegasus News, one of the first uses of - Django outside World Online, and has since joined Votizen, a startup intent + Jeremy was the lead developer of Pegasus News, one of the first uses of + Django outside World Online, and has since joined Votizen, a startup intent on reducing the influence of money in politics. - He serves as DSF Secretary, organizes and helps organize sprints, cares - about the health and equity of the Django community. He has gone an + He serves as DSF Secretary, organizes and helps organize sprints, cares + about the health and equity of the Django community. He has gone an embarrassingly long time without a working blog. - + Jeremy lives in Mountain View, CA, USA. `Bryan Veloso`_ @@ -441,6 +441,20 @@ Jeremy Dunck .. _Ultimate Frisbee: http://www.montrealultimate.ca .. _Reptiletech: http://www.reptiletech.com +`Daniel Lindsley`_ + Pythonista since 2003, Djangonaut since 2006. Daniel started with Django + just after the v0.90 release (back when ``Manipulators`` looked good) & fell + in love. Since then, he wrote third-party apps like Haystack & Tastypie + & has run the annual Django Dash since 2007. One of the testing faithful, + Daniel's contributions include rewriting the ``Forms`` test suite & the + addition of ``request.is_ajax``. Daniel currently works as a Python + developer at `Amazon Web Services`_ on the ``boto`` library. + + Daniel lives in Seattle, WA, USA. + +.. _`Daniel Lindsley`: http://toastdriven.com/ +.. _`Amazon Web Services`: https://aws.amazon.com/ + Specialists ----------- From f9f0e8da4d1107575ebee07a808cd254ac3d9dc0 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 4 May 2013 10:08:38 +0200 Subject: [PATCH 36/70] Used ngettext in a formsets error message Several languages will distinctly translate '%d or fewer forms' depending on the variable. --- django/forms/formsets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 2ab197dee2..2bd11d9f53 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -9,7 +9,7 @@ from django.utils.encoding import python_2_unicode_compatible from django.utils.safestring import mark_safe from django.utils import six from django.utils.six.moves import xrange -from django.utils.translation import ugettext as _ +from django.utils.translation import ungettext, ugettext as _ __all__ = ('BaseFormSet', 'all_valid') @@ -302,7 +302,9 @@ class BaseFormSet(object): try: if (self.validate_max and self.total_form_count() > self.max_num) or \ self.management_form.cleaned_data[TOTAL_FORM_COUNT] > self.absolute_max: - raise ValidationError(_("Please submit %s or fewer forms." % self.max_num)) + raise ValidationError(ungettext( + "Please submit %d or fewer forms.", + "Please submit %d or fewer forms.", self.max_num) % self.max_num) # Give self.clean() a chance to do cross-form validation. self.clean() except ValidationError as e: From ac9daa0cbdd82a6b3bcaf0cd5874bd7cdb94fc60 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 4 May 2013 11:53:12 +0200 Subject: [PATCH 37/70] Systematically imported wraps from functools --- django/db/models/__init__.py | 3 ++- django/test/utils.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/django/db/models/__init__.py b/django/db/models/__init__.py index 6c5ccd4bd2..5f17229753 100644 --- a/django/db/models/__init__.py +++ b/django/db/models/__init__.py @@ -1,3 +1,5 @@ +from functools import wraps + from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured from django.db.models.loading import get_apps, get_app, get_models, get_model, register_models from django.db.models.query import Q @@ -11,7 +13,6 @@ from django.db.models.fields.files import FileField, ImageField from django.db.models.fields.related import ForeignKey, ForeignObject, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel from django.db.models.deletion import CASCADE, PROTECT, SET, SET_NULL, SET_DEFAULT, DO_NOTHING, ProtectedError from django.db.models import signals -from django.utils.decorators import wraps def permalink(func): diff --git a/django/test/utils.py b/django/test/utils.py index d839c0403c..92cef59f72 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -1,5 +1,6 @@ import re import warnings +from functools import wraps from xml.dom.minidom import parseString, Node from django.conf import settings, UserSettingsHolder @@ -10,7 +11,6 @@ from django.template import Template, loader, TemplateDoesNotExist from django.template.loaders import cached from django.test.signals import template_rendered, setting_changed from django.utils.encoding import force_str -from django.utils.functional import wraps from django.utils import six from django.utils.translation import deactivate From 66c83dce074b48342dbfd0d9039c76b8949f0833 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 4 May 2013 12:08:15 +0200 Subject: [PATCH 38/70] Fixed #18351 -- Added X-Robots-Tag header to sitemaps Thanks Michael Lissner for the report and initial patch, and Tom Mortimer-Jones for working on the patch. --- django/contrib/sitemaps/tests/test_http.py | 7 +++++++ django/contrib/sitemaps/views.py | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/django/contrib/sitemaps/tests/test_http.py b/django/contrib/sitemaps/tests/test_http.py index 1a91d970f3..a99025e6e2 100644 --- a/django/contrib/sitemaps/tests/test_http.py +++ b/django/contrib/sitemaps/tests/test_http.py @@ -144,3 +144,10 @@ class HTTPSitemapTests(SitemapTestsBase): """ % self.base_url self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + def test_x_robots_sitemap(self): + response = self.client.get('/simple/index.xml') + self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive') + + response = self.client.get('/simple/sitemap.xml') + self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive') diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py index c8d2f4dfa0..a851f8088a 100644 --- a/django/contrib/sitemaps/views.py +++ b/django/contrib/sitemaps/views.py @@ -6,7 +6,17 @@ from django.core.paginator import EmptyPage, PageNotAnInteger from django.http import Http404 from django.template.response import TemplateResponse from django.utils import six +from django.utils.functional import wraps +def x_robots_tag(func): + @wraps(func) + def inner(request, *args, **kwargs): + response = func(request, *args, **kwargs) + response['X-Robots-Tag'] = 'noindex, noodp, noarchive' + return response + return inner + +@x_robots_tag def index(request, sitemaps, template_name='sitemap_index.xml', content_type='application/xml', sitemap_url_name='django.contrib.sitemaps.views.sitemap', @@ -35,6 +45,7 @@ def index(request, sitemaps, return TemplateResponse(request, template_name, {'sitemaps': sites}, content_type=content_type) +@x_robots_tag def sitemap(request, sitemaps, section=None, template_name='sitemap.xml', content_type='application/xml', mimetype=None): From f3b3c569e770f8b576aba446847430586486bac6 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 4 May 2013 13:29:40 +0200 Subject: [PATCH 39/70] One more changed import location of wraps --- django/contrib/sitemaps/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py index a851f8088a..95bc7eac54 100644 --- a/django/contrib/sitemaps/views.py +++ b/django/contrib/sitemaps/views.py @@ -1,4 +1,5 @@ import warnings +from functools import wraps from django.contrib.sites.models import get_current_site from django.core import urlresolvers @@ -6,7 +7,6 @@ from django.core.paginator import EmptyPage, PageNotAnInteger from django.http import Http404 from django.template.response import TemplateResponse from django.utils import six -from django.utils.functional import wraps def x_robots_tag(func): @wraps(func) From da85c8cf326fa8858bac29f1b92da7972dd88dda Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sun, 5 May 2013 15:27:14 +0200 Subject: [PATCH 40/70] Fixed a regression introduced in 9f7a01ef2bed8c0395a970286e8f87fd7d344b3b. --- tests/view_tests/tests/test_i18n.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index 5a3bdd1062..2da17dd0f5 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -217,5 +217,6 @@ class JavascriptI18nTests(LiveServerTestCase): def test_escaping(self): extended_apps = list(settings.INSTALLED_APPS) + ['view_tests'] with self.settings(INSTALLED_APPS=extended_apps): - response = self.client.get('%s%s' % (self.live_server_url, '/jsi18n_admin/')) + # Force a language via GET otherwise the gettext functions are a noop! + response = self.client.get('/jsi18n_admin/?language=de') self.assertContains(response, '\\x04') From 371dbbe6e01f0fd0276998f6d7f78c86b49d2e97 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sun, 5 May 2013 15:52:07 +0200 Subject: [PATCH 41/70] Ensured that the javascript_catalog view doesn't leak translations. --- django/views/i18n.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/django/views/i18n.py b/django/views/i18n.py index c7ee1b33bd..37ec10b552 100644 --- a/django/views/i18n.py +++ b/django/views/i18n.py @@ -6,7 +6,7 @@ from django import http from django.conf import settings from django.template import Context, Template from django.utils import importlib -from django.utils.translation import check_for_language, activate, to_locale, get_language +from django.utils.translation import check_for_language, to_locale, get_language from django.utils.encoding import smart_text from django.utils.formats import get_format_modules, get_format from django.utils._os import upath @@ -205,17 +205,18 @@ def javascript_catalog(request, domain='djangojs', packages=None): go to the djangojs domain. But this might be needed if you deliver your JavaScript source from Django templates. """ - if request.GET: - if 'language' in request.GET: - if check_for_language(request.GET['language']): - activate(request.GET['language']) + default_locale = to_locale(settings.LANGUAGE_CODE) + locale = to_locale(get_language()) + + if request.GET and 'language' in request.GET: + if check_for_language(request.GET['language']): + locale = to_locale(request.GET['language']) + if packages is None: packages = ['django.conf'] if isinstance(packages, six.string_types): packages = packages.split('+') packages = [p for p in packages if p == 'django.conf' or p in settings.INSTALLED_APPS] - default_locale = to_locale(settings.LANGUAGE_CODE) - locale = to_locale(get_language()) t = {} paths = [] en_selected = locale.startswith('en') From a96bff179a57c6c7fb79585120088f155831a6ae Mon Sep 17 00:00:00 2001 From: Christopher Allen-Poole Date: Sun, 5 May 2013 10:22:25 -0400 Subject: [PATCH 42/70] Found a mistake in SQL documentation --- docs/topics/db/sql.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt index 3bf0684b29..2ec31a4988 100644 --- a/docs/topics/db/sql.txt +++ b/docs/topics/db/sql.txt @@ -211,7 +211,7 @@ For example:: from django.db import connection - def my_custom_sql(): + def my_custom_sql(self): cursor = connection.cursor() cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz]) From 86d3079d5797811c1e118cfd600a913685212165 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 6 May 2013 06:19:52 -0400 Subject: [PATCH 43/70] Fixed #20305 - Added include for polls/static to MANIFEST.in in tutorial. Thanks monuszko for the report. --- docs/intro/reusable-apps.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt index a19478fc3a..4247b45238 100644 --- a/docs/intro/reusable-apps.txt +++ b/docs/intro/reusable-apps.txt @@ -223,6 +223,7 @@ Create a file ``django-polls/setup.py`` with the following contents:: include LICENSE include README.rst + recursive-include polls/static * recursive-include polls/templates * 7. It's optional, but recommended, to include detailed documentation with your From e88680899490b9ed8b58c88ee78dc189506204c5 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 6 May 2013 13:45:24 -0400 Subject: [PATCH 44/70] Fixed #20274 - Added some clarifying section headings in the test docs. --- docs/topics/testing/advanced.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt index 26dc8ee1ae..9cadf796fa 100644 --- a/docs/topics/testing/advanced.txt +++ b/docs/topics/testing/advanced.txt @@ -340,6 +340,9 @@ Methods Testing utilities ----------------- +django.test.utils +~~~~~~~~~~~~~~~~~ + .. module:: django.test.utils :synopsis: Helpers to write custom test runners. @@ -358,10 +361,13 @@ utility methods in the ``django.test.utils`` module. magic hooks into the template system and restoring normal email services. +django.db.connection.creation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + .. currentmodule:: django.db.connection.creation -The creation module of the database backend (``connection.creation``) -also provides some utilities that can be useful during testing. +The creation module of the database backend also provides some utilities that +can be useful during testing. .. function:: create_test_db([verbosity=1, autoclobber=False]) From bc02a963db3aeebf7c349d83a492b6e093f42b00 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 6 May 2013 13:55:02 -0400 Subject: [PATCH 45/70] Fixed #20177 - Corrected docs for django.test.utils.setup_test_environment. Thanks vlad.london.uk@ for the report. --- docs/intro/tutorial05.txt | 7 +++++++ docs/topics/testing/advanced.txt | 12 ++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/intro/tutorial05.txt b/docs/intro/tutorial05.txt index 3b0a95f253..261a5038ab 100644 --- a/docs/intro/tutorial05.txt +++ b/docs/intro/tutorial05.txt @@ -326,6 +326,13 @@ in the shell:: >>> from django.test.utils import setup_test_environment >>> setup_test_environment() +:meth:`~django.test.utils.setup_test_environment` installs a template renderer +which will allow us to examine some additional attributes on responses such as +``response.context`` that otherwise wouldn't be available. Note that this +method *does not* setup a test database, so the following will be run against +the existing database and the output may differ slightly depending on what +polls you already created. + Next we need to import the test client class (later in ``tests.py`` we will use the :class:`django.test.TestCase` class, which comes with its own client, so this won't be required):: diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt index 9cadf796fa..5f2fa65bed 100644 --- a/docs/topics/testing/advanced.txt +++ b/docs/topics/testing/advanced.txt @@ -163,10 +163,12 @@ environment first. Django provides a convenience method to do this:: >>> from django.test.utils import setup_test_environment >>> setup_test_environment() -This convenience method sets up the test database, and puts other -Django features into modes that allow for repeatable testing. +:func:`~django.test.utils.setup_test_environment` puts several Django features +into modes that allow for repeatable testing, but does not create the test +databases; :func:`django.test.simple.DjangoTestSuiteRunner.setup_databases` +takes care of that. -The call to :meth:`~django.test.utils.setup_test_environment` is made +The call to :func:`~django.test.utils.setup_test_environment` is made automatically as part of the setup of ``./manage.py test``. You only need to manually invoke this method if you're not using running your tests via Django's test runner. @@ -282,7 +284,9 @@ Methods .. method:: DjangoTestSuiteRunner.setup_test_environment(**kwargs) - Sets up the test environment ready for testing. + Sets up the test environment by calling + :func:`~django.test.utils.setup_test_environment` and setting + :setting:`DEBUG` to ``False``. .. method:: DjangoTestSuiteRunner.build_suite(test_labels, extra_tests=None, **kwargs) From 1ad83145dff92117839400e4cfacdfee6473b2a6 Mon Sep 17 00:00:00 2001 From: Mike Fogel Date: Mon, 6 May 2013 22:43:06 -0700 Subject: [PATCH 46/70] Remove outdated ForeignKey manager documentation. --- docs/topics/db/queries.txt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index c4f7ee59ae..2553eac27a 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -1112,15 +1112,6 @@ above example code would look like this:: >>> b.entries.filter(headline__contains='Lennon') >>> b.entries.count() -You cannot access a reverse :class:`~django.db.models.ForeignKey` -:class:`~django.db.models.Manager` from the class; it must be accessed from an -instance:: - - >>> Blog.entry_set - Traceback: - ... - AttributeError: "Manager must be accessed via instance". - In addition to the :class:`~django.db.models.query.QuerySet` methods defined in "Retrieving objects" above, the :class:`~django.db.models.ForeignKey` :class:`~django.db.models.Manager` has additional methods used to handle the From 99a6f0e77c6e02b310687667d41df56c17b953bf Mon Sep 17 00:00:00 2001 From: Tai Lee Date: Mon, 6 May 2013 13:32:07 +1000 Subject: [PATCH 47/70] Fixed #20354 -- `makemessages` no longer crashes with `UnicodeDecodeError` Handle the `UnicodeDecodeError` exception, send a warning to `stdout` with the file name and location, and continue processing other files. --- .../core/management/commands/makemessages.py | 5 ++++- tests/i18n/commands/extraction.py | 22 +++++++++++++------ tests/i18n/commands/not_utf8.sample | 1 + 3 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 tests/i18n/commands/not_utf8.sample diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py index a7e98173ac..bc171176c2 100644 --- a/django/core/management/commands/makemessages.py +++ b/django/core/management/commands/makemessages.py @@ -294,7 +294,10 @@ class Command(NoArgsCommand): os.unlink(potfile) for f in file_list: - f.process(self, potfile, self.domain, self.keep_pot) + try: + f.process(self, potfile, self.domain, self.keep_pot) + except UnicodeDecodeError: + self.stdout.write("UnicodeDecodeError: skipped file %s in %s" % (f.file, f.dirpath)) return potfile def find_files(self, root): diff --git a/tests/i18n/commands/extraction.py b/tests/i18n/commands/extraction.py index 80e4ee0110..7c482e58fb 100644 --- a/tests/i18n/commands/extraction.py +++ b/tests/i18n/commands/extraction.py @@ -30,6 +30,10 @@ class ExtractorTests(SimpleTestCase): return shutil.rmtree(dname) + def rmfile(self, filepath): + if os.path.exists(filepath): + os.remove(filepath) + def tearDown(self): os.chdir(self.test_dir) try: @@ -126,18 +130,22 @@ class BasicExtractorTests(ExtractorTests): # Check that the temporary file was cleaned up self.assertFalse(os.path.exists('./templates/template_with_error.tpl.py')) + def test_unicode_decode_error(self): + os.chdir(self.test_dir) + shutil.copyfile('./not_utf8.sample', './not_utf8.txt') + self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'not_utf8.txt')) + stdout = StringIO() + management.call_command('makemessages', locale=LOCALE, stdout=stdout) + self.assertIn("UnicodeDecodeError: skipped file not_utf8.txt in .", + force_text(stdout.getvalue())) + def test_extraction_warning(self): """test xgettext warning about multiple bare interpolation placeholders""" os.chdir(self.test_dir) shutil.copyfile('./code.sample', './code_sample.py') + self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'code_sample.py')) stdout = StringIO() - try: - management.call_command('makemessages', locale=LOCALE, stdout=stdout) - finally: - try: - os.remove('./code_sample.py') - except OSError: - pass + management.call_command('makemessages', locale=LOCALE, stdout=stdout) self.assertIn("code_sample.py:4", force_text(stdout.getvalue())) def test_template_message_context_extractor(self): diff --git a/tests/i18n/commands/not_utf8.sample b/tests/i18n/commands/not_utf8.sample new file mode 100644 index 0000000000..6449f52803 --- /dev/null +++ b/tests/i18n/commands/not_utf8.sample @@ -0,0 +1 @@ +Copyright (c) 2009 Øyvind Sean Kinsey, oyvind@kinsey.no \ No newline at end of file From d89b421352998020b9a9f2e7c3fd2ab8cc352593 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Tue, 7 May 2013 17:05:01 -0600 Subject: [PATCH 48/70] Python 2.5 is not relevant to master. --- docs/topics/testing/overview.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index cd7c98c85c..9228a07b31 100644 --- a/docs/topics/testing/overview.txt +++ b/docs/topics/testing/overview.txt @@ -25,7 +25,7 @@ module defines tests in class-based approach. adding some extremely useful features. To ensure that every Django project can benefit from these new features, Django ships with a copy of unittest2_, a copy of the Python 2.7 unittest library, - backported for Python 2.5 compatibility. + backported for Python 2.6 compatibility. To access this library, Django provides the ``django.utils.unittest`` module alias. If you are using Python From 6c6c67f5a6150ca89bbcfb06561a61d2cd7730cf Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Tue, 7 May 2013 17:07:29 -0600 Subject: [PATCH 49/70] Fixed inconsistent punctuation in tutorial 5. --- docs/intro/tutorial05.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/intro/tutorial05.txt b/docs/intro/tutorial05.txt index 261a5038ab..97d1d96ad7 100644 --- a/docs/intro/tutorial05.txt +++ b/docs/intro/tutorial05.txt @@ -15,9 +15,9 @@ What are automated tests? Tests are simple routines that check the operation of your code. Testing operates at different levels. Some tests might apply to a tiny detail -- *does a particular model method return values as expected?*, while others -examine the overall operation of the software - *does a sequence of user inputs -on the site produce the desired result?* That's no different from the kind of +(*does a particular model method return values as expected?*) while others +examine the overall operation of the software (*does a sequence of user inputs +on the site produce the desired result?*). That's no different from the kind of testing you did earlier in :doc:`Tutorial 1 `, using the shell to examine the behavior of a method, or running the application and entering data to check how it behaves. From bc46f67fa83a82d48eafc0e3bbd368a3746e3936 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Tue, 7 May 2013 18:18:42 -0600 Subject: [PATCH 50/70] Fixed Sphinx error in tutorial 1. --- docs/intro/tutorial01.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index a0e776ae69..d623bd8451 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -598,8 +598,7 @@ for your own sanity when dealing with the interactive prompt, but also because objects' representations are used throughout Django's automatically-generated admin. -.. admonition:: :meth:`~django.db.models.Model.__unicode__` or - :meth:`~django.db.models.Model.__str__`? +.. admonition:: `__unicode__` or `__str__`? On Python 3, things are simpler, just use :meth:`~django.db.models.Model.__str__` and forget about From a22e15effc957df977233c64278b364a955b9ce9 Mon Sep 17 00:00:00 2001 From: Mike Fogel Date: Tue, 7 May 2013 21:50:59 -0700 Subject: [PATCH 51/70] Remove unnecessary check on __set__ parameters. --- django/contrib/contenttypes/generic.py | 6 ------ django/db/models/fields/related.py | 15 --------------- 2 files changed, 21 deletions(-) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index 3e132bb3a4..cc03f799f3 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -137,9 +137,6 @@ class GenericForeignKey(six.with_metaclass(RenameGenericForeignKeyMethods)): return rel_obj def __set__(self, instance, value): - if instance is None: - raise AttributeError("%s must be accessed via instance" % self.related.opts.object_name) - ct = None fk = None if value is not None: @@ -280,9 +277,6 @@ class ReverseGenericRelatedObjectsDescriptor(object): return manager def __set__(self, instance, value): - if instance is None: - raise AttributeError("Manager must be accessed via instance") - manager = self.__get__(instance) manager.clear() for obj in value: diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index e85b3301bf..256c3e0f61 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -206,9 +206,6 @@ class SingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjectDescri return rel_obj def __set__(self, instance, value): - if instance is None: - raise AttributeError("%s must be accessed via instance" % self.related.opts.object_name) - # The similarity of the code below to the code in # ReverseSingleRelatedObjectDescriptor is annoying, but there's a bunch # of small differences that would make a common base class convoluted. @@ -312,9 +309,6 @@ class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjec return rel_obj def __set__(self, instance, value): - if instance is None: - raise AttributeError("%s must be accessed via instance" % self.field.name) - # If null=True, we can assign null here, but otherwise the value needs # to be an instance of the related class. if value is None and self.field.null == False: @@ -384,9 +378,6 @@ class ForeignRelatedObjectsDescriptor(object): return self.related_manager_cls(instance) def __set__(self, instance, value): - if instance is None: - raise AttributeError("Manager must be accessed via instance") - manager = self.__get__(instance) # If the foreign key can support nulls, then completely clear the related set. # Otherwise, just move the named objects into the set. @@ -767,9 +758,6 @@ class ManyRelatedObjectsDescriptor(object): return manager def __set__(self, instance, value): - if instance is None: - raise AttributeError("Manager must be accessed via instance") - if not self.related.field.rel.through._meta.auto_created: opts = self.related.field.rel.through._meta raise AttributeError("Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s.%s's Manager instead." % (opts.app_label, opts.object_name)) @@ -824,9 +812,6 @@ class ReverseManyRelatedObjectsDescriptor(object): return manager def __set__(self, instance, value): - if instance is None: - raise AttributeError("Manager must be accessed via instance") - if not self.field.rel.through._meta.auto_created: opts = self.field.rel.through._meta raise AttributeError("Cannot set values on a ManyToManyField which specifies an intermediary model. Use %s.%s's Manager instead." % (opts.app_label, opts.object_name)) From de8aa3a9a9098af35d5b56442c39dd2ed56b3299 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Wed, 8 May 2013 09:55:40 +0200 Subject: [PATCH 52/70] Fixed #20256 -- Corrected startproject --template help text Thanks n0nam3 for the patch. --- django/core/management/templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/core/management/templates.py b/django/core/management/templates.py index 0f45399109..893e5c95af 100644 --- a/django/core/management/templates.py +++ b/django/core/management/templates.py @@ -43,7 +43,7 @@ class TemplateCommand(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--template', action='store', dest='template', - help='The dotted import path to load the template from.'), + help='The path or URL to load the template from.'), make_option('--extension', '-e', dest='extensions', action='append', default=['py'], help='The file extension(s) to render (default: "py"). ' From 1fff8daf889822aa1dd9adba19c94bb522d960b4 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 8 May 2013 12:57:35 +0200 Subject: [PATCH 53/70] Fixed test failures on MySQL. Some tests failed when the time zone definitions were loaded in MySQL and pytz wasn't installed. This setup isn't supported. --- django/db/backends/mysql/base.py | 14 ++++++++++++++ django/db/models/sql/compiler.py | 2 +- docs/ref/models/querysets.txt | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index d26bd9fd60..d9f66ad5ef 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -30,6 +30,11 @@ if (version < (1, 2, 1) or (version[:3] == (1, 2, 1) and from MySQLdb.converters import conversions, Thing2Literal from MySQLdb.constants import FIELD_TYPE, CLIENT +try: + import pytz +except ImportError: + pytz = None + from django.conf import settings from django.db import utils from django.db.backends import * @@ -186,6 +191,15 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def has_zoneinfo_database(self): + # MySQL accepts full time zones names (eg. Africa/Nairobi) but rejects + # abbreviations (eg. EAT). When pytz isn't installed and the current + # time zone is LocalTimezone (the only sensible value in this + # context), the current time zone name will be an abbreviation. As a + # consequence, MySQL cannot perform time zone conversions reliably. + if pytz is None: + return False + + # Test if the time zone definitions are installed. cursor = self.connection.cursor() cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1") return cursor.fetchone() is not None diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 3444b74ac3..018fc098ea 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -1092,7 +1092,7 @@ class SQLDateTimeCompiler(SQLCompiler): if datetime is None: raise ValueError("Database returned an invalid value " "in QuerySet.dates(). Are time zone " - "definitions installed?") + "definitions and pytz installed?") datetime = datetime.replace(tzinfo=None) datetime = timezone.make_aware(datetime, self.query.tzinfo) yield datetime diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 3dde0d5411..d27214a66c 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -627,7 +627,8 @@ object. If it's ``None``, Django uses the :ref:`current time zone - SQLite: install pytz_ — conversions are actually performed in Python. - PostgreSQL: no requirements (see `Time Zones`_). - Oracle: no requirements (see `Choosing a Time Zone File`_). - - MySQL: load the time zone tables with `mysql_tzinfo_to_sql`_. + - MySQL: install pytz_ and load the time zone tables with + `mysql_tzinfo_to_sql`_. .. _pytz: http://pytz.sourceforge.net/ .. _Time Zones: http://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-TIMEZONES From e81e319f15f448d550d7e30b204d9490a9999b99 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 5 May 2013 19:44:43 +0200 Subject: [PATCH 54/70] Fixed #20025 -- Pointed to a MySQLdb fork for Python 3. Made a few minor compatibility adjustments. --- django/db/backends/mysql/base.py | 3 ++- django/db/backends/mysql/validation.py | 3 ++- docs/ref/databases.txt | 13 +++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index d9f66ad5ef..945d737354 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -405,8 +405,9 @@ class DatabaseWrapper(BaseDatabaseWrapper): kwargs = { 'conv': django_conversions, 'charset': 'utf8', - 'use_unicode': True, } + if not six.PY3: + kwargs['use_unicode'] = True settings_dict = self.settings_dict if settings_dict['USER']: kwargs['user'] = settings_dict['USER'] diff --git a/django/db/backends/mysql/validation.py b/django/db/backends/mysql/validation.py index de7474d1e5..2ce957cce7 100644 --- a/django/db/backends/mysql/validation.py +++ b/django/db/backends/mysql/validation.py @@ -10,6 +10,7 @@ class DatabaseValidation(BaseDatabaseValidation): from django.db import models varchar_fields = (models.CharField, models.CommaSeparatedIntegerField, models.SlugField) - if isinstance(f, varchar_fields) and f.max_length > 255 and f.unique: + if (isinstance(f, varchar_fields) and f.unique + and (f.max_length is None or int(f.max_length) > 255)): msg = '"%(name)s": %(cls)s cannot have a "max_length" greater than 255 when using "unique=True".' errors.add(opts, msg % {'name': f.name, 'cls': f.__class__.__name__}) diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 35f0cc6b41..f28079ae33 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -252,6 +252,15 @@ required for full MySQL support in Django. .. _MySQLdb: http://sourceforge.net/projects/mysql-python +Python 3 +-------- + +At the time of writing, the latest release of MySQLdb (1.2.4) doesn't support +Python 3. In order to use MySQL under Python 3, you'll have to install an +unofficial fork, such as `MySQL-for-Python-3`_. + +.. _MySQL-for-Python-3: https://github.com/clelland/MySQL-for-Python-3 + Creating your database ---------------------- @@ -361,8 +370,8 @@ Here's a sample configuration which uses a MySQL option file:: default-character-set = utf8 Several other MySQLdb connection options may be useful, such as ``ssl``, -``use_unicode``, ``init_command``, and ``sql_mode``. Consult the -`MySQLdb documentation`_ for more details. +``init_command``, and ``sql_mode``. Consult the `MySQLdb documentation`_ for +more details. .. _MySQL option file: http://dev.mysql.com/doc/refman/5.0/en/option-files.html .. _MySQLdb documentation: http://mysql-python.sourceforge.net/ From 86b4ac665afe793a457ae84dfa1dfbbbb7e3c2bf Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 8 May 2013 12:56:50 +0200 Subject: [PATCH 55/70] [py3] Stopped iterating on exceptions. Refs #20025. --- django/db/backends/mysql/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 945d737354..dbddad7312 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -125,7 +125,7 @@ class CursorWrapper(object): except Database.OperationalError as e: # Map some error codes to IntegrityError, since they seem to be # misclassified and Django would prefer the more logical place. - if e[0] in self.codes_for_integrityerror: + if e.args[0] in self.codes_for_integrityerror: six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2]) raise @@ -135,7 +135,7 @@ class CursorWrapper(object): except Database.OperationalError as e: # Map some error codes to IntegrityError, since they seem to be # misclassified and Django would prefer the more logical place. - if e[0] in self.codes_for_integrityerror: + if e.args[0] in self.codes_for_integrityerror: six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2]) raise From 7476d96f83a004d674244aeb7a66289035427396 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 8 May 2013 15:33:02 +0200 Subject: [PATCH 56/70] Marked tests of BinaryFields as expected failures on MySQL and Python 3. Current ports of MySQLdb are very buggy in this area. --- docs/ref/databases.txt | 3 +++ tests/model_fields/tests.py | 6 +++++- tests/serializers_regress/tests.py | 7 ++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index f28079ae33..2ef048216f 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -259,6 +259,9 @@ At the time of writing, the latest release of MySQLdb (1.2.4) doesn't support Python 3. In order to use MySQL under Python 3, you'll have to install an unofficial fork, such as `MySQL-for-Python-3`_. +This port is still in alpha. In particular, it doesn't support binary data, +making it impossible to use :class:`django.db.models.BinaryField`. + .. _MySQL-for-Python-3: https://github.com/clelland/MySQL-for-Python-3 Creating your database diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index 9af8325040..035a5c2ae3 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -6,7 +6,7 @@ from decimal import Decimal from django import test from django import forms from django.core.exceptions import ValidationError -from django.db import models, IntegrityError +from django.db import connection, models, IntegrityError from django.db.models.fields.files import FieldFile from django.utils import six from django.utils import unittest @@ -455,6 +455,10 @@ class BinaryFieldTests(test.TestCase): # Test default value self.assertEqual(bytes(dm.short_data), b'\x08') + if connection.vendor == 'mysql' and six.PY3: + # Existing MySQL DB-API drivers fail on binary data. + test_set_and_retrieve = unittest.expectedFailure(test_set_and_retrieve) + def test_max_length(self): dm = DataModel(short_data=self.binary_data*4) self.assertRaises(ValidationError, dm.full_clean) diff --git a/tests/serializers_regress/tests.py b/tests/serializers_regress/tests.py index 72e3825d91..04b4d4c839 100644 --- a/tests/serializers_regress/tests.py +++ b/tests/serializers_regress/tests.py @@ -26,7 +26,7 @@ from django.test import TestCase from django.utils import six from django.utils.encoding import force_text from django.utils.functional import curry -from django.utils.unittest import skipUnless +from django.utils.unittest import expectedFailure, skipUnless from .models import (BinaryData, BooleanData, CharData, DateData, DateTimeData, EmailData, FileData, FilePathData, DecimalData, FloatData, IntegerData, IPAddressData, @@ -459,6 +459,11 @@ def serializerTest(format, self): for klass, count in instance_count.items(): self.assertEqual(count, klass.objects.count()) +if connection.vendor == 'mysql' and six.PY3: + # Existing MySQL DB-API drivers fail on binary data. + serializerTest = expectedFailure(serializerTest) + + def naturalKeySerializerTest(format, self): # Create all the objects defined in the test data objects = [] From ea3a378c22c0a61715663b9bf807c786233f70c1 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 8 May 2013 12:44:58 -0700 Subject: [PATCH 57/70] Added an HTTP status code to Django's WSGI application that was missing (reason unknown). --- django/core/handlers/wsgi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index 3c88aeac6d..c348c6c8da 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -57,6 +57,7 @@ STATUS_CODE_TEXT = { 415: 'UNSUPPORTED MEDIA TYPE', 416: 'REQUESTED RANGE NOT SATISFIABLE', 417: 'EXPECTATION FAILED', + 418: "I'M A TEAPOT", 422: 'UNPROCESSABLE ENTITY', 423: 'LOCKED', 424: 'FAILED DEPENDENCY', From 832b4a5722ba6b55e7b17c3bac6614ecca9aa88d Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 8 May 2013 23:12:04 +0200 Subject: [PATCH 58/70] Marked a test as an expected failure on MySQL and Python 3.2. This test hits a bug in current ports of MySQLdb. --- tests/model_regress/tests.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/model_regress/tests.py b/tests/model_regress/tests.py index c1e864c270..71ccb071d2 100644 --- a/tests/model_regress/tests.py +++ b/tests/model_regress/tests.py @@ -2,12 +2,14 @@ from __future__ import absolute_import, unicode_literals import datetime from operator import attrgetter +import sys from django.core.exceptions import ValidationError from django.test import TestCase, skipUnlessDBFeature from django.utils import six from django.utils import tzinfo -from django.db import router +from django.utils import unittest +from django.db import connection, router from django.db.models.sql import InsertQuery from .models import (Worker, Article, Party, Event, Department, @@ -131,6 +133,11 @@ class ModelTests(TestCase): attrgetter("when") ) + if (3,) <= sys.version_info < (3, 3) and connection.vendor == 'mysql': + # In Python < 3.3, datetime.strftime raises an exception for years + # below 1000, and existing MySQL DB-API drivers hit this problem. + test_date_lookup = unittest.expectedFailure(test_date_lookup) + def test_date_filter_null(self): # Date filtering was failing with NULL date values in SQLite # (regression test for #3501, amongst other things). From 1556b1c3b7a18cadf4d66d2ab6301d7fe8646ba5 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Thu, 9 May 2013 00:49:05 +0100 Subject: [PATCH 59/70] Removed fragile admin validation of fields on ModelForm Refs #19445 --- django/contrib/admin/options.py | 8 ++++++-- django/contrib/admin/validation.py | 9 --------- tests/admin_validation/tests.py | 8 -------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index b35f100fda..543655b357 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -13,7 +13,7 @@ from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_o from django.contrib.admin.templatetags.admin_static import static from django.contrib import messages from django.views.decorators.csrf import csrf_protect -from django.core.exceptions import PermissionDenied, ValidationError +from django.core.exceptions import PermissionDenied, ValidationError, FieldError from django.core.paginator import Paginator from django.core.urlresolvers import reverse from django.db import models, transaction, router @@ -488,7 +488,11 @@ class ModelAdmin(BaseModelAdmin): "formfield_callback": partial(self.formfield_for_dbfield, request=request), } defaults.update(kwargs) - return modelform_factory(self.model, **defaults) + try: + return modelform_factory(self.model, **defaults) + except FieldError as e: + raise FieldError('%s. Check fields/fieldsets/exclude attributes of class %s.' + % (e, self.__class__.__name__)) def get_changelist(self, request, **kwargs): """ diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py index a02bb7a316..178df9d844 100644 --- a/django/contrib/admin/validation.py +++ b/django/contrib/admin/validation.py @@ -387,15 +387,6 @@ def check_formfield(cls, model, opts, label, field): except KeyError: raise ImproperlyConfigured("'%s.%s' refers to field '%s' that " "is missing from the form." % (cls.__name__, label, field)) - else: - get_form_is_overridden = hasattr(cls, 'get_form') and cls.get_form != ModelAdmin.get_form - if not get_form_is_overridden: - fields = fields_for_model(model) - try: - fields[field] - except KeyError: - raise ImproperlyConfigured("'%s.%s' refers to field '%s' that " - "is missing from the form." % (cls.__name__, label, field)) def fetch_attr(cls, model, opts, label, field): try: diff --git a/tests/admin_validation/tests.py b/tests/admin_validation/tests.py index 5b2c45f6f2..5329cc7e8c 100644 --- a/tests/admin_validation/tests.py +++ b/tests/admin_validation/tests.py @@ -16,10 +16,6 @@ class ValidFields(admin.ModelAdmin): form = SongForm fields = ['title'] -class InvalidFields(admin.ModelAdmin): - form = SongForm - fields = ['spam'] - class ValidFormFieldsets(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): class ExtraFieldForm(SongForm): @@ -49,10 +45,6 @@ class ValidationTestCase(TestCase): # Regression test for #8027: custom ModelForms with fields/fieldsets """ validate(ValidFields, Song) - self.assertRaisesMessage(ImproperlyConfigured, - "'InvalidFields.fields' refers to field 'spam' that is missing from the form.", - validate, - InvalidFields, Song) def test_custom_get_form_with_fieldsets(self): """ From 2f8f2ad1d7762b2c46cdc707b4921685bcea3612 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Thu, 9 May 2013 01:45:34 +0100 Subject: [PATCH 60/70] Removed some failing tests missed in 1556b1c3b7a18cadf4d66d2ab6301d7fe8646ba5 --- tests/modeladmin/tests.py | 42 --------------------------------------- 1 file changed, 42 deletions(-) diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index e5450ab8ff..a63984a8a9 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -616,17 +616,6 @@ class ValidationTests(unittest.TestCase): ValidationTestModel, ) - class ValidationTestModelAdmin(ModelAdmin): - fieldsets = (("General", {"fields": ("non_existent_field",)}),) - - six.assertRaisesRegex(self, - ImproperlyConfigured, - "'ValidationTestModelAdmin.fieldsets\[0\]\[1\]\['fields'\]' refers to field 'non_existent_field' that is missing from the form.", - validate, - ValidationTestModelAdmin, - ValidationTestModel, - ) - class ValidationTestModelAdmin(ModelAdmin): fieldsets = (("General", {"fields": ("name",)}),) @@ -684,22 +673,6 @@ class ValidationTests(unittest.TestCase): def test_fieldsets_with_custom_form_validation(self): - class BandAdmin(ModelAdmin): - - fieldsets = ( - ('Band', { - 'fields': ('non_existent_field',) - }), - ) - - six.assertRaisesRegex(self, - ImproperlyConfigured, - "'BandAdmin.fieldsets\[0\]\[1\]\['fields'\]' refers to field 'non_existent_field' that is missing from the form.", - validate, - BandAdmin, - Band, - ) - class BandAdmin(ModelAdmin): fieldsets = ( ('Band', { @@ -1371,21 +1344,6 @@ class ValidationTests(unittest.TestCase): ValidationTestModel, ) - class ValidationTestInline(TabularInline): - model = ValidationTestInlineModel - fields = ("non_existent_field",) - - class ValidationTestModelAdmin(ModelAdmin): - inlines = [ValidationTestInline] - - six.assertRaisesRegex(self, - ImproperlyConfigured, - "'ValidationTestInline.fields' refers to field 'non_existent_field' that is missing from the form.", - validate, - ValidationTestModelAdmin, - ValidationTestModel, - ) - def test_fk_name_validation(self): class ValidationTestInline(TabularInline): From 3d595c3bc38cf939503b69ce7a2802d5663f85b9 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 9 May 2013 15:42:14 +0200 Subject: [PATCH 61/70] Fixed #20215 -- Disabled persistent connections by default. --- django/db/utils.py | 2 +- docs/howto/deployment/checklist.txt | 9 ++++++++ docs/ref/databases.txt | 33 ++++++++++++++++++----------- docs/ref/settings.txt | 2 +- docs/releases/1.6.txt | 24 ++------------------- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/django/db/utils.py b/django/db/utils.py index 936b42039d..e84060f9b3 100644 --- a/django/db/utils.py +++ b/django/db/utils.py @@ -169,7 +169,7 @@ class ConnectionHandler(object): conn.setdefault('ENGINE', 'django.db.backends.dummy') if conn['ENGINE'] == 'django.db.backends.' or not conn['ENGINE']: conn['ENGINE'] = 'django.db.backends.dummy' - conn.setdefault('CONN_MAX_AGE', 600) + conn.setdefault('CONN_MAX_AGE', 0) conn.setdefault('OPTIONS', {}) conn.setdefault('TIME_ZONE', 'UTC' if settings.USE_TZ else settings.TIME_ZONE) for setting in ['NAME', 'USER', 'PASSWORD', 'HOST', 'PORT']: diff --git a/docs/howto/deployment/checklist.txt b/docs/howto/deployment/checklist.txt index b092048870..b72be75497 100644 --- a/docs/howto/deployment/checklist.txt +++ b/docs/howto/deployment/checklist.txt @@ -157,6 +157,15 @@ Performance optimizations Setting :setting:`DEBUG = False ` disables several features that are only useful in development. In addition, you can tune the following settings. +:setting:`CONN_MAX_AGE` +----------------------- + +Enabling `persistent database connections `_ +can result in a nice speed-up when connecting to the database accounts for a +significant part of the request processing time. + +This helps a lot on virtualized hosts with limited network performance. + :setting:`TEMPLATE_LOADERS` --------------------------- diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 2ef048216f..7555acaaba 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -22,14 +22,14 @@ Persistent connections .. versionadded:: 1.6 Persistent connections avoid the overhead of re-establishing a connection to -the database in each request. By default, connections are kept open for up 10 -minutes — if not specified, :setting:`CONN_MAX_AGE` defaults to 600 seconds. +the database in each request. They're controlled by the +:setting:`CONN_MAX_AGE` parameter which defines the maximum lifetime of a +connection. It can be set independently for each database. -Django 1.5 and earlier didn't have persistent connections. To restore the -legacy behavior of closing the connection at the end of every request, set -:setting:`CONN_MAX_AGE` to ``0``. - -For unlimited persistent connections, set :setting:`CONN_MAX_AGE` to ``None``. +The default value is ``0``, preserving the historical behavior of closing the +database connection at the end of each request. To enable persistent +connections, set :setting:`CONN_MAX_AGE` to a positive number of seconds. For +unlimited persistent connections, set it to ``None``. Connection management ~~~~~~~~~~~~~~~~~~~~~ @@ -64,13 +64,22 @@ least as many simultaneous connections as you have worker threads. Sometimes a database won't be accessed by the majority of your views, for example because it's the database of an external system, or thanks to caching. -In such cases, you should set :setting:`CONN_MAX_AGE` to a lower value, or -even ``0``, because it doesn't make sense to maintain a connection that's -unlikely to be reused. This will help keep the number of simultaneous -connections to this database small. +In such cases, you should set :setting:`CONN_MAX_AGE` to a low value or even +``0``, because it doesn't make sense to maintain a connection that's unlikely +to be reused. This will help keep the number of simultaneous connections to +this database small. The development server creates a new thread for each request it handles, -negating the effect of persistent connections. +negating the effect of persistent connections. Don't enable them during +development. + +When Django establishes a connection to the database, it sets up appropriate +parameters, depending on the backend being used. If you enable persistent +connections, this setup is no longer repeated every request. If you modify +parameters such as the connection's isolation level or time zone, you should +either restore Django's defaults at the end of each request, force an +appropriate value at the beginning of each request, or disable persistent +connections. .. _postgresql-notes: diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index f2d418d4d9..04b42aeeb2 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -509,7 +509,7 @@ CONN_MAX_AGE .. versionadded:: 1.6 -Default: ``600`` +Default: ``0`` The lifetime of a database connection, in seconds. Use ``0`` to close database connections at the end of each request — Django's historical behavior — and diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 3e15f5b767..611b661415 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -66,13 +66,8 @@ Persistent database connections Django now supports reusing the same database connection for several requests. This avoids the overhead of re-establishing a connection at the beginning of -each request. - -By default, database connections will kept open for 10 minutes. This behavior -is controlled by the :setting:`CONN_MAX_AGE` setting. To restore the previous -behavior of closing the connection at the end of each request, set -:setting:`CONN_MAX_AGE` to ``0``. See :ref:`persistent-database-connections` -for details. +each request. For backwards compatibility, this feature is disabled by +default. See :ref:`persistent-database-connections` for details. Time zone aware aggregation ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -298,21 +293,6 @@ Django 1.6 introduces ``hour``, ``minute``, and ``second`` lookups on ``hour``, ``minute``, or ``second``, the new lookups will clash with you field names. Append an explicit :lookup:`exact` lookup if this is an issue. -Persistent database connections -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Connection setup not repeated for each request -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When Django establishes a connection to the database, it sets up appropriate -parameters, depending on the backend being used. Since `persistent database -connections `_ are enabled by default in -Django 1.6, this setup isn't repeated at every request any more. If you -modifiy parameters such as the connection's isolation level or time zone, you -should either restore Django's defaults at the end of each request, force an -appropriate value at the beginning of each request, or disable persistent -connections. - ``BooleanField`` no longer defaults to ``False`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 6634cb7b53bc35241e070f1896ff2fc439c19d0f Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 9 May 2013 06:55:25 -0700 Subject: [PATCH 62/70] Removed a test that didn't make sense; code could never be called the way the test was written. --- tests/many_to_one_regress/tests.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/many_to_one_regress/tests.py b/tests/many_to_one_regress/tests.py index a9969a7e90..035ba53bff 100644 --- a/tests/many_to_one_regress/tests.py +++ b/tests/many_to_one_regress/tests.py @@ -60,10 +60,6 @@ class ManyToOneRegressionTests(TestCase): self.assertRaises(ValueError, Child, name='xyzzy', parent=None) self.assertRaises(ValueError, Child.objects.create, name='xyzzy', parent=None) - # Trying to assign to unbound attribute raises AttributeError - six.assertRaisesRegex(self, AttributeError, "must be accessed via instance", - Child.parent.__set__, None, p) - # Creation using keyword argument should cache the related object. p = Parent.objects.get(name="Parent") c = Child(parent=p) From a53d7a0a50f12a1a731b81e646b55872717093bb Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 9 May 2013 08:13:13 -0700 Subject: [PATCH 63/70] Made gis_terms be a set, rather than a dict with None for all keys. --- django/contrib/gis/db/backends/base.py | 4 ++- .../gis/db/backends/mysql/operations.py | 29 +++++++++---------- .../gis/db/backends/oracle/operations.py | 21 +++++++++----- .../gis/db/backends/postgis/operations.py | 26 ++++++++--------- .../gis/db/backends/spatialite/operations.py | 5 ++-- 5 files changed, 45 insertions(+), 40 deletions(-) diff --git a/django/contrib/gis/db/backends/base.py b/django/contrib/gis/db/backends/base.py index 171a304439..7db7ce51ba 100644 --- a/django/contrib/gis/db/backends/base.py +++ b/django/contrib/gis/db/backends/base.py @@ -3,10 +3,12 @@ Base/mixin classes for the spatial backend database operations and the `SpatialRefSys` model the backend. """ import re + from django.contrib.gis import gdal from django.utils import six from django.utils.encoding import python_2_unicode_compatible + class BaseSpatialOperations(object): """ This module holds the base `BaseSpatialBackend` object, which is @@ -18,7 +20,7 @@ class BaseSpatialOperations(object): geometry_operators = {} geography_operators = {} geography_functions = {} - gis_terms = {} + gis_terms = set() truncate_params = {} # Quick booleans for the type of this spatial backend, and diff --git a/django/contrib/gis/db/backends/mysql/operations.py b/django/contrib/gis/db/backends/mysql/operations.py index 14402ec0a3..26cec743a9 100644 --- a/django/contrib/gis/db/backends/mysql/operations.py +++ b/django/contrib/gis/db/backends/mysql/operations.py @@ -3,7 +3,6 @@ from django.db.backends.mysql.base import DatabaseOperations from django.contrib.gis.db.backends.adapter import WKTAdapter from django.contrib.gis.db.backends.base import BaseSpatialOperations -from django.utils import six class MySQLOperations(DatabaseOperations, BaseSpatialOperations): @@ -18,21 +17,21 @@ class MySQLOperations(DatabaseOperations, BaseSpatialOperations): Adaptor = Adapter # Backwards-compatibility alias. geometry_functions = { - 'bbcontains' : 'MBRContains', # For consistency w/PostGIS API - 'bboverlaps' : 'MBROverlaps', # .. .. - 'contained' : 'MBRWithin', # .. .. - 'contains' : 'MBRContains', - 'disjoint' : 'MBRDisjoint', - 'equals' : 'MBREqual', - 'exact' : 'MBREqual', - 'intersects' : 'MBRIntersects', - 'overlaps' : 'MBROverlaps', - 'same_as' : 'MBREqual', - 'touches' : 'MBRTouches', - 'within' : 'MBRWithin', - } + 'bbcontains': 'MBRContains', # For consistency w/PostGIS API + 'bboverlaps': 'MBROverlaps', # .. .. + 'contained': 'MBRWithin', # .. .. + 'contains': 'MBRContains', + 'disjoint': 'MBRDisjoint', + 'equals': 'MBREqual', + 'exact': 'MBREqual', + 'intersects': 'MBRIntersects', + 'overlaps': 'MBROverlaps', + 'same_as': 'MBREqual', + 'touches': 'MBRTouches', + 'within': 'MBRWithin', + } - gis_terms = dict([(term, None) for term in list(geometry_functions) + ['isnull']]) + gis_terms = set(geometry_functions) | set(['isnull']) def geo_db_type(self, f): return f.geom_type diff --git a/django/contrib/gis/db/backends/oracle/operations.py b/django/contrib/gis/db/backends/oracle/operations.py index 18697ac8c0..84217c331b 100644 --- a/django/contrib/gis/db/backends/oracle/operations.py +++ b/django/contrib/gis/db/backends/oracle/operations.py @@ -18,6 +18,7 @@ from django.contrib.gis.geometry.backend import Geometry from django.contrib.gis.measure import Distance from django.utils import six + class SDOOperation(SpatialFunction): "Base class for SDO* Oracle operations." sql_template = "%(function)s(%(geo_col)s, %(geometry)s) %(operator)s '%(result)s'" @@ -32,6 +33,7 @@ class SDODistance(SpatialFunction): sql_template = ('%(function)s(%(geo_col)s, %(geometry)s, %(tolerance)s) ' '%(operator)s %(result)s') dist_func = 'SDO_GEOM.SDO_DISTANCE' + def __init__(self, op, tolerance=0.05): super(SDODistance, self).__init__(self.dist_func, tolerance=tolerance, @@ -40,6 +42,7 @@ class SDODistance(SpatialFunction): class SDODWithin(SpatialFunction): dwithin_func = 'SDO_WITHIN_DISTANCE' sql_template = "%(function)s(%(geo_col)s, %(geometry)s, %%s) = 'TRUE'" + def __init__(self): super(SDODWithin, self).__init__(self.dwithin_func) @@ -48,6 +51,7 @@ class SDOGeomRelate(SpatialFunction): relate_func = 'SDO_GEOM.RELATE' sql_template = ("%(function)s(%(geo_col)s, '%(mask)s', %(geometry)s, " "%(tolerance)s) %(operator)s '%(mask)s'") + def __init__(self, mask, tolerance=0.05): # SDO_GEOM.RELATE(...) has a peculiar argument order: column, mask, geom, tolerance. # Moreover, the runction result is the mask (e.g., 'DISJOINT' instead of 'TRUE'). @@ -60,6 +64,7 @@ class SDORelate(SpatialFunction): mask_regex = re.compile(r'^(%s)(\+(%s))*$' % (masks, masks), re.I) sql_template = "%(function)s(%(geo_col)s, %(geometry)s, 'mask=%(mask)s') = 'TRUE'" relate_func = 'SDO_RELATE' + def __init__(self, mask): if not self.mask_regex.match(mask): raise ValueError('Invalid %s mask: "%s"' % (self.relate_func, mask)) @@ -79,12 +84,12 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations): Adaptor = Adapter # Backwards-compatibility alias. area = 'SDO_GEOM.SDO_AREA' - gml= 'SDO_UTIL.TO_GMLGEOMETRY' + gml = 'SDO_UTIL.TO_GMLGEOMETRY' centroid = 'SDO_GEOM.SDO_CENTROID' difference = 'SDO_GEOM.SDO_DIFFERENCE' distance = 'SDO_GEOM.SDO_DISTANCE' - extent= 'SDO_AGGR_MBR' - intersection= 'SDO_GEOM.SDO_INTERSECTION' + extent = 'SDO_AGGR_MBR' + intersection = 'SDO_GEOM.SDO_INTERSECTION' length = 'SDO_GEOM.SDO_LENGTH' num_geom = 'SDO_UTIL.GETNUMELEM' num_points = 'SDO_UTIL.GETNUMVERTICES' @@ -127,9 +132,8 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations): } geometry_functions.update(distance_functions) - gis_terms = ['isnull'] - gis_terms += list(geometry_functions) - gis_terms = dict([(term, None) for term in gis_terms]) + gis_terms = set(['isnull']) + gis_terms.update(geometry_functions) truncate_params = {'relate' : None} @@ -272,7 +276,8 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations): given Aggregate instance. """ agg_name = agg.__class__.__name__.lower() - if agg_name == 'union' : agg_name += 'agg' + if agg_name == 'union': + agg_name += 'agg' if agg.is_extent: sql_template = '%(function)s(%(field)s)' else: @@ -295,5 +300,5 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations): """ # This code doesn't work for bulk insert cases. assert len(placeholders) == 1 - return [[param for pholder,param + return [[param for pholder, param in six.moves.zip(placeholders[0], params[0]) if pholder != 'NULL'], ] diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py index 4d31bbb53c..734f39c752 100644 --- a/django/contrib/gis/db/backends/postgis/operations.py +++ b/django/contrib/gis/db/backends/postgis/operations.py @@ -208,23 +208,22 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations): # for the geography type. self.geography_functions = self.distance_functions.copy() self.geography_functions.update({ - 'coveredby' : self.geometry_functions['coveredby'], - 'covers' : self.geometry_functions['covers'], - 'intersects' : self.geometry_functions['intersects'], - }) + 'coveredby': self.geometry_functions['coveredby'], + 'covers': self.geometry_functions['covers'], + 'intersects': self.geometry_functions['intersects'], + }) self.geography_operators = { - 'bboverlaps' : PostGISOperator('&&'), - } + 'bboverlaps': PostGISOperator('&&'), + } # Native geometry type support added in PostGIS 2.0. if version >= (2, 0, 0): self.geometry = True # Creating a dictionary lookup of all GIS terms for PostGIS. - gis_terms = ['isnull'] - gis_terms += list(self.geometry_operators) - gis_terms += list(self.geometry_functions) - self.gis_terms = dict([(term, None) for term in gis_terms]) + self.gis_terms = set(['isnull']) + self.gis_terms.update(self.geometry_operators) + self.gis_terms.update(self.geometry_functions) self.area = prefix + 'Area' self.bounding_circle = BOUNDINGCIRCLE @@ -247,7 +246,7 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations): self.makeline = prefix + 'MakeLine' self.mem_size = prefix + 'mem_size' self.num_geom = prefix + 'NumGeometries' - self.num_points =prefix + 'npoints' + self.num_points = prefix + 'npoints' self.perimeter = prefix + 'Perimeter' self.point_on_surface = prefix + 'PointOnSurface' self.polygonize = prefix + 'Polygonize' @@ -324,7 +323,7 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations): raise NotImplementedError('PostGIS 1.5 supports geography columns ' 'only with an SRID of 4326.') - return 'geography(%s,%d)'% (f.geom_type, f.srid) + return 'geography(%s,%d)' % (f.geom_type, f.srid) elif self.geometry: # Postgis 2.0 supports type-based geometries. # TODO: Support 'M' extension. @@ -565,7 +564,8 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations): if not self.check_aggregate_support(agg): raise NotImplementedError('%s spatial aggregate is not implmented for this backend.' % agg_name) agg_name = agg_name.lower() - if agg_name == 'union': agg_name += 'agg' + if agg_name == 'union': + agg_name += 'agg' sql_template = '%(function)s(%(field)s)' sql_function = getattr(self, agg_name) return sql_template, sql_function diff --git a/django/contrib/gis/db/backends/spatialite/operations.py b/django/contrib/gis/db/backends/spatialite/operations.py index 83745476a0..4281cafa23 100644 --- a/django/contrib/gis/db/backends/spatialite/operations.py +++ b/django/contrib/gis/db/backends/spatialite/operations.py @@ -117,9 +117,8 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations): super(DatabaseOperations, self).__init__(connection) # Creating the GIS terms dictionary. - gis_terms = ['isnull'] - gis_terms += self.geometry_functions.keys() - self.gis_terms = dict([(term, None) for term in gis_terms]) + self.gis_terms = set(['isnull']) + self.gis_terms.update(self.geometry_functions) @cached_property def spatial_version(self): From 1906cb93609ce6042f97939a04067a122f5719a6 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 9 May 2013 17:38:47 +0200 Subject: [PATCH 64/70] Fixed conditional skipping of test for left/right lookup types. connection.ops.spatial_version is None for some backends (eg. MySQL) and the comparison fails on Python 3 with TypeError. --- django/contrib/gis/tests/geoapp/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/contrib/gis/tests/geoapp/tests.py b/django/contrib/gis/tests/geoapp/tests.py index a5164d39d1..672d78c1dd 100644 --- a/django/contrib/gis/tests/geoapp/tests.py +++ b/django/contrib/gis/tests/geoapp/tests.py @@ -297,7 +297,7 @@ class GeoLookupTest(TestCase): # The left/right lookup tests are known failures on PostGIS 2.0/2.0.1 # http://trac.osgeo.org/postgis/ticket/2035 - if (2, 0, 0) <= connection.ops.spatial_version <= (2, 0, 1): + if connection.ops.postgis and (2, 0, 0) <= connection.ops.spatial_version <= (2, 0, 1): test_left_right_lookups = unittest.expectedFailure(test_left_right_lookups) def test_equals_lookups(self): From 1e37cb37cec330a6b78925e2ef5589817428d09a Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Thu, 9 May 2013 15:11:02 +0100 Subject: [PATCH 65/70] Further removal of static admin validation that can fail erroneously --- django/contrib/admin/validation.py | 17 ----------------- tests/admin_validation/tests.py | 2 -- tests/modeladmin/tests.py | 21 --------------------- 3 files changed, 40 deletions(-) diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py index 178df9d844..8d65f96cf1 100644 --- a/django/contrib/admin/validation.py +++ b/django/contrib/admin/validation.py @@ -246,7 +246,6 @@ def validate_fields_spec(cls, model, opts, flds, label): # readonly_fields will handle the validation of such # things. continue - check_formfield(cls, model, opts, label, field) try: f = opts.get_field(field) except models.FieldDoesNotExist: @@ -302,14 +301,6 @@ def validate_base(cls, model): # exclude if cls.exclude: # default value is None check_isseq(cls, 'exclude', cls.exclude) - for field in cls.exclude: - check_formfield(cls, model, opts, 'exclude', field) - try: - f = opts.get_field(field) - except models.FieldDoesNotExist: - # If we can't find a field on the model that matches, - # it could be an extra field on the form. - continue if len(cls.exclude) > len(set(cls.exclude)): raise ImproperlyConfigured('There are duplicate field(s) in %s.exclude' % cls.__name__) @@ -380,14 +371,6 @@ def get_field(cls, model, opts, label, field): raise ImproperlyConfigured("'%s.%s' refers to field '%s' that is missing from model '%s.%s'." % (cls.__name__, label, field, model._meta.app_label, model.__name__)) -def check_formfield(cls, model, opts, label, field): - if getattr(cls.form, 'base_fields', None): - try: - cls.form.base_fields[field] - except KeyError: - raise ImproperlyConfigured("'%s.%s' refers to field '%s' that " - "is missing from the form." % (cls.__name__, label, field)) - def fetch_attr(cls, model, opts, label, field): try: return opts.get_field(field) diff --git a/tests/admin_validation/tests.py b/tests/admin_validation/tests.py index 5329cc7e8c..6ce0ee7e03 100644 --- a/tests/admin_validation/tests.py +++ b/tests/admin_validation/tests.py @@ -269,8 +269,6 @@ class ValidationTestCase(TestCase): """ class SongForm(forms.ModelForm): extra_data = forms.CharField() - class Meta: - model = Song class FieldsOnFormOnlyAdmin(admin.ModelAdmin): form = SongForm diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index a63984a8a9..bac8d30153 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -682,27 +682,6 @@ class ValidationTests(unittest.TestCase): validate(BandAdmin, Band) - class AdminBandForm(forms.ModelForm): - class Meta: - model = Band - - class BandAdmin(ModelAdmin): - form = AdminBandForm - - fieldsets = ( - ('Band', { - 'fields': ('non_existent_field',) - }), - ) - - six.assertRaisesRegex(self, - ImproperlyConfigured, - "'BandAdmin.fieldsets\[0]\[1\]\['fields'\]' refers to field 'non_existent_field' that is missing from the form.", - validate, - BandAdmin, - Band, - ) - class AdminBandForm(forms.ModelForm): delete = forms.BooleanField() From f026a519aea8f3ea7ca339bfbbb007e1ee0068b0 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Thu, 21 Feb 2013 21:56:55 +0000 Subject: [PATCH 66/70] Fixed #19733 - deprecated ModelForms without 'fields' or 'exclude', and added '__all__' shortcut This also updates all dependent functionality, including modelform_factory and modelformset_factory, and the generic views `ModelFormMixin`, `CreateView` and `UpdateView` which gain a new `fields` attribute. --- django/contrib/admin/options.py | 14 +- django/contrib/auth/forms.py | 1 + django/contrib/contenttypes/generic.py | 9 +- django/contrib/flatpages/forms.py | 1 + .../formtools/tests/wizard/test_forms.py | 4 +- .../tests/wizard/wizardtests/tests.py | 1 + django/forms/models.py | 57 +++++- django/views/generic/edit.py | 11 +- .../ref/class-based-views/generic-editing.txt | 2 + docs/ref/class-based-views/mixins-editing.txt | 12 ++ docs/ref/contrib/admin/index.txt | 34 +++- docs/ref/forms/models.txt | 8 + docs/releases/1.6.txt | 52 ++++++ docs/topics/auth/customizing.txt | 1 + .../class-based-views/generic-editing.txt | 37 ++-- docs/topics/forms/modelforms.txt | 163 +++++++++--------- tests/admin_validation/tests.py | 2 + tests/bug639/models.py | 1 + tests/foreign_object/tests.py | 1 + tests/forms_tests/tests/test_regressions.py | 1 + tests/forms_tests/tests/tests.py | 4 + tests/generic_relations/tests.py | 1 + tests/generic_views/test_edit.py | 44 ++++- tests/generic_views/test_forms.py | 1 + tests/generic_views/views.py | 9 + tests/i18n/forms.py | 1 + tests/inline_formsets/tests.py | 10 +- tests/model_forms/tests.py | 111 ++++++++++-- tests/model_forms_regress/tests.py | 57 +++++- tests/model_formsets/tests.py | 89 +++++----- tests/model_formsets_regress/tests.py | 28 +-- tests/model_inheritance_regress/tests.py | 2 + tests/modeladmin/tests.py | 9 +- tests/timezones/forms.py | 1 + 34 files changed, 578 insertions(+), 201 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 543655b357..9bc3d55454 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -5,7 +5,7 @@ from django import forms from django.conf import settings from django.forms.formsets import all_valid, DELETION_FIELD_NAME from django.forms.models import (modelform_factory, modelformset_factory, - inlineformset_factory, BaseInlineFormSet) + inlineformset_factory, BaseInlineFormSet, modelform_defines_fields) from django.contrib.contenttypes.models import ContentType from django.contrib.admin import widgets, helpers from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_objects, @@ -488,6 +488,10 @@ class ModelAdmin(BaseModelAdmin): "formfield_callback": partial(self.formfield_for_dbfield, request=request), } defaults.update(kwargs) + + if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): + defaults['fields'] = forms.ALL_FIELDS + try: return modelform_factory(self.model, **defaults) except FieldError as e: @@ -523,6 +527,10 @@ class ModelAdmin(BaseModelAdmin): "formfield_callback": partial(self.formfield_for_dbfield, request=request), } defaults.update(kwargs) + if (defaults.get('fields') is None + and not modelform_defines_fields(defaults.get('form'))): + defaults['fields'] = forms.ALL_FIELDS + return modelform_factory(self.model, **defaults) def get_changelist_formset(self, request, **kwargs): @@ -1527,6 +1535,10 @@ class InlineModelAdmin(BaseModelAdmin): return result defaults['form'] = DeleteProtectedModelForm + + if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): + defaults['fields'] = forms.ALL_FIELDS + return inlineformset_factory(self.parent_model, self.model, **defaults) def get_fieldsets(self, request, obj=None): diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index 42abde2f19..e44e7a703e 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -130,6 +130,7 @@ class UserChangeForm(forms.ModelForm): class Meta: model = User + fields = '__all__' def __init__(self, *args, **kwargs): super(UserChangeForm, self).__init__(*args, **kwargs) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index cc03f799f3..399d24aa87 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -13,8 +13,9 @@ from django.db.models import signals from django.db.models.fields.related import ForeignObject, ForeignObjectRel from django.db.models.related import PathInfo from django.db.models.sql.where import Constraint -from django.forms import ModelForm -from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance +from django.forms import ModelForm, ALL_FIELDS +from django.forms.models import (BaseModelFormSet, modelformset_factory, save_instance, + modelform_defines_fields) from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets from django.contrib.contenttypes.models import ContentType from django.utils import six @@ -480,6 +481,10 @@ class GenericInlineModelAdmin(InlineModelAdmin): "exclude": exclude } defaults.update(kwargs) + + if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): + defaults['fields'] = ALL_FIELDS + return generic_inlineformset_factory(self.model, **defaults) class GenericStackedInline(GenericInlineModelAdmin): diff --git a/django/contrib/flatpages/forms.py b/django/contrib/flatpages/forms.py index a848875a9f..80938116ad 100644 --- a/django/contrib/flatpages/forms.py +++ b/django/contrib/flatpages/forms.py @@ -12,6 +12,7 @@ class FlatpageForm(forms.ModelForm): class Meta: model = FlatPage + fields = '__all__' def clean_url(self): url = self.cleaned_data['url'] diff --git a/django/contrib/formtools/tests/wizard/test_forms.py b/django/contrib/formtools/tests/wizard/test_forms.py index 14c6e6a685..7425755cf5 100644 --- a/django/contrib/formtools/tests/wizard/test_forms.py +++ b/django/contrib/formtools/tests/wizard/test_forms.py @@ -60,9 +60,11 @@ class TestModel(models.Model): class TestModelForm(forms.ModelForm): class Meta: model = TestModel + fields = '__all__' -TestModelFormSet = forms.models.modelformset_factory(TestModel, form=TestModelForm, extra=2) +TestModelFormSet = forms.models.modelformset_factory(TestModel, form=TestModelForm, extra=2, + fields='__all__') class TestWizard(WizardView): diff --git a/django/contrib/formtools/tests/wizard/wizardtests/tests.py b/django/contrib/formtools/tests/wizard/wizardtests/tests.py index 1ee5dbdc78..3c2dbc3efb 100644 --- a/django/contrib/formtools/tests/wizard/wizardtests/tests.py +++ b/django/contrib/formtools/tests/wizard/wizardtests/tests.py @@ -15,6 +15,7 @@ from django.utils._os import upath class UserForm(forms.ModelForm): class Meta: model = User + fields = '__all__' UserFormSet = forms.models.modelformset_factory(User, form=UserForm, extra=2) diff --git a/django/forms/models.py b/django/forms/models.py index 5e7797809a..af5cda8faf 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -5,6 +5,8 @@ and database field objects. from __future__ import absolute_import, unicode_literals +import warnings + from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError from django.forms.fields import Field, ChoiceField from django.forms.forms import BaseForm, get_declared_fields @@ -22,8 +24,12 @@ from django.utils.translation import ugettext_lazy as _, ugettext __all__ = ( 'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model', 'save_instance', 'ModelChoiceField', 'ModelMultipleChoiceField', + 'ALL_FIELDS', ) +ALL_FIELDS = '__all__' + + def construct_instance(form, instance, fields=None, exclude=None): """ Constructs and returns a model instance from the bound ``form``'s @@ -211,7 +217,7 @@ class ModelFormMetaclass(type): # of ('foo',) for opt in ['fields', 'exclude']: value = getattr(opts, opt) - if isinstance(value, six.string_types): + if isinstance(value, six.string_types) and value != ALL_FIELDS: msg = ("%(model)s.Meta.%(opt)s cannot be a string. " "Did you mean to type: ('%(value)s',)?" % { 'model': new_class.__name__, @@ -222,6 +228,20 @@ class ModelFormMetaclass(type): if opts.model: # If a model is defined, extract form fields from it. + + if opts.fields is None and opts.exclude is None: + # This should be some kind of assertion error once deprecation + # cycle is complete. + warnings.warn("Creating a ModelForm without either the 'fields' attribute " + "or the 'exclude' attribute is deprecated - form %s " + "needs updating" % name, + PendingDeprecationWarning) + + if opts.fields == ALL_FIELDS: + # sentinel for fields_for_model to indicate "get the list of + # fields from the model" + opts.fields = None + fields = fields_for_model(opts.model, opts.fields, opts.exclude, opts.widgets, formfield_callback) # make sure opts.fields doesn't specify an invalid field @@ -394,7 +414,8 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None, Returns a ModelForm containing form fields for the given model. ``fields`` is an optional list of field names. If provided, only the named - fields will be included in the returned fields. + fields will be included in the returned fields. If omitted or '__all__', + all fields will be used. ``exclude`` is an optional list of field names. If provided, the named fields will be excluded from the returned fields, even if they are listed @@ -434,6 +455,15 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None, 'formfield_callback': formfield_callback } + # The ModelFormMetaclass will trigger a similar warning/error, but this will + # be difficult to debug for code that needs updating, so we produce the + # warning here too. + if (getattr(Meta, 'fields', None) is None and + getattr(Meta, 'exclude', None) is None): + warnings.warn("Calling modelform_factory without defining 'fields' or " + "'exclude' explicitly is deprecated", + PendingDeprecationWarning, stacklevel=2) + # Instatiate type(form) in order to use the same metaclass as form. return type(form)(class_name, (form,), form_class_attrs) @@ -701,6 +731,21 @@ def modelformset_factory(model, form=ModelForm, formfield_callback=None, """ Returns a FormSet class for the given Django model class. """ + # modelform_factory will produce the same warning/error, but that will be + # difficult to debug for code that needs upgrading, so we produce the + # warning here too. This logic is reproducing logic inside + # modelform_factory, but it can be removed once the deprecation cycle is + # complete, since the validation exception will produce a helpful + # stacktrace. + meta = getattr(form, 'Meta', None) + if meta is None: + meta = type(str('Meta'), (object,), {}) + if (getattr(meta, 'fields', fields) is None and + getattr(meta, 'exclude', exclude) is None): + warnings.warn("Calling modelformset_factory without defining 'fields' or " + "'exclude' explicitly is deprecated", + PendingDeprecationWarning, stacklevel=2) + form = modelform_factory(model, form=form, fields=fields, exclude=exclude, formfield_callback=formfield_callback, widgets=widgets) @@ -1091,3 +1136,11 @@ class ModelMultipleChoiceField(ModelChoiceField): initial_set = set([force_text(value) for value in self.prepare_value(initial)]) data_set = set([force_text(value) for value in data]) return data_set != initial_set + + +def modelform_defines_fields(form_class): + return (form_class is not None and ( + hasattr(form_class, '_meta') and + (form_class._meta.fields is not None or + form_class._meta.exclude is not None) + )) diff --git a/django/views/generic/edit.py b/django/views/generic/edit.py index 5b97fc81c9..e2cc741ffb 100644 --- a/django/views/generic/edit.py +++ b/django/views/generic/edit.py @@ -1,3 +1,5 @@ +import warnings + from django.forms import models as model_forms from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponseRedirect @@ -95,7 +97,14 @@ class ModelFormMixin(FormMixin, SingleObjectMixin): # Try to get a queryset and extract the model class # from that model = self.get_queryset().model - return model_forms.modelform_factory(model) + + fields = getattr(self, 'fields', None) + if fields is None: + warnings.warn("Using ModelFormMixin (base class of %s) without " + "the 'fields' attribute is deprecated." % self.__class__.__name__, + PendingDeprecationWarning) + + return model_forms.modelform_factory(model, fields=fields) def get_form_kwargs(self): """ diff --git a/docs/ref/class-based-views/generic-editing.txt b/docs/ref/class-based-views/generic-editing.txt index 1dbb427036..555ba40cfb 100644 --- a/docs/ref/class-based-views/generic-editing.txt +++ b/docs/ref/class-based-views/generic-editing.txt @@ -110,6 +110,7 @@ CreateView class AuthorCreate(CreateView): model = Author + fields = ['name'] UpdateView ---------- @@ -152,6 +153,7 @@ UpdateView class AuthorUpdate(UpdateView): model = Author + fields = ['name'] DeleteView ---------- diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt index 3f32269742..51d8628818 100644 --- a/docs/ref/class-based-views/mixins-editing.txt +++ b/docs/ref/class-based-views/mixins-editing.txt @@ -116,6 +116,18 @@ ModelFormMixin by examining ``self.object`` or :attr:`~django.views.generic.detail.SingleObjectMixin.queryset`. + .. attribute:: fields + + .. versionadded:: 1.6 + + A list of names of fields. This is interpreted the same way as the + ``Meta.fields`` attribute of :class:`~django.forms.ModelForm`. + + This is a required attribute if you are generating the form class + automatically (e.g. using ``model``). Omitting this attribute will + result in all fields being used, but this behaviour is deprecated + and will be removed in Django 1.8. + .. attribute:: success_url The URL to redirect to when the form is successfully processed. diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 43f0398566..67e498ee91 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -335,6 +335,22 @@ subclass:: For an example see the section `Adding custom validation to the admin`_. + .. admonition:: Note + + .. versionchanged:: 1.6 + + If you define the ``Meta.model`` attribute on a + :class:`~django.forms.ModelForm`, you must also define the + ``Meta.fields`` attribute (or the ``Meta.exclude`` attribute). However, + since the admin has its own way of defining fields, the ``Meta.fields`` + attribute will be ignored. + + If the ``ModelForm`` is only going to be used for the admin, the easiest + solution is to omit the ``Meta.model`` attribute, since ``ModelAdmin`` + will provide the correct model to use. Alternatively, you can set + ``fields = []`` in the ``Meta`` class to satisfy the validation on the + ``ModelForm``. + .. admonition:: Note If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude`` @@ -1283,13 +1299,24 @@ templates used by the :class:`ModelAdmin` views: on the changelist page. To use a custom form, for example:: class MyForm(forms.ModelForm): - class Meta: - model = MyModel + pass class MyModelAdmin(admin.ModelAdmin): def get_changelist_form(self, request, **kwargs): return MyForm + .. admonition:: Note + + .. versionchanged:: 1.6 + + If you define the ``Meta.model`` attribute on a + :class:`~django.forms.ModelForm`, you must also define the + ``Meta.fields`` attribute (or the ``Meta.exclude`` attribute). However, + ``ModelAdmin`` ignores this value, overriding it with the + :attr:`ModelAdmin.list_editable` attribute. The easiest solution is to + omit the ``Meta.model`` attribute, since ``ModelAdmin`` will provide the + correct model to use. + .. method:: ModelAdmin.get_changelist_formset(self, request, **kwargs) Returns a :ref:`ModelFormSet ` class for use on the @@ -1490,9 +1517,6 @@ needed. Now within your form you can add your own custom validation for any field:: class MyArticleAdminForm(forms.ModelForm): - class Meta: - model = Article - def clean_name(self): # do something that validates your data return self.cleaned_data["name"] diff --git a/docs/ref/forms/models.txt b/docs/ref/forms/models.txt index 7e3a1470b6..9b3480758a 100644 --- a/docs/ref/forms/models.txt +++ b/docs/ref/forms/models.txt @@ -25,6 +25,14 @@ Model Form Functions See :ref:`modelforms-factory` for example usage. + .. versionchanged:: 1.6 + + You must provide the list of fields explicitly, either via keyword arguments + ``fields`` or ``exclude``, or the corresponding attributes on the form's + inner ``Meta`` class. See :ref:`modelforms-selecting-fields` for more + information. Omitting any definition of the fields to use will result in all + fields being used, but this behaviour is deprecated. + .. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False) Returns a ``FormSet`` class for the given ``model`` class. diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 611b661415..9cce36aac3 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -532,3 +532,55 @@ including it in an URLconf, simply replace:: with:: (r'^prefix/(?P\d+)/(?P.*)/$', 'django.contrib.contenttypes.views.shortcut'), + +``ModelForm`` without ``fields`` or ``exclude`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, if you wanted a :class:`~django.forms.ModelForm` to use all fields on +the model, you could simply omit the ``Meta.fields`` attribute, and all fields +would be used. + +This can lead to security problems where fields are added to the model and, +unintentionally, automatically become editable by end users. In some cases, +particular with boolean fields, it is possible for this problem to be completely +invisible. This is a form of `Mass assignment vulnerability +`_. + +For this reason, this behaviour is deprecated, and using the ``Meta.exclude`` +option is strongly discouraged. Instead, all fields that are intended for +inclusion in the form should be listed explicitly in the ``fields`` attribute. + +If this security concern really does not apply in your case, there is a shortcut +to explicitly indicate that all fields should be used - use the special value +``"__all__"`` for the fields attribute:: + + class MyModelForm(ModelForm): + class Meta: + fields = "__all__" + model = MyModel + +If you have custom ``ModelForms`` that only need to be used in the admin, there +is another option. The admin has its own methods for defining fields +(``fieldsets`` etc.), and so adding a list of fields to the ``ModelForm`` is +redundant. Instead, simply omit the ``Meta`` inner class of the ``ModelForm``, +or omit the ``Meta.model`` attribute. Since the ``ModelAdmin`` subclass knows +which model it is for, it can add the necessary attributes to derive a +functioning ``ModelForm``. This behaviour also works for earlier Django +versions. + +``UpdateView`` and ``CreateView`` without explicit fields +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The generic views :class:`~django.views.generic.edit.CreateView` and +:class:`~django.views.generic.edit.UpdateView`, and anything else derived from +:class:`~django.views.generic.edit.ModelFormMixin`, are vulnerable to the +security problem described in the section above, because they can automatically +create a ``ModelForm`` that uses all fields for a model. + +For this reason, if you use these views for editing models, you must also supply +the ``fields`` attribute, which is a list of model fields and works in the same +way as the :class:`~django.forms.ModelForm` ``Meta.fields`` attribute. Alternatively, +you can set set the ``form_class`` attribute to a ``ModelForm`` that explicitly +defines the fields to be used. Defining an ``UpdateView`` or ``CreateView`` +subclass to be used with a model but without an explicit list of fields is +deprecated. diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt index a5d7d3f9a1..b53bbe8211 100644 --- a/docs/topics/auth/customizing.txt +++ b/docs/topics/auth/customizing.txt @@ -1051,6 +1051,7 @@ code would be required in the app's ``admin.py`` file:: class Meta: model = MyUser + fields = ['email', 'password', 'date_of_birth', 'is_active', 'is_admin'] def clean_password(self): # Regardless of what the user provides, return the initial value. diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt index 66ba36fd87..86c5280159 100644 --- a/docs/topics/class-based-views/generic-editing.txt +++ b/docs/topics/class-based-views/generic-editing.txt @@ -114,9 +114,11 @@ here; we don't have to write any logic ourselves:: class AuthorCreate(CreateView): model = Author + fields = ['name'] class AuthorUpdate(UpdateView): model = Author + fields = ['name'] class AuthorDelete(DeleteView): model = Author @@ -126,6 +128,17 @@ here; we don't have to write any logic ourselves:: We have to use :func:`~django.core.urlresolvers.reverse_lazy` here, not just ``reverse`` as the urls are not loaded when the file is imported. +.. versionchanged:: 1.6 + +In Django 1.6, the ``fields`` attribute was added, which works the same way as +the ``fields`` attribute on the inner ``Meta`` class on +:class:`~django.forms.ModelForm`. + +Omitting the fields attribute will work as previously, but is deprecated and +this attribute will be required from 1.8 (unless you define the form class in +another way). + + Finally, we hook these new views into the URLconf:: # urls.py @@ -177,33 +190,17 @@ the foreign key relation to the model:: # ... -Create a custom :class:`~django.forms.ModelForm` in order to exclude the -``created_by`` field and prevent the user from editing it: - -.. code-block:: python - - # forms.py - from django import forms - from myapp.models import Author - - class AuthorForm(forms.ModelForm): - class Meta: - model = Author - exclude = ('created_by',) - -In the view, use the custom -:attr:`~django.views.generic.edit.FormMixin.form_class` and override -:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the -user:: +In the view, ensure that you exclude ``created_by`` in the list of fields to +edit, and override +:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the user:: # views.py from django.views.generic.edit import CreateView from myapp.models import Author - from myapp.forms import AuthorForm class AuthorCreate(CreateView): - form_class = AuthorForm model = Author + fields = ['name'] def form_valid(self, form): form.instance.created_by = self.request.user diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index eaf2bbbaf2..e58dade736 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -28,6 +28,7 @@ For example:: >>> class ArticleForm(ModelForm): ... class Meta: ... model = Article + ... fields = ['pub_date', 'headline', 'content', 'reporter'] # Creating a form to add an article. >>> form = ArticleForm() @@ -39,11 +40,13 @@ For example:: Field types ----------- -The generated ``Form`` class will have a form field for every model field. Each -model field has a corresponding default form field. For example, a -``CharField`` on a model is represented as a ``CharField`` on a form. A -model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is -the full list of conversions: +The generated ``Form`` class will have a form field for every model field +specified, in the order specified in the ``fields`` attribute. + +Each model field has a corresponding default form field. For example, a +``CharField`` on a model is represented as a ``CharField`` on a form. A model +``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is the +full list of conversions: =============================== ======================================== Model field Form field @@ -168,10 +171,13 @@ Consider this set of models:: class AuthorForm(ModelForm): class Meta: model = Author + fields = ['name', 'title', 'birth_date'] class BookForm(ModelForm): class Meta: model = Book + fields = ['name', 'authors'] + With these models, the ``ModelForm`` subclasses above would be roughly equivalent to this (the only difference being the ``save()`` method, which @@ -288,47 +294,66 @@ method is used to determine whether a form requires multipart file upload (and hence whether ``request.FILES`` must be passed to the form), etc. See :ref:`binding-uploaded-files` for more information. -Using a subset of fields on the form ------------------------------------- +.. _modelforms-selecting-fields: -In some cases, you may not want all the model fields to appear on the generated -form. There are three ways of telling ``ModelForm`` to use only a subset of the -model fields: +Selecting the fields to use +--------------------------- -1. Set ``editable=False`` on the model field. As a result, *any* form - created from the model via ``ModelForm`` will not include that - field. +It is strongly recommended that you explicitly set all fields that should be +edited in the form using the ``fields`` attribute. Failure to do so can easily +lead to security problems when a form unexpectedly allows a user to set certain +fields, especially when new fields are added to a model. Depending on how the +form is rendered, the problem may not even be visible on the web page. -2. Use the ``fields`` attribute of the ``ModelForm``'s inner ``Meta`` - class. This attribute, if given, should be a list of field names - to include in the form. The order in which the fields names are specified - in that list is respected when the form renders them. +The alternative approach would be to include all fields automatically, or +blacklist only some. This fundamental approach is known to be much less secure +and has led to serious exploits on major websites (e.g. `GitHub +`_). -3. Use the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` - class. This attribute, if given, should be a list of field names - to exclude from the form. +There are, however, two shortcuts available for cases where you can guarantee +these security concerns do not apply to you: -For example, if you want a form for the ``Author`` model (defined -above) that includes only the ``name`` and ``birth_date`` fields, you would -specify ``fields`` or ``exclude`` like this:: +1. Set the ``fields`` attribute to the special value ``'__all__'`` to indicate + that all fields in the model should be used. For example:: - class PartialAuthorForm(ModelForm): - class Meta: - model = Author - fields = ('name', 'birth_date') + class AuthorForm(ModelForm): + class Meta: + model = Author + fields = '__all__' - class PartialAuthorForm(ModelForm): - class Meta: - model = Author - exclude = ('title',) +2. Set the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` class to + a list of fields to be excluded from the form. + + For example:: + + class PartialAuthorForm(ModelForm): + class Meta: + model = Author + exclude = ['title'] + + Since the ``Author`` model has the 3 fields ``name``, ``title`` and + ``birth_date``, this will result in the fields ``name`` and ``birth_date`` + being present on the form. + +If either of these are used, the order the fields appear in the form will be the +order the fields are defined in the model, with ``ManyToManyField`` instances +appearing last. + +In addition, Django applies the following rule: if you set ``editable=False`` on +the model field, *any* form created from the model via ``ModelForm`` will not +include that field. + +.. versionchanged:: 1.6 + + Before version 1.6, the ``'__all__'`` shortcut did not exist, but omitting + the ``fields`` attribute had the same effect. Omitting both ``fields`` and + ``exclude`` is now deprecated, but will continue to work as before until + version 1.8 -Since the Author model has only 3 fields, 'name', 'title', and -'birth_date', the forms above will contain exactly the same fields. .. note:: - If you specify ``fields`` or ``exclude`` when creating a form with - ``ModelForm``, then the fields that are not in the resulting form + Any fields not included in a form by the above logic will not be set by the form's ``save()`` method. Also, if you manually add the excluded fields back to the form, they will not be initialized from the model instance. @@ -401,15 +426,19 @@ field, you could do the following:: class Meta: model = Article + fields = ['pub_date', 'headline', 'content', 'reporter'] + If you want to override a field's default label, then specify the ``label`` parameter when declaring the form field:: - >>> class ArticleForm(ModelForm): - ... pub_date = DateField(label='Publication date') - ... - ... class Meta: - ... model = Article + class ArticleForm(ModelForm): + pub_date = DateField(label='Publication date') + + class Meta: + model = Article + fields = ['pub_date', 'headline', 'content', 'reporter'] + .. note:: @@ -436,6 +465,7 @@ parameter when declaring the form field:: class Meta: model = Article + fields = ['headline', 'content'] You must ensure that the type of the form field can be used to set the contents of the corresponding model field. When they are not compatible, @@ -444,30 +474,6 @@ parameter when declaring the form field:: See the :doc:`form field documentation ` for more information on fields and their arguments. -Changing the order of fields ----------------------------- - -By default, a ``ModelForm`` will render fields in the same order that they are -defined on the model, with ``ManyToManyField`` instances appearing last. If -you want to change the order in which fields are rendered, you can use the -``fields`` attribute on the ``Meta`` class. - -The ``fields`` attribute defines the subset of model fields that will be -rendered, and the order in which they will be rendered. For example given this -model:: - - class Book(models.Model): - author = models.ForeignKey(Author) - title = models.CharField(max_length=100) - -the ``author`` field would be rendered first. If we wanted the title field -to be rendered first, we could specify the following ``ModelForm``:: - - >>> class BookForm(ModelForm): - ... class Meta: - ... model = Book - ... fields = ('title', 'author') - .. _overriding-modelform-clean-method: Overriding the clean() method @@ -550,21 +556,19 @@ definition. This may be more convenient if you do not have many customizations to make:: >>> from django.forms.models import modelform_factory - >>> BookForm = modelform_factory(Book) + >>> BookForm = modelform_factory(Book, fields=("author", "title")) This can also be used to make simple modifications to existing forms, for -example by specifying which fields should be displayed:: - - >>> Form = modelform_factory(Book, form=BookForm, fields=("author",)) - -... or which fields should be excluded:: - - >>> Form = modelform_factory(Book, form=BookForm, exclude=("title",)) - -You can also specify the widgets to be used for a given field:: +example by specifying the widgets to be used for a given field:: >>> from django.forms import Textarea - >>> Form = modelform_factory(Book, form=BookForm, widgets={"title": Textarea()}) + >>> Form = modelform_factory(Book, form=BookForm, + widgets={"title": Textarea()}) + +The fields to include can be specified using the ``fields`` and ``exclude`` +keyword arguments, or the corresponding attributes on the ``ModelForm`` inner +``Meta`` class. Please see the ``ModelForm`` :ref:`modelforms-selecting-fields` +documentation. .. _model-formsets: @@ -688,11 +692,10 @@ database. If a given instance's data didn't change in the bound data, the instance won't be saved to the database and won't be included in the return value (``instances``, in the above example). -When fields are missing from the form (for example because they have -been excluded), these fields will not be set by the ``save()`` -method. You can find more information about this restriction, which -also holds for regular ``ModelForms``, in `Using a subset of fields on -the form`_. +When fields are missing from the form (for example because they have been +excluded), these fields will not be set by the ``save()`` method. You can find +more information about this restriction, which also holds for regular +``ModelForms``, in `Selecting the fields to use`_. Pass ``commit=False`` to return the unsaved model instances:: diff --git a/tests/admin_validation/tests.py b/tests/admin_validation/tests.py index 6ce0ee7e03..16f73c6390 100644 --- a/tests/admin_validation/tests.py +++ b/tests/admin_validation/tests.py @@ -285,6 +285,8 @@ class ValidationTestCase(TestCase): extra_data = forms.CharField() class Meta: model = Song + fields = '__all__' + class FieldsOnFormOnlyAdmin(admin.ModelAdmin): form = SongForm diff --git a/tests/bug639/models.py b/tests/bug639/models.py index e641555c87..fa8e7d2c07 100644 --- a/tests/bug639/models.py +++ b/tests/bug639/models.py @@ -25,3 +25,4 @@ class Photo(models.Model): class PhotoForm(ModelForm): class Meta: model = Photo + fields = '__all__' diff --git a/tests/foreign_object/tests.py b/tests/foreign_object/tests.py index 2ca13cb786..55dd6a0f47 100644 --- a/tests/foreign_object/tests.py +++ b/tests/foreign_object/tests.py @@ -322,6 +322,7 @@ class FormsTests(TestCase): class ArticleForm(forms.ModelForm): class Meta: model = Article + fields = '__all__' def test_foreign_object_form(self): # A very crude test checking that the non-concrete fields do not get form fields. diff --git a/tests/forms_tests/tests/test_regressions.py b/tests/forms_tests/tests/test_regressions.py index be9dc8c593..74509a0f1a 100644 --- a/tests/forms_tests/tests/test_regressions.py +++ b/tests/forms_tests/tests/test_regressions.py @@ -139,6 +139,7 @@ class FormsRegressionsTestCase(TestCase): class CheeseForm(ModelForm): class Meta: model = Cheese + fields = '__all__' form = CheeseForm({ 'name': 'Brie', diff --git a/tests/forms_tests/tests/tests.py b/tests/forms_tests/tests/tests.py index 80e2cadcc3..deda4822b8 100644 --- a/tests/forms_tests/tests/tests.py +++ b/tests/forms_tests/tests/tests.py @@ -17,11 +17,13 @@ from ..models import (ChoiceOptionModel, ChoiceFieldModel, FileModel, Group, class ChoiceFieldForm(ModelForm): class Meta: model = ChoiceFieldModel + fields = '__all__' class OptionalMultiChoiceModelForm(ModelForm): class Meta: model = OptionalMultiChoiceModel + fields = '__all__' class FileForm(Form): @@ -139,6 +141,7 @@ class FormsModelTestCase(TestCase): class BoundaryForm(ModelForm): class Meta: model = BoundaryModel + fields = '__all__' f = BoundaryForm({'positive_integer': 100}) self.assertTrue(f.is_valid()) @@ -154,6 +157,7 @@ class FormsModelTestCase(TestCase): class DefaultsForm(ModelForm): class Meta: model = Defaults + fields = '__all__' self.assertEqual(DefaultsForm().fields['name'].initial, 'class default value') self.assertEqual(DefaultsForm().fields['def_date'].initial, datetime.date(1980, 1, 1)) diff --git a/tests/generic_relations/tests.py b/tests/generic_relations/tests.py index 27b25185ea..dd9dc506ca 100644 --- a/tests/generic_relations/tests.py +++ b/tests/generic_relations/tests.py @@ -247,6 +247,7 @@ class CustomWidget(forms.TextInput): class TaggedItemForm(forms.ModelForm): class Meta: model = TaggedItem + fields = '__all__' widgets = {'tag': CustomWidget} class GenericInlineFormsetTest(TestCase): diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py index c54d632363..54eab7ffa4 100644 --- a/tests/generic_views/test_edit.py +++ b/tests/generic_views/test_edit.py @@ -1,12 +1,14 @@ from __future__ import absolute_import +import warnings + from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import reverse from django import forms from django.test import TestCase from django.utils.unittest import expectedFailure from django.views.generic.base import View -from django.views.generic.edit import FormMixin +from django.views.generic.edit import FormMixin, CreateView, UpdateView from . import views from .models import Artist, Author @@ -34,6 +36,7 @@ class ModelFormMixinTests(TestCase): form_class = views.AuthorGetQuerySetFormView().get_form_class() self.assertEqual(form_class._meta.model, Author) + class CreateViewTests(TestCase): urls = 'generic_views.urls' @@ -112,6 +115,45 @@ class CreateViewTests(TestCase): self.assertEqual(res.status_code, 302) self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/') + def test_create_view_with_restricted_fields(self): + + class MyCreateView(CreateView): + model = Author + fields = ['name'] + + self.assertEqual(list(MyCreateView().get_form_class().base_fields), + ['name']) + + def test_create_view_all_fields(self): + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always", PendingDeprecationWarning) + + class MyCreateView(CreateView): + model = Author + fields = '__all__' + + self.assertEqual(list(MyCreateView().get_form_class().base_fields), + ['name', 'slug']) + self.assertEqual(len(w), 0) + + + def test_create_view_without_explicit_fields(self): + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always", PendingDeprecationWarning) + + class MyCreateView(CreateView): + model = Author + + # Until end of the deprecation cycle, should still create the form + # as before: + self.assertEqual(list(MyCreateView().get_form_class().base_fields), + ['name', 'slug']) + + # but with a warning: + self.assertEqual(w[0].category, PendingDeprecationWarning) + class UpdateViewTests(TestCase): urls = 'generic_views.urls' diff --git a/tests/generic_views/test_forms.py b/tests/generic_views/test_forms.py index e036ad8afc..8c118e32a6 100644 --- a/tests/generic_views/test_forms.py +++ b/tests/generic_views/test_forms.py @@ -11,6 +11,7 @@ class AuthorForm(forms.ModelForm): class Meta: model = Author + fields = ['name', 'slug'] class ContactForm(forms.Form): diff --git a/tests/generic_views/views.py b/tests/generic_views/views.py index 69bfcb32dd..aa8777e8c6 100644 --- a/tests/generic_views/views.py +++ b/tests/generic_views/views.py @@ -85,15 +85,18 @@ class ContactView(generic.FormView): class ArtistCreate(generic.CreateView): model = Artist + fields = '__all__' class NaiveAuthorCreate(generic.CreateView): queryset = Author.objects.all() + fields = '__all__' class AuthorCreate(generic.CreateView): model = Author success_url = '/list/authors/' + fields = '__all__' class SpecializedAuthorCreate(generic.CreateView): @@ -112,19 +115,23 @@ class AuthorCreateRestricted(AuthorCreate): class ArtistUpdate(generic.UpdateView): model = Artist + fields = '__all__' class NaiveAuthorUpdate(generic.UpdateView): queryset = Author.objects.all() + fields = '__all__' class AuthorUpdate(generic.UpdateView): model = Author success_url = '/list/authors/' + fields = '__all__' class OneAuthorUpdate(generic.UpdateView): success_url = '/list/authors/' + fields = '__all__' def get_object(self): return Author.objects.get(pk=1) @@ -184,6 +191,8 @@ class BookDetail(BookConfig, generic.DateDetailView): pass class AuthorGetQuerySetFormView(generic.edit.ModelFormMixin): + fields = '__all__' + def get_queryset(self): return Author.objects.all() diff --git a/tests/i18n/forms.py b/tests/i18n/forms.py index abb99f443a..6e4def9c5e 100644 --- a/tests/i18n/forms.py +++ b/tests/i18n/forms.py @@ -24,3 +24,4 @@ class CompanyForm(forms.ModelForm): class Meta: model = Company + fields = '__all__' diff --git a/tests/inline_formsets/tests.py b/tests/inline_formsets/tests.py index df682d34ef..ad8a666cb5 100644 --- a/tests/inline_formsets/tests.py +++ b/tests/inline_formsets/tests.py @@ -10,7 +10,7 @@ from .models import Poet, Poem, School, Parent, Child class DeletionTests(TestCase): def test_deletion(self): - PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True) + PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__") poet = Poet.objects.create(name='test') poem = poet.poem_set.create(name='test poem') data = { @@ -32,7 +32,7 @@ class DeletionTests(TestCase): Make sure that an add form that is filled out, but marked for deletion doesn't cause validation errors. """ - PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True) + PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__") poet = Poet.objects.create(name='test') data = { 'poem_set-TOTAL_FORMS': '1', @@ -60,7 +60,7 @@ class DeletionTests(TestCase): Make sure that a change form that is filled out, but marked for deletion doesn't cause validation errors. """ - PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True) + PoemFormSet = inlineformset_factory(Poet, Poem, can_delete=True, fields="__all__") poet = Poet.objects.create(name='test') poem = poet.poem_set.create(name='test poem') data = { @@ -115,8 +115,8 @@ class InlineFormsetFactoryTest(TestCase): """ These should both work without a problem. """ - inlineformset_factory(Parent, Child, fk_name='mother') - inlineformset_factory(Parent, Child, fk_name='father') + inlineformset_factory(Parent, Child, fk_name='mother', fields="__all__") + inlineformset_factory(Parent, Child, fk_name='father', fields="__all__") def test_exception_on_unspecified_foreign_key(self): """ diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 96c3ecbdce..c5db011404 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals import datetime import os from decimal import Decimal +import warnings from django import forms from django.core.exceptions import FieldError @@ -30,19 +31,25 @@ if test_images: class ImageFileForm(forms.ModelForm): class Meta: model = ImageFile + fields = '__all__' + class OptionalImageFileForm(forms.ModelForm): class Meta: model = OptionalImageFile + fields = '__all__' + class ProductForm(forms.ModelForm): class Meta: model = Product + fields = '__all__' class PriceForm(forms.ModelForm): class Meta: model = Price + fields = '__all__' class BookForm(forms.ModelForm): @@ -66,11 +73,13 @@ class ExplicitPKForm(forms.ModelForm): class PostForm(forms.ModelForm): class Meta: model = Post + fields = '__all__' class DerivedPostForm(forms.ModelForm): class Meta: model = DerivedPost + fields = '__all__' class CustomAuthorForm(forms.ModelForm): @@ -78,61 +87,79 @@ class CustomAuthorForm(forms.ModelForm): class Meta: model = Author + fields = '__all__' class FlexDatePostForm(forms.ModelForm): class Meta: model = FlexibleDatePost + fields = '__all__' class BaseCategoryForm(forms.ModelForm): class Meta: model = Category + fields = '__all__' class ArticleForm(forms.ModelForm): class Meta: model = Article + fields = '__all__' class ArticleForm(forms.ModelForm): class Meta: model = Article + fields = '__all__' + class PartialArticleForm(forms.ModelForm): class Meta: model = Article fields = ('headline','pub_date') + class RoykoForm(forms.ModelForm): class Meta: model = Author + fields = '__all__' + class TestArticleForm(forms.ModelForm): class Meta: model = Article + fields = '__all__' + class PartialArticleFormWithSlug(forms.ModelForm): class Meta: model = Article - fields=('headline', 'slug', 'pub_date') + fields = ('headline', 'slug', 'pub_date') + class ArticleStatusForm(forms.ModelForm): class Meta: model = ArticleStatus + fields = '__all__' + class InventoryForm(forms.ModelForm): class Meta: model = Inventory + fields = '__all__' + class SelectInventoryForm(forms.Form): items = forms.ModelMultipleChoiceField(Inventory.objects.all(), to_field_name='barcode') + class CustomFieldForExclusionForm(forms.ModelForm): class Meta: model = CustomFieldForExclusionModel fields = ['name', 'markup'] + class ShortCategory(forms.ModelForm): name = forms.CharField(max_length=5) slug = forms.CharField(max_length=5) @@ -140,30 +167,44 @@ class ShortCategory(forms.ModelForm): class Meta: model = Category + fields = '__all__' + class ImprovedArticleForm(forms.ModelForm): class Meta: model = ImprovedArticle + fields = '__all__' + class ImprovedArticleWithParentLinkForm(forms.ModelForm): class Meta: model = ImprovedArticleWithParentLink + fields = '__all__' + class BetterAuthorForm(forms.ModelForm): class Meta: model = BetterAuthor + fields = '__all__' + class AuthorProfileForm(forms.ModelForm): class Meta: model = AuthorProfile + fields = '__all__' + class TextFileForm(forms.ModelForm): class Meta: model = TextFile + fields = '__all__' + class BigIntForm(forms.ModelForm): class Meta: model = BigInt + fields = '__all__' + class ModelFormWithMedia(forms.ModelForm): class Media: @@ -173,19 +214,25 @@ class ModelFormWithMedia(forms.ModelForm): } class Meta: model = TextFile + fields = '__all__' + class CommaSeparatedIntegerForm(forms.ModelForm): - class Meta: - model = CommaSeparatedInteger + class Meta: + model = CommaSeparatedInteger + fields = '__all__' + class PriceFormWithoutQuantity(forms.ModelForm): class Meta: model = Price exclude = ('quantity',) + class ColourfulItemForm(forms.ModelForm): class Meta: model = ColourfulItem + fields = '__all__' class ModelFormBaseTest(TestCase): @@ -193,6 +240,25 @@ class ModelFormBaseTest(TestCase): self.assertEqual(list(BaseCategoryForm.base_fields), ['name', 'slug', 'url']) + def test_missing_fields_attribute(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always", PendingDeprecationWarning) + + class MissingFieldsForm(forms.ModelForm): + class Meta: + model = Category + + # There is some internal state in warnings module which means that + # if a warning has been seen already, the catch_warnings won't + # have recorded it. The following line therefore will not work reliably: + + # self.assertEqual(w[0].category, PendingDeprecationWarning) + + # Until end of the deprecation cycle, should still create the + # form as before: + self.assertEqual(list(MissingFieldsForm.base_fields), + ['name', 'slug', 'url']) + def test_extra_fields(self): class ExtraFields(BaseCategoryForm): some_extra_field = forms.BooleanField() @@ -206,6 +272,33 @@ class ModelFormBaseTest(TestCase): class Meta: model = Category + fields = '__all__' + + self.assertTrue(isinstance(ReplaceField.base_fields['url'], + forms.fields.BooleanField)) + + def test_replace_field_variant_2(self): + # Should have the same result as before, + # but 'fields' attribute specified differently + class ReplaceField(forms.ModelForm): + url = forms.BooleanField() + + class Meta: + model = Category + fields = ['url'] + + self.assertTrue(isinstance(ReplaceField.base_fields['url'], + forms.fields.BooleanField)) + + def test_replace_field_variant_3(self): + # Should have the same result as before, + # but 'fields' attribute specified differently + class ReplaceField(forms.ModelForm): + url = forms.BooleanField() + + class Meta: + model = Category + fields = [] # url will still appear, since it is explicit above self.assertTrue(isinstance(ReplaceField.base_fields['url'], forms.fields.BooleanField)) @@ -216,19 +309,11 @@ class ModelFormBaseTest(TestCase): class Meta: model = Author + fields = '__all__' wf = AuthorForm({'name': 'Richard Lockridge'}) self.assertTrue(wf.is_valid()) - def test_limit_fields(self): - class LimitFields(forms.ModelForm): - class Meta: - model = Category - fields = ['url'] - - self.assertEqual(list(LimitFields.base_fields), - ['url']) - def test_limit_nonexistent_field(self): expected_msg = 'Unknown field(s) (nonexistent) specified for Category' with self.assertRaisesMessage(FieldError, expected_msg): @@ -294,6 +379,7 @@ class ModelFormBaseTest(TestCase): """ class Meta: model = Article + fields = '__all__' # MixModelForm is now an Article-related thing, because MixModelForm.Meta # overrides BaseCategoryForm.Meta. @@ -348,6 +434,7 @@ class ModelFormBaseTest(TestCase): class Meta: model = Category + fields = '__all__' class SubclassMeta(SomeCategoryForm): """ We can also subclass the Meta inner class to change the fields diff --git a/tests/model_forms_regress/tests.py b/tests/model_forms_regress/tests.py index 90c907f2a6..3f15c35938 100644 --- a/tests/model_forms_regress/tests.py +++ b/tests/model_forms_regress/tests.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, unicode_literals from datetime import date +import warnings from django import forms from django.core.exceptions import FieldError, ValidationError @@ -43,9 +44,12 @@ class ModelMultipleChoiceFieldTests(TestCase): f.clean([p.pk for p in Person.objects.all()[8:9]]) self.assertTrue(self._validator_run) + class TripleForm(forms.ModelForm): class Meta: model = Triple + fields = '__all__' + class UniqueTogetherTests(TestCase): def test_multiple_field_unique_together(self): @@ -63,15 +67,18 @@ class UniqueTogetherTests(TestCase): form = TripleForm({'left': '1', 'middle': '3', 'right': '1'}) self.assertTrue(form.is_valid()) + class TripleFormWithCleanOverride(forms.ModelForm): class Meta: model = Triple + fields = '__all__' def clean(self): if not self.cleaned_data['left'] == self.cleaned_data['right']: raise forms.ValidationError('Left and right should be equal') return self.cleaned_data + class OverrideCleanTests(TestCase): def test_override_clean(self): """ @@ -84,6 +91,7 @@ class OverrideCleanTests(TestCase): # by form.full_clean(). self.assertEqual(form.instance.left, 1) + # Regression test for #12960. # Make sure the cleaned_data returned from ModelForm.clean() is applied to the # model instance. @@ -95,6 +103,8 @@ class PublicationForm(forms.ModelForm): class Meta: model = Publication + fields = '__all__' + class ModelFormCleanTest(TestCase): def test_model_form_clean_applies_to_model(self): @@ -103,9 +113,12 @@ class ModelFormCleanTest(TestCase): publication = form.save() self.assertEqual(publication.title, 'TEST') + class FPForm(forms.ModelForm): class Meta: model = FilePathModel + fields = '__all__' + class FilePathFieldTests(TestCase): def test_file_path_field_blank(self): @@ -133,7 +146,8 @@ class ManyToManyCallableInitialTests(TestCase): book3 = Publication.objects.create(title="Third Book", date_published=date(2009,1,1)) # Create a ModelForm, instantiate it, and check that the output is as expected - ModelForm = modelform_factory(Article, formfield_callback=formfield_for_dbfield) + ModelForm = modelform_factory(Article, fields="__all__", + formfield_callback=formfield_for_dbfield) form = ModelForm() self.assertHTMLEqual(form.as_ul(), """

  • Hold down "Control", or "Command" on a Mac, to select more than one.
  • """ % (book1.pk, book2.pk, book3.pk)) + class CFFForm(forms.ModelForm): class Meta: model = CustomFF + fields = '__all__' + class CustomFieldSaveTests(TestCase): def test_save(self): @@ -168,9 +185,12 @@ class ModelChoiceIteratorTests(TestCase): f = Form() self.assertEqual(len(f.fields["publications"].choices), 1) + class RealPersonForm(forms.ModelForm): class Meta: model = RealPerson + fields = '__all__' + class CustomModelFormSaveMethod(TestCase): def test_string_message(self): @@ -230,9 +250,12 @@ class TestTicket11183(TestCase): self.assertTrue(field1 is not ModelChoiceForm.base_fields['person']) self.assertTrue(field1.widget.choices.field is field1) + class HomepageForm(forms.ModelForm): class Meta: model = Homepage + fields = '__all__' + class URLFieldTests(TestCase): def test_url_on_modelform(self): @@ -274,6 +297,7 @@ class FormFieldCallbackTests(TestCase): class Meta: model = Person widgets = {'name': widget} + fields = "__all__" Form = modelform_factory(Person, form=BaseForm) self.assertTrue(Form.base_fields['name'].widget is widget) @@ -285,11 +309,11 @@ class FormFieldCallbackTests(TestCase): widget = forms.Textarea() # Without a widget should not set the widget to textarea - Form = modelform_factory(Person) + Form = modelform_factory(Person, fields="__all__") self.assertNotEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) # With a widget should not set the widget to textarea - Form = modelform_factory(Person, widgets={'name':widget}) + Form = modelform_factory(Person, fields="__all__", widgets={'name':widget}) self.assertEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) def test_custom_callback(self): @@ -307,6 +331,7 @@ class FormFieldCallbackTests(TestCase): class Meta: model = Person widgets = {'name': widget} + fields = "__all__" _ = modelform_factory(Person, form=BaseForm, formfield_callback=callback) @@ -317,7 +342,7 @@ class FormFieldCallbackTests(TestCase): def test_bad_callback(self): # A bad callback provided by user still gives an error - self.assertRaises(TypeError, modelform_factory, Person, + self.assertRaises(TypeError, modelform_factory, Person, fields="__all__", formfield_callback='not a function or callable') @@ -362,6 +387,8 @@ class InvalidFieldAndFactory(TestCase): class DocumentForm(forms.ModelForm): class Meta: model = Document + fields = '__all__' + class FileFieldTests(unittest.TestCase): def test_clean_false(self): @@ -425,6 +452,7 @@ class FileFieldTests(unittest.TestCase): self.assertTrue('something.txt' in rendered) self.assertTrue('myfile-clear' in rendered) + class EditionForm(forms.ModelForm): author = forms.ModelChoiceField(queryset=Person.objects.all()) publication = forms.ModelChoiceField(queryset=Publication.objects.all()) @@ -433,6 +461,8 @@ class EditionForm(forms.ModelForm): class Meta: model = Edition + fields = '__all__' + class UniqueErrorsTests(TestCase): def setUp(self): @@ -473,7 +503,7 @@ class EmptyFieldsTestCase(TestCase): def test_empty_fields_to_construct_instance(self): "No fields should be set on a model instance if construct_instance receives fields=()" - form = modelform_factory(Person)({'name': 'John Doe'}) + form = modelform_factory(Person, fields="__all__")({'name': 'John Doe'}) self.assertTrue(form.is_valid()) instance = construct_instance(form, Person(), fields=()) self.assertEqual(instance.name, '') @@ -485,10 +515,25 @@ class CustomMetaclass(ModelFormMetaclass): new.base_fields = {} return new + class CustomMetaclassForm(six.with_metaclass(CustomMetaclass, forms.ModelForm)): pass + class CustomMetaclassTestCase(TestCase): def test_modelform_factory_metaclass(self): - new_cls = modelform_factory(Person, form=CustomMetaclassForm) + new_cls = modelform_factory(Person, fields="__all__", form=CustomMetaclassForm) self.assertEqual(new_cls.base_fields, {}) + + +class TestTicket19733(TestCase): + def test_modelform_factory_without_fields(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always", PendingDeprecationWarning) + # This should become an error once deprecation cycle is complete. + form = modelform_factory(Person) + self.assertEqual(w[0].category, PendingDeprecationWarning) + + def test_modelform_factory_with_all_fields(self): + form = modelform_factory(Person, fields="__all__") + self.assertEqual(form.base_fields.keys(), ["name"]) diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index 8d0c017a61..8cfdf53995 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -21,7 +21,7 @@ from .models import (Author, BetterAuthor, Book, BookWithCustomPK, class DeletionTests(TestCase): def test_deletion(self): - PoetFormSet = modelformset_factory(Poet, can_delete=True) + PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True) poet = Poet.objects.create(name='test') data = { 'form-TOTAL_FORMS': '1', @@ -41,7 +41,7 @@ class DeletionTests(TestCase): Make sure that an add form that is filled out, but marked for deletion doesn't cause validation errors. """ - PoetFormSet = modelformset_factory(Poet, can_delete=True) + PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True) poet = Poet.objects.create(name='test') # One existing untouched and two new unvalid forms data = { @@ -75,7 +75,7 @@ class DeletionTests(TestCase): Make sure that a change form that is filled out, but marked for deletion doesn't cause validation errors. """ - PoetFormSet = modelformset_factory(Poet, can_delete=True) + PoetFormSet = modelformset_factory(Poet, fields="__all__", can_delete=True) poet = Poet.objects.create(name='test') data = { 'form-TOTAL_FORMS': '1', @@ -100,7 +100,7 @@ class DeletionTests(TestCase): class ModelFormsetTest(TestCase): def test_simple_save(self): qs = Author.objects.all() - AuthorFormSet = modelformset_factory(Author, extra=3) + AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=3) formset = AuthorFormSet(queryset=qs) self.assertEqual(len(formset.forms), 3) @@ -138,7 +138,7 @@ class ModelFormsetTest(TestCase): # we'll use it to display them in alphabetical order by name. qs = Author.objects.order_by('name') - AuthorFormSet = modelformset_factory(Author, extra=1, can_delete=False) + AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=1, can_delete=False) formset = AuthorFormSet(queryset=qs) self.assertEqual(len(formset.forms), 3) @@ -176,7 +176,7 @@ class ModelFormsetTest(TestCase): # marked for deletion, make sure we don't save that form. qs = Author.objects.order_by('name') - AuthorFormSet = modelformset_factory(Author, extra=1, can_delete=True) + AuthorFormSet = modelformset_factory(Author, fields="__all__", extra=1, can_delete=True) formset = AuthorFormSet(queryset=qs) self.assertEqual(len(formset.forms), 4) @@ -256,7 +256,7 @@ class ModelFormsetTest(TestCase): author4 = Author.objects.create(name='John Steinbeck') - AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, extra=1, can_delete=True) + AuthorMeetingFormSet = modelformset_factory(AuthorMeeting, fields="__all__", extra=1, can_delete=True) data = { 'form-TOTAL_FORMS': '2', # the number of forms rendered 'form-INITIAL_FORMS': '1', # the number of forms with initial data @@ -294,22 +294,22 @@ class ModelFormsetTest(TestCase): qs = Author.objects.order_by('name') - AuthorFormSet = modelformset_factory(Author, max_num=None, extra=3) + AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=None, extra=3) formset = AuthorFormSet(queryset=qs) self.assertEqual(len(formset.forms), 6) self.assertEqual(len(formset.extra_forms), 3) - AuthorFormSet = modelformset_factory(Author, max_num=4, extra=3) + AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=4, extra=3) formset = AuthorFormSet(queryset=qs) self.assertEqual(len(formset.forms), 4) self.assertEqual(len(formset.extra_forms), 1) - AuthorFormSet = modelformset_factory(Author, max_num=0, extra=3) + AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=0, extra=3) formset = AuthorFormSet(queryset=qs) self.assertEqual(len(formset.forms), 3) self.assertEqual(len(formset.extra_forms), 0) - AuthorFormSet = modelformset_factory(Author, max_num=None) + AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=None) formset = AuthorFormSet(queryset=qs) self.assertQuerysetEqual(formset.get_queryset(), [ '', @@ -317,7 +317,7 @@ class ModelFormsetTest(TestCase): '', ]) - AuthorFormSet = modelformset_factory(Author, max_num=0) + AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=0) formset = AuthorFormSet(queryset=qs) self.assertQuerysetEqual(formset.get_queryset(), [ '', @@ -325,7 +325,7 @@ class ModelFormsetTest(TestCase): '', ]) - AuthorFormSet = modelformset_factory(Author, max_num=4) + AuthorFormSet = modelformset_factory(Author, fields="__all__", max_num=4) formset = AuthorFormSet(queryset=qs) self.assertQuerysetEqual(formset.get_queryset(), [ '', @@ -343,7 +343,7 @@ class ModelFormsetTest(TestCase): author.save() return author - PoetFormSet = modelformset_factory(Poet, form=PoetForm) + PoetFormSet = modelformset_factory(Poet, fields="__all__", form=PoetForm) data = { 'form-TOTAL_FORMS': '3', # the number of forms rendered @@ -387,7 +387,7 @@ class ModelFormsetTest(TestCase): self.assertFalse("subtitle" in formset.forms[0].fields) def test_model_inheritance(self): - BetterAuthorFormSet = modelformset_factory(BetterAuthor) + BetterAuthorFormSet = modelformset_factory(BetterAuthor, fields="__all__") formset = BetterAuthorFormSet() self.assertEqual(len(formset.forms), 1) self.assertHTMLEqual(formset.forms[0].as_p(), @@ -440,7 +440,7 @@ class ModelFormsetTest(TestCase): # We can also create a formset that is tied to a parent model. This is # how the admin system's edit inline functionality works. - AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=3) + AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=3, fields="__all__") author = Author.objects.create(name='Charles Baudelaire') formset = AuthorBooksFormSet(instance=author) @@ -474,7 +474,7 @@ class ModelFormsetTest(TestCase): # another one. This time though, an edit form will be available for # every existing book. - AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) + AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__") author = Author.objects.get(name='Charles Baudelaire') formset = AuthorBooksFormSet(instance=author) @@ -514,7 +514,7 @@ class ModelFormsetTest(TestCase): def test_inline_formsets_save_as_new(self): # The save_as_new parameter lets you re-associate the data to a new # instance. This is used in the admin for save_as functionality. - AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) + AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__") author = Author.objects.create(name='Charles Baudelaire') data = { @@ -553,7 +553,7 @@ class ModelFormsetTest(TestCase): # primary key that is not the fk to the parent object. self.maxDiff = 1024 - AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1) + AuthorBooksFormSet2 = inlineformset_factory(Author, BookWithCustomPK, can_delete=False, extra=1, fields="__all__") author = Author.objects.create(pk=1, name='Charles Baudelaire') formset = AuthorBooksFormSet2(instance=author) @@ -585,7 +585,7 @@ class ModelFormsetTest(TestCase): # Test inline formsets where the inline-edited object uses multi-table # inheritance, thus has a non AutoField yet auto-created primary key. - AuthorBooksFormSet3 = inlineformset_factory(Author, AlternateBook, can_delete=False, extra=1) + AuthorBooksFormSet3 = inlineformset_factory(Author, AlternateBook, can_delete=False, extra=1, fields="__all__") author = Author.objects.create(pk=1, name='Charles Baudelaire') formset = AuthorBooksFormSet3(instance=author) @@ -616,7 +616,7 @@ class ModelFormsetTest(TestCase): # Test inline formsets where the inline-edited object has a # unique_together constraint with a nullable member - AuthorBooksFormSet4 = inlineformset_factory(Author, BookWithOptionalAltEditor, can_delete=False, extra=2) + AuthorBooksFormSet4 = inlineformset_factory(Author, BookWithOptionalAltEditor, can_delete=False, extra=2, fields="__all__") author = Author.objects.create(pk=1, name='Charles Baudelaire') data = { @@ -640,7 +640,7 @@ class ModelFormsetTest(TestCase): self.assertEqual(book2.title, 'Les Fleurs du Mal') def test_inline_formsets_with_custom_save_method(self): - AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2) + AuthorBooksFormSet = inlineformset_factory(Author, Book, can_delete=False, extra=2, fields="__all__") author = Author.objects.create(pk=1, name='Charles Baudelaire') book1 = Book.objects.create(pk=1, author=author, title='Les Paradis Artificiels') book2 = Book.objects.create(pk=2, author=author, title='Les Fleurs du Mal') @@ -655,7 +655,7 @@ class ModelFormsetTest(TestCase): poem.save() return poem - PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm) + PoemFormSet = inlineformset_factory(Poet, Poem, form=PoemForm, fields="__all__") data = { 'poem_set-TOTAL_FORMS': '3', # the number of forms rendered @@ -732,7 +732,7 @@ class ModelFormsetTest(TestCase): def test_custom_pk(self): # We need to ensure that it is displayed - CustomPrimaryKeyFormSet = modelformset_factory(CustomPrimaryKey) + CustomPrimaryKeyFormSet = modelformset_factory(CustomPrimaryKey, fields="__all__") formset = CustomPrimaryKeyFormSet() self.assertEqual(len(formset.forms), 1) self.assertHTMLEqual(formset.forms[0].as_p(), @@ -743,7 +743,7 @@ class ModelFormsetTest(TestCase): place = Place.objects.create(pk=1, name='Giordanos', city='Chicago') - FormSet = inlineformset_factory(Place, Owner, extra=2, can_delete=False) + FormSet = inlineformset_factory(Place, Owner, extra=2, can_delete=False, fields="__all__") formset = FormSet(instance=place) self.assertEqual(len(formset.forms), 2) self.assertHTMLEqual(formset.forms[0].as_p(), @@ -799,7 +799,7 @@ class ModelFormsetTest(TestCase): # Ensure a custom primary key that is a ForeignKey or OneToOneField get rendered for the user to choose. - FormSet = modelformset_factory(OwnerProfile) + FormSet = modelformset_factory(OwnerProfile, fields="__all__") formset = FormSet() self.assertHTMLEqual(formset.forms[0].as_p(), '

    ' % (band2.id, self.band.id)) class AdminConcertForm(forms.ModelForm): - class Meta: - model = Concert + pass def __init__(self, *args, **kwargs): super(AdminConcertForm, self).__init__(*args, **kwargs) @@ -685,9 +681,6 @@ class ValidationTests(unittest.TestCase): class AdminBandForm(forms.ModelForm): delete = forms.BooleanField() - class Meta: - model = Band - class BandAdmin(ModelAdmin): form = AdminBandForm diff --git a/tests/timezones/forms.py b/tests/timezones/forms.py index 3c9c31167e..45fb1d080b 100644 --- a/tests/timezones/forms.py +++ b/tests/timezones/forms.py @@ -11,3 +11,4 @@ class EventSplitForm(forms.Form): class EventModelForm(forms.ModelForm): class Meta: model = Event + fields = '__all__' From 9a3708cec8cf76165afd0fd46d1267d7cae772ca Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 9 May 2013 19:33:02 +0200 Subject: [PATCH 67/70] Fixed a test that fails under Python 3. --- tests/model_forms_regress/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/model_forms_regress/tests.py b/tests/model_forms_regress/tests.py index 3f15c35938..0e033e033f 100644 --- a/tests/model_forms_regress/tests.py +++ b/tests/model_forms_regress/tests.py @@ -536,4 +536,4 @@ class TestTicket19733(TestCase): def test_modelform_factory_with_all_fields(self): form = modelform_factory(Person, fields="__all__") - self.assertEqual(form.base_fields.keys(), ["name"]) + self.assertEqual(list(form.base_fields), ["name"]) From 465a29abe09012059b4a8ff34f1020f48879ad71 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 10 May 2013 12:47:03 +0200 Subject: [PATCH 68/70] Fixed #20384 -- Forced GeoIP_open path argument to bytestring Thanks Julian Wachholz for the report. --- django/contrib/gis/geoip/base.py | 7 ++++--- django/contrib/gis/geoip/tests.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/django/contrib/gis/geoip/base.py b/django/contrib/gis/geoip/base.py index 4b8b6e1ed3..4b114f8b2b 100644 --- a/django/contrib/gis/geoip/base.py +++ b/django/contrib/gis/geoip/base.py @@ -11,6 +11,7 @@ from django.contrib.gis.geoip.prototypes import ( GeoIP_country_name_by_addr, GeoIP_country_name_by_name) from django.utils import six +from django.utils.encoding import force_bytes # Regular expressions for recognizing the GeoIP free database editions. free_regex = re.compile(r'^GEO-\d{3}FREE') @@ -97,18 +98,18 @@ class GeoIP(object): # and/or city datasets exist, then try and open them. country_db = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat')) if os.path.isfile(country_db): - self._country = GeoIP_open(country_db, cache) + self._country = GeoIP_open(force_bytes(country_db), cache) self._country_file = country_db city_db = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat')) if os.path.isfile(city_db): - self._city = GeoIP_open(city_db, cache) + self._city = GeoIP_open(force_bytes(city_db), cache) self._city_file = city_db elif os.path.isfile(path): # Otherwise, some detective work will be needed to figure # out whether the given database path is for the GeoIP country # or city databases. - ptr = GeoIP_open(path, cache) + ptr = GeoIP_open(force_bytes(path), cache) info = GeoIP_database_info(ptr) if lite_regex.match(info): # GeoLite City database detected. diff --git a/django/contrib/gis/geoip/tests.py b/django/contrib/gis/geoip/tests.py index c890c4f4ba..458c947a27 100644 --- a/django/contrib/gis/geoip/tests.py +++ b/django/contrib/gis/geoip/tests.py @@ -103,8 +103,8 @@ class GeoIPTest(unittest.TestCase): def test05_unicode_response(self): "Testing that GeoIP strings are properly encoded, see #16553." g = GeoIP() - d = g.city('62.224.93.23') - self.assertEqual('Schümberg', d['city']) + d = g.city("www.osnabrueck.de") + self.assertEqual('Osnabrück', d['city']) def test06_unicode_query(self): "Testing that GeoIP accepts unicode string queries, see #17059." From 7b00d90208e998debc60e51cadb352645c91e3fd Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 10 May 2013 13:18:07 +0200 Subject: [PATCH 69/70] [py3] Made GeoIP tests pass with Python 3 --- django/contrib/gis/geoip/base.py | 25 +++++++++++-------------- django/contrib/gis/geoip/prototypes.py | 7 ++++++- django/contrib/gis/geoip/tests.py | 6 ------ 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/django/contrib/gis/geoip/base.py b/django/contrib/gis/geoip/base.py index 4b114f8b2b..d4793a2ae9 100644 --- a/django/contrib/gis/geoip/base.py +++ b/django/contrib/gis/geoip/base.py @@ -137,9 +137,6 @@ class GeoIP(object): if not isinstance(query, six.string_types): raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__) - # GeoIP only takes ASCII-encoded strings. - query = query.encode('ascii') - # Extra checks for the existence of country and city databases. if city_or_country and not (self._country or self._city): raise GeoIPException('Invalid GeoIP country and city data files.') @@ -148,8 +145,8 @@ class GeoIP(object): elif city and not self._city: raise GeoIPException('Invalid GeoIP city data file: %s' % self._city_file) - # Return the query string back to the caller. - return query + # Return the query string back to the caller. GeoIP only takes bytestrings. + return force_bytes(query) def city(self, query): """ @@ -157,33 +154,33 @@ class GeoIP(object): Fully Qualified Domain Name (FQDN). Some information in the dictionary may be undefined (None). """ - query = self._check_query(query, city=True) + enc_query = self._check_query(query, city=True) if ipv4_re.match(query): # If an IP address was passed in - return GeoIP_record_by_addr(self._city, c_char_p(query)) + return GeoIP_record_by_addr(self._city, c_char_p(enc_query)) else: # If a FQDN was passed in. - return GeoIP_record_by_name(self._city, c_char_p(query)) + return GeoIP_record_by_name(self._city, c_char_p(enc_query)) def country_code(self, query): "Returns the country code for the given IP Address or FQDN." - query = self._check_query(query, city_or_country=True) + enc_query = self._check_query(query, city_or_country=True) if self._country: if ipv4_re.match(query): - return GeoIP_country_code_by_addr(self._country, query) + return GeoIP_country_code_by_addr(self._country, enc_query) else: - return GeoIP_country_code_by_name(self._country, query) + return GeoIP_country_code_by_name(self._country, enc_query) else: return self.city(query)['country_code'] def country_name(self, query): "Returns the country name for the given IP Address or FQDN." - query = self._check_query(query, city_or_country=True) + enc_query = self._check_query(query, city_or_country=True) if self._country: if ipv4_re.match(query): - return GeoIP_country_name_by_addr(self._country, query) + return GeoIP_country_name_by_addr(self._country, enc_query) else: - return GeoIP_country_name_by_name(self._country, query) + return GeoIP_country_name_by_name(self._country, enc_query) else: return self.city(query)['country_name'] diff --git a/django/contrib/gis/geoip/prototypes.py b/django/contrib/gis/geoip/prototypes.py index 1cec0d5c24..283d721395 100644 --- a/django/contrib/gis/geoip/prototypes.py +++ b/django/contrib/gis/geoip/prototypes.py @@ -92,7 +92,7 @@ def check_string(result, func, cargs): free(result) else: s = '' - return s + return s.decode() GeoIP_database_info = lgeoip.GeoIP_database_info GeoIP_database_info.restype = geoip_char_p @@ -100,7 +100,12 @@ GeoIP_database_info.errcheck = check_string # String output routines. def string_output(func): + def _err_check(result, func, cargs): + if result: + return result.decode() + return result func.restype = c_char_p + func.errcheck = _err_check return func GeoIP_country_code_by_addr = string_output(lgeoip.GeoIP_country_code_by_addr) diff --git a/django/contrib/gis/geoip/tests.py b/django/contrib/gis/geoip/tests.py index 458c947a27..bb4a3e7e23 100644 --- a/django/contrib/gis/geoip/tests.py +++ b/django/contrib/gis/geoip/tests.py @@ -106,12 +106,6 @@ class GeoIPTest(unittest.TestCase): d = g.city("www.osnabrueck.de") self.assertEqual('Osnabrück', d['city']) - def test06_unicode_query(self): - "Testing that GeoIP accepts unicode string queries, see #17059." - g = GeoIP() - d = g.country('whitehouse.gov') - self.assertEqual('US', d['country_code']) - def suite(): s = unittest.TestSuite() From bdd285723f9b0044eca690634c412c1c3eec76c0 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Fri, 10 May 2013 13:23:32 +0200 Subject: [PATCH 70/70] Fixed #20385 -- Typo in files docs. --- docs/topics/files.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/files.txt b/docs/topics/files.txt index c05f98ef7e..fb3cdd4af9 100644 --- a/docs/topics/files.txt +++ b/docs/topics/files.txt @@ -94,7 +94,7 @@ The following approach may be used to close files automatically:: True Closing files is especially important when accessing file fields in a loop -over a large number of objects:: If files are not manually closed after +over a large number of objects. If files are not manually closed after accessing them, the risk of running out of file descriptors may arise. This may lead to the following error::