From 9f7b277d2e14abd89972f7593aad3543a5028760 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 18 Apr 2013 13:44:20 -0400 Subject: 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(-) (limited to 'docs/topics') 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'), -- cgit v1.3 From bfe25de42993fdbfccd4759f48ac6694e1ed32d9 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Fri, 19 Apr 2013 09:53:19 +0200 Subject: Explained the pattern for special methods compatibility. --- docs/topics/python3.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'docs/topics') 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 -- cgit v1.3 From 78c842a3230f026ad678d243e5459cd6b314d99a Mon Sep 17 00:00:00 2001 From: Juan Catalano Date: Sun, 24 Mar 2013 22:53:48 -0700 Subject: 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 + docs/ref/class-based-views/generic-date-based.txt | 2 +- docs/ref/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(-) (limited to 'docs/topics') 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 -- cgit v1.3 From 73c26f0c95e83582e933e0ef2f71a013f7a9740e Mon Sep 17 00:00:00 2001 From: Baptiste Mispelon Date: Mon, 22 Apr 2013 19:04:28 +0200 Subject: Fixed #20270 -- Fixed error in AjaxResponseMixin documentation --- docs/topics/class-based-views/generic-editing.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'docs/topics') 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 -- cgit v1.3 From b47b0211f52dad9d816db587a33f2d7055a226b3 Mon Sep 17 00:00:00 2001 From: Thomas Thurman Date: Mon, 29 Apr 2013 14:30:51 +0100 Subject: 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(-) (limited to 'docs/topics') 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 -- cgit v1.3 From 1267d2d9bc8bbb38406a676de31c861ec40b5567 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 29 Apr 2013 19:40:03 +0200 Subject: 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(-) (limited to 'docs/topics') 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` -- cgit v1.3 From cee96b87c04d365c7649726da86d86cb655b05da Mon Sep 17 00:00:00 2001 From: Ana Krivokapic Date: Tue, 30 Apr 2013 18:38:59 +0200 Subject: Fix two typos in database transactions docs --- docs/topics/db/transactions.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/topics') 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 -- cgit v1.3 From a96bff179a57c6c7fb79585120088f155831a6ae Mon Sep 17 00:00:00 2001 From: Christopher Allen-Poole Date: Sun, 5 May 2013 10:22:25 -0400 Subject: Found a mistake in SQL documentation --- docs/topics/db/sql.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/topics') 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]) -- cgit v1.3 From e88680899490b9ed8b58c88ee78dc189506204c5 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 6 May 2013 13:45:24 -0400 Subject: Fixed #20274 - Added some clarifying section headings in the test docs. --- docs/topics/testing/advanced.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'docs/topics') 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]) -- cgit v1.3 From bc02a963db3aeebf7c349d83a492b6e093f42b00 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 6 May 2013 13:55:02 -0400 Subject: 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(-) (limited to 'docs/topics') 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) -- cgit v1.3 From 1ad83145dff92117839400e4cfacdfee6473b2a6 Mon Sep 17 00:00:00 2001 From: Mike Fogel Date: Mon, 6 May 2013 22:43:06 -0700 Subject: Remove outdated ForeignKey manager documentation. --- docs/topics/db/queries.txt | 9 --------- 1 file changed, 9 deletions(-) (limited to 'docs/topics') 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 -- cgit v1.3 From d89b421352998020b9a9f2e7c3fd2ab8cc352593 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Tue, 7 May 2013 17:05:01 -0600 Subject: Python 2.5 is not relevant to master. --- docs/topics/testing/overview.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/topics') 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 -- cgit v1.3 From f026a519aea8f3ea7ca339bfbbb007e1ee0068b0 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Thu, 21 Feb 2013 21:56:55 +0000 Subject: 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 + .../contrib/formtools/tests/wizard/test_forms.py | 4 +- .../formtools/tests/wizard/wizardtests/tests.py | 1 + django/forms/models.py | 57 ++++++- django/views/generic/edit.py | 11 +- docs/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 + docs/topics/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(-) (limited to 'docs/topics') 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__' -- cgit v1.3 From bdd285723f9b0044eca690634c412c1c3eec76c0 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Fri, 10 May 2013 13:23:32 +0200 Subject: Fixed #20385 -- Typo in files docs. --- docs/topics/files.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/topics') 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:: -- cgit v1.3