From 31fadc120213284da76801cc7bc56e9f32d7281b Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Fri, 26 Jul 2013 11:59:40 +0300 Subject: Fixed #20625 -- Chainable Manager/QuerySet methods. Additionally this patch solves the orthogonal problem that specialized `QuerySet` like `ValuesQuerySet` didn't inherit from the current `QuerySet` type. This wasn't an issue until now because we didn't officially support custom `QuerySet` but it became necessary with the introduction of this new feature. Thanks aaugustin, akaariai, carljm, charettes, mjtamlyn, shaib and timgraham for the reviews. --- docs/ref/models/querysets.txt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'docs/ref/models') diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 3963785733..c3f6a660b4 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -121,9 +121,7 @@ described here. QuerySet API ============ -Though you usually won't create one manually — you'll go through a -:class:`~django.db.models.Manager` — here's the formal declaration of a -``QuerySet``: +Here's the formal declaration of a ``QuerySet``: .. class:: QuerySet([model=None, query=None, using=None]) @@ -1866,6 +1864,17 @@ DO_NOTHING do not prevent taking the fast-path in deletion. Note that the queries generated in object deletion is an implementation detail subject to change. +as_manager +~~~~~~~~~~ + +.. classmethod:: as_manager() + +.. versionadded:: 1.7 + +Class method that returns an instance of :class:`~django.db.models.Manager` +with a copy of the ``QuerySet``'s methods. See +:ref:`create-manager-with-queryset-methods` for more details. + .. _field-lookups: Field lookups -- cgit v1.3 From 47c755327be9a3b976f5f78fc21f336f27aa6413 Mon Sep 17 00:00:00 2001 From: Julien Phalip Date: Sat, 27 Jul 2013 18:45:25 -0700 Subject: Fixed a number of minor misspellings. --- django/contrib/admin/filters.py | 2 +- django/contrib/formtools/wizard/views.py | 2 +- django/contrib/gis/geoip/prototypes.py | 2 +- django/core/management/__init__.py | 2 +- django/core/management/commands/dumpdata.py | 2 +- django/db/backends/oracle/compiler.py | 2 +- django/db/backends/sqlite3/base.py | 2 +- django/db/models/fields/__init__.py | 2 +- docs/howto/initial-data.txt | 2 +- docs/ref/contrib/admin/actions.txt | 2 +- docs/ref/contrib/staticfiles.txt | 2 +- docs/ref/django-admin.txt | 4 ++-- docs/ref/models/instances.txt | 2 +- docs/releases/1.2.1.txt | 2 +- docs/releases/1.5-beta-1.txt | 2 +- docs/topics/class-based-views/mixins.txt | 2 +- docs/topics/db/transactions.txt | 2 +- docs/topics/http/sessions.txt | 2 +- docs/topics/http/urls.txt | 2 +- docs/topics/testing/advanced.txt | 2 +- docs/topics/testing/overview.txt | 6 +++--- tests/custom_managers/tests.py | 2 +- tests/forms_tests/tests/test_forms.py | 2 +- tests/model_formsets/tests.py | 2 +- tests/queries/tests.py | 2 +- tests/servers/tests.py | 2 +- tests/settings_tests/tests.py | 2 +- tests/template_tests/test_response.py | 2 +- 28 files changed, 31 insertions(+), 31 deletions(-) (limited to 'docs/ref/models') diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py index 4131494515..3a37de2404 100644 --- a/django/contrib/admin/filters.py +++ b/django/contrib/admin/filters.py @@ -87,7 +87,7 @@ class SimpleListFilter(ListFilter): def lookups(self, request, model_admin): """ - Must be overriden to return a list of tuples (value, verbose value) + Must be overridden to return a list of tuples (value, verbose value) """ raise NotImplementedError diff --git a/django/contrib/formtools/wizard/views.py b/django/contrib/formtools/wizard/views.py index c478f20854..c137f1aeaf 100644 --- a/django/contrib/formtools/wizard/views.py +++ b/django/contrib/formtools/wizard/views.py @@ -17,7 +17,7 @@ from django.contrib.formtools.wizard.forms import ManagementForm def normalize_name(name): """ - Converts camel-case style names into underscore seperated words. Example:: + Converts camel-case style names into underscore separated words. Example:: >>> normalize_name('oneTwoThree') 'one_two_three' diff --git a/django/contrib/gis/geoip/prototypes.py b/django/contrib/gis/geoip/prototypes.py index 283d721395..c13dd8aba2 100644 --- a/django/contrib/gis/geoip/prototypes.py +++ b/django/contrib/gis/geoip/prototypes.py @@ -14,7 +14,7 @@ class GeoIPRecord(Structure): ('longitude', c_float), # TODO: In 1.4.6 this changed from `int dma_code;` to # `union {int metro_code; int dma_code;};`. Change - # to a `ctypes.Union` in to accomodate in future when + # to a `ctypes.Union` in to accommodate in future when # pre-1.4.6 versions are no longer distributed. ('dma_code', c_int), ('area_code', c_int), diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index 8fd46aa759..0ed8e0a786 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -146,7 +146,7 @@ def call_command(name, *args, **options): # Grab out a list of defaults from the options. optparse does this for us # when the script runs from the command line, but since call_command can - # be called programatically, we need to simulate the loading and handling + # be called programmatically, we need to simulate the loading and handling # of defaults (see #10080 for details). defaults = {} for opt in klass.option_list: diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index c5eb1b9a9e..100c1872a7 100644 --- a/django/core/management/commands/dumpdata.py +++ b/django/core/management/commands/dumpdata.py @@ -22,7 +22,7 @@ class Command(BaseCommand): make_option('-a', '--all', action='store_true', dest='use_base_manager', default=False, help="Use Django's base manager to dump all models stored in the database, including those that would otherwise be filtered or modified by a custom manager."), make_option('--pks', dest='primary_keys', help="Only dump objects with " - "given primary keys. Accepts a comma seperated list of keys. " + "given primary keys. Accepts a comma separated list of keys. " "This option will only work when you specify one model."), ) help = ("Output the contents of the database as a fixture of the given " diff --git a/django/db/backends/oracle/compiler.py b/django/db/backends/oracle/compiler.py index d2d4cd3ac9..b6eb80e7ce 100644 --- a/django/db/backends/oracle/compiler.py +++ b/django/db/backends/oracle/compiler.py @@ -25,7 +25,7 @@ class SQLCompiler(compiler.SQLCompiler): def as_sql(self, with_limits=True, with_col_aliases=False): """ Creates the SQL for this query. Returns the SQL string and list - of parameters. This is overriden from the original Query class + of parameters. This is overridden from the original Query class to handle the additional SQL Oracle requires to emulate LIMIT and OFFSET. diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index dc7db2fceb..f396bf7408 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -335,7 +335,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): if 'check_same_thread' in kwargs and kwargs['check_same_thread']: warnings.warn( 'The `check_same_thread` option was provided and set to ' - 'True. It will be overriden with False. Use the ' + 'True. It will be overridden with False. Use the ' '`DatabaseWrapper.allow_thread_sharing` property instead ' 'for controlling thread shareability.', RuntimeWarning diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index ddc76fccf7..4f5707952e 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -90,7 +90,7 @@ class Field(object): 'already exists.'), } - # Generic field type description, usually overriden by subclasses + # Generic field type description, usually overridden by subclasses def _description(self): return _('Field of type: %(field_type)s') % { 'field_type': self.__class__.__name__ diff --git a/docs/howto/initial-data.txt b/docs/howto/initial-data.txt index cea07bfea3..b86aaa834e 100644 --- a/docs/howto/initial-data.txt +++ b/docs/howto/initial-data.txt @@ -142,7 +142,7 @@ database tables already will have been created. If you require data for a test case, you should add it using either a :ref:`test fixture `, or - programatically add it during the ``setUp()`` of your test case. + programmatically add it during the ``setUp()`` of your test case. Database-backend-specific SQL data ---------------------------------- diff --git a/docs/ref/contrib/admin/actions.txt b/docs/ref/contrib/admin/actions.txt index 65d096921f..58d8244525 100644 --- a/docs/ref/contrib/admin/actions.txt +++ b/docs/ref/contrib/admin/actions.txt @@ -271,7 +271,7 @@ Making actions available site-wide This makes the `export_selected_objects` action globally available as an action named `"export_selected_objects"`. You can explicitly give the action - a name -- good if you later want to programatically :ref:`remove the action + a name -- good if you later want to programmatically :ref:`remove the action ` -- by passing a second argument to :meth:`AdminSite.add_action()`:: diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt index 806d135deb..678ab32a05 100644 --- a/docs/ref/contrib/staticfiles.txt +++ b/docs/ref/contrib/staticfiles.txt @@ -226,7 +226,7 @@ CachedStaticFilesStorage would be replaced by calling the :meth:`~django.core.files.storage.Storage.url` - method of the ``CachedStaticFilesStorage`` storage backend, ultimatively + method of the ``CachedStaticFilesStorage`` storage backend, ultimately saving a ``'css/styles.55e7cbb9ba48.css'`` file with the following content: diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 31c6a0f660..565c779525 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -233,7 +233,7 @@ probably be using this flag. .. django-admin-option:: --pks By default, ``dumpdata`` will output all the records of the model, but -you can use the ``--pks`` option to specify a comma seperated list of +you can use the ``--pks`` option to specify a comma separated list of primary keys on which to filter. This is only available when dumping one model. @@ -743,7 +743,7 @@ You can provide an IPv6 address surrounded by brackets A hostname containing ASCII-only characters can also be used. If the :doc:`staticfiles` contrib app is enabled -(default in new projects) the :djadmin:`runserver` command will be overriden +(default in new projects) the :djadmin:`runserver` command will be overridden with its own :ref:`runserver` command. .. django-admin-option:: --noreload diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index f06866d9a1..1e7ad1ffdf 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -118,7 +118,7 @@ validation for your own manually created models. For example:: article.full_clean() except ValidationError as e: # Do something based on the errors contained in e.message_dict. - # Display them to a user, or handle them programatically. + # Display them to a user, or handle them programmatically. pass The first step ``full_clean()`` performs is to clean each individual field. diff --git a/docs/releases/1.2.1.txt b/docs/releases/1.2.1.txt index bdac39dd5d..0e193bfe34 100644 --- a/docs/releases/1.2.1.txt +++ b/docs/releases/1.2.1.txt @@ -4,7 +4,7 @@ Django 1.2.1 release notes Django 1.2.1 was released almost immediately after 1.2.0 to correct two small bugs: one was in the documentation packaging script, the other was a bug_ that -affected datetime form field widgets when localisation was enabled. +affected datetime form field widgets when localization was enabled. .. _bug: https://code.djangoproject.com/ticket/13560 diff --git a/docs/releases/1.5-beta-1.txt b/docs/releases/1.5-beta-1.txt index 69c591ac03..4495a281af 100644 --- a/docs/releases/1.5-beta-1.txt +++ b/docs/releases/1.5-beta-1.txt @@ -7,7 +7,7 @@ November 27, 2012. Welcome to Django 1.5 beta! This is the second in a series of preview/development releases leading -up to the eventual release of Django 1.5, scheduled for Decemeber +up to the eventual release of Django 1.5, scheduled for December 2012. This release is primarily targeted at developers who are interested in trying out new features and testing the Django codebase to help identify and resolve bugs prior to the final 1.5 release. diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt index df1a5505a5..f6a70e7acc 100644 --- a/docs/topics/class-based-views/mixins.txt +++ b/docs/topics/class-based-views/mixins.txt @@ -170,7 +170,7 @@ being taken from the :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix` attribute. (The date based generic views use suffixes such as ``_archive``, ``_archive_year`` and so on to use different templates for the various -specialised date-based list views.) +specialized date-based list views.) Using Django's class-based view mixins ====================================== diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt index 903579cc38..4411b326d7 100644 --- a/docs/topics/db/transactions.txt +++ b/docs/topics/db/transactions.txt @@ -619,7 +619,7 @@ context managers breaks atomicity. Managing autocommit ~~~~~~~~~~~~~~~~~~~ -Django 1.6 introduces an explicit :ref:`API for mananging autocommit +Django 1.6 introduces an explicit :ref:`API for managing autocommit `. To disable autocommit temporarily, instead of:: diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt index 772ee122d5..8538859dad 100644 --- a/docs/topics/http/sessions.txt +++ b/docs/topics/http/sessions.txt @@ -293,7 +293,7 @@ You can edit it multiple times. expiration (or those set to expire at browser close), this will equal the date :setting:`SESSION_COOKIE_AGE` seconds from now. - This function accepts the same keyword argumets as :meth:`get_expiry_age`. + This function accepts the same keyword arguments as :meth:`get_expiry_age`. .. method:: get_expire_at_browser_close diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 8a3f240307..65c1f83168 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -623,7 +623,7 @@ change the entry in the URLconf. In some scenarios where views are of a generic nature, a many-to-one relationship might exist between URLs and views. For these cases the view name -isn't a good enough identificator for it when it comes the time of reversing +isn't a good enough identifier for it when comes the time of reversing URLs. Read the next section to know about the solution Django provides for this. .. _naming-url-patterns: diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt index 2417274ab5..26d7521177 100644 --- a/docs/topics/testing/advanced.txt +++ b/docs/topics/testing/advanced.txt @@ -174,7 +174,7 @@ Advanced features of ``TransactionTestCase`` .. warning:: This attribute is a private API. It may be changed or removed without - a deprecation period in the future, for instance to accomodate changes + a deprecation period in the future, for instance to accommodate changes in application loading. It's used to optimize Django's own test suite, which contains hundreds diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index 0380d6931b..0673ed6e7a 100644 --- a/docs/topics/testing/overview.txt +++ b/docs/topics/testing/overview.txt @@ -1421,7 +1421,7 @@ The decorator can also be applied to test case classes:: You can also simulate the absence of a setting by deleting it after settings -have been overriden, like this:: +have been overridden, like this:: @override_settings() def test_something(self): @@ -1437,7 +1437,7 @@ callbacks to clean up and otherwise reset state when settings are changed. Django itself uses this signal to reset various data: ================================ ======================== -Overriden settings Data reset +Overridden settings Data reset ================================ ======================== USE_TZ, TIME_ZONE Databases timezone TEMPLATE_CONTEXT_PROCESSORS Context processors cache @@ -1639,7 +1639,7 @@ your test suite. .. versionadded:: 1.5 Asserts that the strings ``xml1`` and ``xml2`` are equal. The - comparison is based on XML semantics. Similarily to + comparison is based on XML semantics. Similarly to :meth:`~SimpleTestCase.assertHTMLEqual`, the comparison is made on parsed content, hence only semantic differences are considered, not syntax differences. When unvalid XML is passed in any parameter, an diff --git a/tests/custom_managers/tests.py b/tests/custom_managers/tests.py index 7fa58b2a88..5ab58b534a 100644 --- a/tests/custom_managers/tests.py +++ b/tests/custom_managers/tests.py @@ -36,7 +36,7 @@ class CustomManagerTests(TestCase): with self.assertRaises(AttributeError): manager.optout_public_method() - # Test that the overriden method is called. + # Test that the overridden method is called. queryset = manager.filter() self.assertQuerysetEqual(queryset, ["Bugs Bunny"], six.text_type) self.assertEqual(queryset._filter_CustomQuerySet, True) diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index c77181273c..89c19cc5a4 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -1824,7 +1824,7 @@ class FormsTestCase(TestCase): # passing just one argument: overrides the field's label (('custom',), {}, ''), - # the overriden label is escaped + # the overridden label is escaped (('custom&',), {}, ''), ((mark_safe('custom&'),), {}, ''), diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index f66b5abb63..3bb4f95ea5 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -391,7 +391,7 @@ class ModelFormsetTest(TestCase): def test_custom_queryset_init(self): """ - Test that a queryset can be overriden in the __init__ method. + Test that a queryset can be overridden in the __init__ method. https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset """ author1 = Author.objects.create(name='Charles Baudelaire') diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 514b0ee5d7..94c12c5ba9 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -789,7 +789,7 @@ class Queries1Tests(BaseQuerysetTest): ) def test_ticket7181(self): - # Ordering by related tables should accomodate nullable fields (this + # Ordering by related tables should accommodate nullable fields (this # test is a little tricky, since NULL ordering is database dependent. # Instead, we just count the number of results). self.assertEqual(len(Tag.objects.order_by('parent__name')), 5) diff --git a/tests/servers/tests.py b/tests/servers/tests.py index be74afe820..1f02a88d5a 100644 --- a/tests/servers/tests.py +++ b/tests/servers/tests.py @@ -113,7 +113,7 @@ class LiveServerAddress(LiveServerBase): def test_test_test(self): # Intentionally empty method so that the test is picked up by the - # test runner and the overriden setUpClass() method is executed. + # test runner and the overridden setUpClass() method is executed. pass class LiveServerViews(LiveServerBase): diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index 87ed4d4f5e..3673031447 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -163,7 +163,7 @@ class SettingsTests(TestCase): def test_override_settings_delete(self): """ - Allow deletion of a setting in an overriden settings set (#18824) + Allow deletion of a setting in an overridden settings set (#18824) """ previous_i18n = settings.USE_I18N with self.settings(USE_I18N=False): diff --git a/tests/template_tests/test_response.py b/tests/template_tests/test_response.py index 6acc45626f..65f7531361 100644 --- a/tests/template_tests/test_response.py +++ b/tests/template_tests/test_response.py @@ -95,7 +95,7 @@ class SimpleTemplateResponseTest(TestCase): self.assertEqual(response.content, b'foo') def test_set_content(self): - # content can be overriden + # content can be overridden response = self._response() self.assertFalse(response.is_rendered) response.content = 'spam' -- cgit v1.3 From acd1d439fd9b3e77bc0291dcd62c09f345d8622c Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Mon, 29 Jul 2013 16:47:49 +0700 Subject: Fixed #20826 -- Moved Manager.raw() and Manager._insert() to the QuerySet class. --- django/db/models/manager.py | 8 +------- django/db/models/query.py | 37 ++++++++++++++++++++++++------------- docs/ref/models/querysets.txt | 22 ++++++++++++++++++++++ tests/basic/tests.py | 2 ++ 4 files changed, 49 insertions(+), 20 deletions(-) (limited to 'docs/ref/models') diff --git a/django/db/models/manager.py b/django/db/models/manager.py index f57944ebbc..4f16b5ebfe 100644 --- a/django/db/models/manager.py +++ b/django/db/models/manager.py @@ -2,7 +2,7 @@ import copy import inspect from django.db import router -from django.db.models.query import QuerySet, insert_query, RawQuerySet +from django.db.models.query import QuerySet from django.db.models import signals from django.db.models.fields import FieldDoesNotExist from django.utils import six @@ -169,12 +169,6 @@ class BaseManager(six.with_metaclass(RenameManagerMethods)): # understanding of how this comes into play. return self.get_queryset() - def _insert(self, objs, fields, **kwargs): - return insert_query(self.model, objs, fields, **kwargs) - - def raw(self, raw_query, params=None, *args, **kwargs): - return RawQuerySet(raw_query=raw_query, model=self.model, params=params, using=self._db, *args, **kwargs) - Manager = BaseManager.from_queryset(QuerySet, class_name='Manager') diff --git a/django/db/models/query.py b/django/db/models/query.py index 63861bb71b..a3e0a9540f 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -413,8 +413,8 @@ class QuerySet(object): specifying whether an object was created. """ lookup, params, _ = self._extract_model_params(defaults, **kwargs) + self._for_write = True try: - self._for_write = True return self.get(**lookup), False except self.model.DoesNotExist: return self._create_object_from_params(lookup, params) @@ -427,8 +427,8 @@ class QuerySet(object): specifying whether an object was created. """ lookup, params, filtered_defaults = self._extract_model_params(defaults, **kwargs) + self._for_write = True try: - self._for_write = True obj = self.get(**lookup) except self.model.DoesNotExist: obj, created = self._create_object_from_params(lookup, params) @@ -623,6 +623,13 @@ class QuerySet(object): # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # ################################################## + def raw(self, raw_query, params=None, translations=None, using=None): + if using is None: + using = self.db + return RawQuerySet(raw_query, model=self.model, + params=params, translations=translations, + using=using) + def values(self, *fields): return self._clone(klass=ValuesQuerySet, setup=True, _fields=fields) @@ -909,6 +916,21 @@ class QuerySet(object): ################### # PRIVATE METHODS # ################### + + def _insert(self, objs, fields, return_id=False, raw=False, using=None): + """ + Inserts a new record for the given model. This provides an interface to + the InsertQuery class and is how Model.save() is implemented. + """ + self._for_write = True + if using is None: + using = self.db + query = sql.InsertQuery(self.model) + query.insert_values(fields, objs, raw=raw) + return query.get_compiler(using=using).execute_sql(return_id) + _insert.alters_data = True + _insert.queryset_only = False + def _batched_insert(self, objs, fields, batch_size): """ A little helper method for bulk_insert to insert the bulk one batch @@ -1601,17 +1623,6 @@ class RawQuerySet(object): return self._model_fields -def insert_query(model, objs, fields, return_id=False, raw=False, using=None): - """ - Inserts a new record for the given model. This provides an interface to - the InsertQuery class and is how Model.save() is implemented. It is not - part of the public API. - """ - query = sql.InsertQuery(model) - query.insert_values(fields, objs, raw=raw) - return query.get_compiler(using=using).execute_sql(return_id) - - def prefetch_related_objects(result_cache, related_lookups): """ Helper function for prefetch_related functionality diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index c3f6a660b4..95dad202fa 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -1262,6 +1262,28 @@ unexpectedly blocking. Using ``select_for_update`` on backends which do not support ``SELECT ... FOR UPDATE`` (such as SQLite) will have no effect. +raw +~~~ + +.. method:: raw(raw_query, params=None, translations=None) + +.. versionchanged:: 1.7 + + ``raw`` was moved to the ``QuerySet`` class. It was previously only on + :class:`~django.db.models.Manager`. + +Takes a raw SQL query, executes it, and returns a +``django.db.models.query.RawQuerySet`` instance. This ``RawQuerySet`` instance +can be iterated over just like an normal QuerySet to provide object instances. + +See the :ref:`executing-raw-queries` for more information. + +.. warning:: + + ``raw()`` always triggers a new query and doesn't account for previous + filtering. As such, it should generally be called from the ``Manager`` or + from a fresh ``QuerySet`` instance. + Methods that do not return QuerySets ------------------------------------ diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 879c7869fa..55ed6a436f 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -773,7 +773,9 @@ class ManagerTest(TestCase): 'only', 'using', 'exists', + '_insert', '_update', + 'raw', ] def test_manager_methods(self): -- cgit v1.3 From 1123f4551158b7fc65d3bd88c375a4517dcd0720 Mon Sep 17 00:00:00 2001 From: Alex Couper Date: Sat, 20 Jul 2013 21:49:33 +0000 Subject: Fixed #20649 -- Allowed blank field display to be defined in the initial list of choices. --- AUTHORS | 1 + django/db/models/fields/__init__.py | 9 +++- django/forms/widgets.py | 2 + docs/ref/models/fields.txt | 19 ++++++-- docs/releases/1.7.txt | 5 ++ tests/forms_tests/models.py | 23 +++++++++ tests/forms_tests/tests/tests.py | 97 ++++++++++++++++++++++++++++++++++++- tests/model_fields/tests.py | 4 ++ 8 files changed, 152 insertions(+), 8 deletions(-) (limited to 'docs/ref/models') diff --git a/AUTHORS b/AUTHORS index a7f12d6e48..ade95df06a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -161,6 +161,7 @@ answer newbie questions, and generally made Django that much better: Paul Collier Paul Collins Robert Coup + Alex Couper Deric Crago Brian Fabian Crain David Cramer diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index d0a2defc48..fb4cbbb11a 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -544,7 +544,14 @@ class Field(object): def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): """Returns choices with a default blank choices included, for use as SelectField choices for this field.""" - first_choice = blank_choice if include_blank else [] + blank_defined = False + for choice, _ in self.choices: + if choice in ('', None): + blank_defined = True + break + + first_choice = (blank_choice if include_blank and + not blank_defined else []) if self.choices: return first_choice + list(self.choices) rel_model = self.rel.to diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 8a351bb637..784ccda159 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -511,6 +511,8 @@ class Select(Widget): return mark_safe('\n'.join(output)) def render_option(self, selected_choices, option_value, option_label): + if option_value == None: + option_value = '' option_value = force_text(option_value) if option_value in selected_choices: selected_html = mark_safe(' selected="selected"') diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 869996643f..736a836924 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -152,11 +152,20 @@ method to retrieve the human-readable name for the field's current value. See :meth:`~django.db.models.Model.get_FOO_display` in the database API documentation. -Finally, note that choices can be any iterable object -- not necessarily a list -or tuple. This lets you construct choices dynamically. But if you find yourself -hacking :attr:`~Field.choices` to be dynamic, you're probably better off using a -proper database table with a :class:`ForeignKey`. :attr:`~Field.choices` is -meant for static data that doesn't change much, if ever. +Note that choices can be any iterable object -- not necessarily a list or tuple. +This lets you construct choices dynamically. But if you find yourself hacking +:attr:`~Field.choices` to be dynamic, you're probably better off using a proper +database table with a :class:`ForeignKey`. :attr:`~Field.choices` is meant for +static data that doesn't change much, if ever. + +.. versionadded:: 1.7 + +Unless :attr:`blank=False` is set on the field along with a +:attr:`~Field.default` then a label containing ``"---------"`` will be rendered +with the select box. To override this behavior, add a tuple to ``choices`` +containing ``None``; e.g. ``(None, 'Your String For Display')``. +Alternatively, you can use an empty string instead of ``None`` where this makes +sense - such as on a :class:`~django.db.models.CharField`. ``db_column`` ------------- diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index b75fd89f4c..c47a392dff 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -105,6 +105,11 @@ Minor features ` method to more easily customize the login policy. +* :attr:`Field.choices` now allows you to + customize the "empty choice" label by including a tuple with an empty string + or ``None`` for the key and the custom label as the value. The default blank + option ``"----------"`` will be omitted in this case. + Backwards incompatible changes in 1.7 ===================================== diff --git a/tests/forms_tests/models.py b/tests/forms_tests/models.py index bec31d12d7..33898ffbb8 100644 --- a/tests/forms_tests/models.py +++ b/tests/forms_tests/models.py @@ -34,7 +34,30 @@ class Defaults(models.Model): class ChoiceModel(models.Model): """For ModelChoiceField and ModelMultipleChoiceField tests.""" + CHOICES = [ + ('', 'No Preference'), + ('f', 'Foo'), + ('b', 'Bar'), + ] + + INTEGER_CHOICES = [ + (None, 'No Preference'), + (1, 'Foo'), + (2, 'Bar'), + ] + + STRING_CHOICES_WITH_NONE = [ + (None, 'No Preference'), + ('f', 'Foo'), + ('b', 'Bar'), + ] + name = models.CharField(max_length=10) + choice = models.CharField(max_length=2, blank=True, choices=CHOICES) + choice_string_w_none = models.CharField( + max_length=2, blank=True, null=True, choices=STRING_CHOICES_WITH_NONE) + choice_integer = models.IntegerField(choices=INTEGER_CHOICES, blank=True, + null=True) @python_2_unicode_compatible diff --git a/tests/forms_tests/tests/tests.py b/tests/forms_tests/tests/tests.py index 4c391646e7..618ab8e07c 100644 --- a/tests/forms_tests/tests/tests.py +++ b/tests/forms_tests/tests/tests.py @@ -10,8 +10,8 @@ from django.forms.models import ModelFormMetaclass from django.test import TestCase from django.utils import six -from ..models import (ChoiceOptionModel, ChoiceFieldModel, FileModel, Group, - BoundaryModel, Defaults, OptionalMultiChoiceModel) +from ..models import (ChoiceModel, ChoiceOptionModel, ChoiceFieldModel, + FileModel, Group, BoundaryModel, Defaults, OptionalMultiChoiceModel) class ChoiceFieldForm(ModelForm): @@ -34,6 +34,24 @@ class ChoiceFieldExclusionForm(ModelForm): model = ChoiceFieldModel +class EmptyCharLabelChoiceForm(ModelForm): + class Meta: + model = ChoiceModel + fields = ['name', 'choice'] + + +class EmptyIntegerLabelChoiceForm(ModelForm): + class Meta: + model = ChoiceModel + fields = ['name', 'choice_integer'] + + +class EmptyCharLabelNoneChoiceForm(ModelForm): + class Meta: + model = ChoiceModel + fields = ['name', 'choice_string_w_none'] + + class FileForm(Form): file1 = FileField() @@ -259,3 +277,78 @@ class ManyToManyExclusionTestCase(TestCase): self.assertEqual(form.instance.choice_int.pk, data['choice_int']) self.assertEqual(list(form.instance.multi_choice.all()), [opt2, opt3]) self.assertEqual([obj.pk for obj in form.instance.multi_choice_int.all()], data['multi_choice_int']) + + +class EmptyLabelTestCase(TestCase): + def test_empty_field_char(self): + f = EmptyCharLabelChoiceForm() + self.assertHTMLEqual(f.as_p(), + """

+

""") + + def test_empty_field_char_none(self): + f = EmptyCharLabelNoneChoiceForm() + self.assertHTMLEqual(f.as_p(), + """

+

""") + + def test_save_empty_label_forms(self): + # Test that saving a form with a blank choice results in the expected + # value being stored in the database. + tests = [ + (EmptyCharLabelNoneChoiceForm, 'choice_string_w_none', None), + (EmptyIntegerLabelChoiceForm, 'choice_integer', None), + (EmptyCharLabelChoiceForm, 'choice', ''), + ] + + for form, key, expected in tests: + f = form({'name': 'some-key', key: ''}) + self.assertTrue(f.is_valid()) + m = f.save() + self.assertEqual(expected, getattr(m, key)) + self.assertEqual('No Preference', + getattr(m, 'get_{0}_display'.format(key))()) + + def test_empty_field_integer(self): + f = EmptyIntegerLabelChoiceForm() + self.assertHTMLEqual(f.as_p(), + """

+

""") + + def test_get_display_value_on_none(self): + m = ChoiceModel.objects.create(name='test', choice='', choice_integer=None) + self.assertEqual(None, m.choice_integer) + self.assertEqual('No Preference', m.get_choice_integer_display()) + + def test_html_rendering_of_prepopulated_models(self): + none_model = ChoiceModel(name='none-test', choice_integer=None) + f = EmptyIntegerLabelChoiceForm(instance=none_model) + self.assertHTMLEqual(f.as_p(), + """

+

""") + + foo_model = ChoiceModel(name='foo-test', choice_integer=1) + f = EmptyIntegerLabelChoiceForm(instance=foo_model) + self.assertHTMLEqual(f.as_p(), + """

+

""") diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index 04a17df947..378e6280cc 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -331,6 +331,10 @@ class ValidationTest(test.TestCase): f = models.CharField(choices=[('a', 'A'), ('b', 'B')]) self.assertRaises(ValidationError, f.clean, "not a", None) + def test_charfield_get_choices_with_blank_defined(self): + f = models.CharField(choices=[('', '<><>'), ('a', 'A')]) + self.assertEqual(f.get_choices(True), [('', '<><>'), ('a', 'A')]) + def test_choices_validation_supports_named_groups(self): f = models.IntegerField( choices=(('group', ((10, 'A'), (20, 'B'))), (30, 'C'))) -- cgit v1.3 From 5df84b268de45fffd90930e6442eb511cba71f5f Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 1 Aug 2013 10:27:30 -0400 Subject: Removed unused model option "admin" --- django/db/models/options.py | 1 - docs/ref/models/options.txt | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'docs/ref/models') diff --git a/django/db/models/options.py b/django/db/models/options.py index dda15770e3..d072d67fb3 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -41,7 +41,6 @@ class Options(object): self.get_latest_by = None self.order_with_respect_to = None self.db_tablespace = settings.DEFAULT_TABLESPACE - self.admin = None self.meta = meta self.pk = None self.has_auto_field, self.auto_field = False, None diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt index d54af37e86..41baa8c106 100644 --- a/docs/ref/models/options.txt +++ b/docs/ref/models/options.txt @@ -227,9 +227,8 @@ Django quotes column and table names behind the scenes. .. attribute:: Options.permissions Extra permissions to enter into the permissions table when creating this object. - Add, delete and change permissions are automatically created for each object - that has ``admin`` set. This example specifies an extra permission, - ``can_deliver_pizzas``:: + Add, delete and change permissions are automatically created for each + model. This example specifies an extra permission, ``can_deliver_pizzas``:: permissions = (("can_deliver_pizzas", "Can deliver pizzas"),) -- cgit v1.3 From 07876cf02b6db453ca0397c29c225668872fa96d Mon Sep 17 00:00:00 2001 From: Curtis Maloney Date: Sat, 3 Aug 2013 15:41:15 +1000 Subject: Deprecated SortedDict (replaced with collections.OrderedDict) Thanks Loic Bistuer for the review. --- django/contrib/admin/options.py | 8 ++--- django/contrib/admin/views/main.py | 6 ++-- django/contrib/auth/forms.py | 5 +-- django/contrib/auth/hashers.py | 16 ++++----- django/contrib/formtools/wizard/views.py | 20 +++++++----- django/contrib/staticfiles/finders.py | 9 ++--- .../management/commands/collectstatic.py | 4 +-- django/contrib/staticfiles/storage.py | 6 ++-- django/core/management/commands/dumpdata.py | 9 ++--- django/core/management/commands/inspectdb.py | 6 ++-- django/core/management/commands/syncdb.py | 4 +-- django/db/models/deletion.py | 4 +-- django/db/models/loading.py | 21 ++++++++---- django/db/models/options.py | 10 +++--- django/db/models/sql/query.py | 20 ++++++------ django/forms/forms.py | 4 +-- django/forms/models.py | 8 ++--- django/middleware/locale.py | 5 +-- django/utils/datastructures.py | 6 +++- django/utils/translation/trans_real.py | 8 ++--- docs/internals/deprecation.txt | 3 ++ docs/ref/models/querysets.txt | 7 ++-- docs/ref/utils.txt | 4 +++ docs/releases/1.7.txt | 7 ++++ tests/extra_regress/tests.py | 38 +++++++++++----------- tests/queries/tests.py | 8 ++--- 26 files changed, 139 insertions(+), 107 deletions(-) (limited to 'docs/ref/models') diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 94e6548650..8a6b65358e 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -1,3 +1,4 @@ +from collections import OrderedDict import copy import operator from functools import partial, reduce, update_wrapper @@ -29,7 +30,6 @@ from django.http.response import HttpResponseBase from django.shortcuts import get_object_or_404 from django.template.response import SimpleTemplateResponse, TemplateResponse from django.utils.decorators import method_decorator -from django.utils.datastructures import SortedDict from django.utils.html import escape, escapejs from django.utils.safestring import mark_safe from django.utils import six @@ -672,7 +672,7 @@ class ModelAdmin(BaseModelAdmin): # want *any* actions enabled on this page. from django.contrib.admin.views.main import _is_changelist_popup if self.actions is None or _is_changelist_popup(request): - return SortedDict() + return OrderedDict() actions = [] @@ -693,8 +693,8 @@ class ModelAdmin(BaseModelAdmin): # get_action might have returned None, so filter any of those out. actions = filter(None, actions) - # Convert the actions into a SortedDict keyed by name. - actions = SortedDict([ + # Convert the actions into an OrderedDict keyed by name. + actions = OrderedDict([ (name, (func, name, desc)) for func, name, desc in actions ]) diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index f766031bf8..3325747a9f 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -1,3 +1,4 @@ +from collections import OrderedDict import sys import warnings @@ -7,7 +8,6 @@ from django.core.urlresolvers import reverse from django.db import models from django.db.models.fields import FieldDoesNotExist from django.utils import six -from django.utils.datastructures import SortedDict from django.utils.deprecation import RenameMethodsBase from django.utils.encoding import force_str, force_text from django.utils.translation import ugettext, ugettext_lazy @@ -319,13 +319,13 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)): def get_ordering_field_columns(self): """ - Returns a SortedDict of ordering field column numbers and asc/desc + Returns an OrderedDict of ordering field column numbers and asc/desc """ # We must cope with more than one column having the same underlying sort # field, so we base things on column numbers. ordering = self._get_default_ordering() - ordering_fields = SortedDict() + ordering_fields = OrderedDict() if ORDER_VAR not in self.params: # for ordering specified on ModelAdmin or model Meta, we don't know # the right column numbers absolutely, because there might be more diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index e3f03cc536..c5c2db456e 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -1,9 +1,10 @@ from __future__ import unicode_literals +from collections import OrderedDict + from django import forms from django.forms.util import flatatt from django.template import loader -from django.utils.datastructures import SortedDict from django.utils.encoding import force_bytes from django.utils.html import format_html, format_html_join from django.utils.http import urlsafe_base64_encode @@ -324,7 +325,7 @@ class PasswordChangeForm(SetPasswordForm): ) return old_password -PasswordChangeForm.base_fields = SortedDict([ +PasswordChangeForm.base_fields = OrderedDict([ (k, PasswordChangeForm.base_fields[k]) for k in ['old_password', 'new_password1', 'new_password2'] ]) diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py index 45ebc0f980..06aec64636 100644 --- a/django/contrib/auth/hashers.py +++ b/django/contrib/auth/hashers.py @@ -2,13 +2,13 @@ from __future__ import unicode_literals import base64 import binascii +from collections import OrderedDict import hashlib import importlib from django.dispatch import receiver from django.conf import settings from django.test.signals import setting_changed -from django.utils.datastructures import SortedDict from django.utils.encoding import force_bytes, force_str, force_text from django.core.exceptions import ImproperlyConfigured from django.utils.crypto import ( @@ -243,7 +243,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher): def safe_summary(self, encoded): algorithm, iterations, salt, hash = encoded.split('$', 3) assert algorithm == self.algorithm - return SortedDict([ + return OrderedDict([ (_('algorithm'), algorithm), (_('iterations'), iterations), (_('salt'), mask_hash(salt)), @@ -320,7 +320,7 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher): algorithm, empty, algostr, work_factor, data = encoded.split('$', 4) assert algorithm == self.algorithm salt, checksum = data[:22], data[22:] - return SortedDict([ + return OrderedDict([ (_('algorithm'), algorithm), (_('work factor'), work_factor), (_('salt'), mask_hash(salt)), @@ -368,7 +368,7 @@ class SHA1PasswordHasher(BasePasswordHasher): def safe_summary(self, encoded): algorithm, salt, hash = encoded.split('$', 2) assert algorithm == self.algorithm - return SortedDict([ + return OrderedDict([ (_('algorithm'), algorithm), (_('salt'), mask_hash(salt, show=2)), (_('hash'), mask_hash(hash)), @@ -396,7 +396,7 @@ class MD5PasswordHasher(BasePasswordHasher): def safe_summary(self, encoded): algorithm, salt, hash = encoded.split('$', 2) assert algorithm == self.algorithm - return SortedDict([ + return OrderedDict([ (_('algorithm'), algorithm), (_('salt'), mask_hash(salt, show=2)), (_('hash'), mask_hash(hash)), @@ -429,7 +429,7 @@ class UnsaltedSHA1PasswordHasher(BasePasswordHasher): def safe_summary(self, encoded): assert encoded.startswith('sha1$$') hash = encoded[6:] - return SortedDict([ + return OrderedDict([ (_('algorithm'), self.algorithm), (_('hash'), mask_hash(hash)), ]) @@ -462,7 +462,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher): return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): - return SortedDict([ + return OrderedDict([ (_('algorithm'), self.algorithm), (_('hash'), mask_hash(encoded, show=3)), ]) @@ -496,7 +496,7 @@ class CryptPasswordHasher(BasePasswordHasher): def safe_summary(self, encoded): algorithm, salt, data = encoded.split('$', 2) assert algorithm == self.algorithm - return SortedDict([ + return OrderedDict([ (_('algorithm'), algorithm), (_('salt'), salt), (_('hash'), mask_hash(data, show=3)), diff --git a/django/contrib/formtools/wizard/views.py b/django/contrib/formtools/wizard/views.py index c137f1aeaf..9c81f77ed2 100644 --- a/django/contrib/formtools/wizard/views.py +++ b/django/contrib/formtools/wizard/views.py @@ -1,3 +1,4 @@ +from collections import OrderedDict import re from django import forms @@ -5,7 +6,6 @@ from django.shortcuts import redirect from django.core.urlresolvers import reverse from django.forms import formsets, ValidationError from django.views.generic import TemplateView -from django.utils.datastructures import SortedDict from django.utils.decorators import classonlymethod from django.utils.translation import ugettext as _ from django.utils import six @@ -158,7 +158,7 @@ class WizardView(TemplateView): form_list = form_list or kwargs.pop('form_list', getattr(cls, 'form_list', None)) or [] - computed_form_list = SortedDict() + computed_form_list = OrderedDict() assert len(form_list) > 0, 'at least one form is needed' @@ -206,7 +206,7 @@ class WizardView(TemplateView): The form_list is always generated on the fly because condition methods could use data from other (maybe previous forms). """ - form_list = SortedDict() + form_list = OrderedDict() for form_key, form_class in six.iteritems(self.form_list): # try to fetch the value from condition list, by default, the form # gets passed to the new list. @@ -498,9 +498,10 @@ class WizardView(TemplateView): if step is None: step = self.steps.current form_list = self.get_form_list() - key = form_list.keyOrder.index(step) + 1 - if len(form_list.keyOrder) > key: - return form_list.keyOrder[key] + keys = list(form_list.keys()) + key = keys.index(step) + 1 + if len(keys) > key: + return keys[key] return None def get_prev_step(self, step=None): @@ -512,9 +513,10 @@ class WizardView(TemplateView): if step is None: step = self.steps.current form_list = self.get_form_list() - key = form_list.keyOrder.index(step) - 1 + keys = list(form_list.keys()) + key = keys.index(step) - 1 if key >= 0: - return form_list.keyOrder[key] + return keys[key] return None def get_step_index(self, step=None): @@ -524,7 +526,7 @@ class WizardView(TemplateView): """ if step is None: step = self.steps.current - return self.get_form_list().keyOrder.index(step) + return list(self.get_form_list().keys()).index(step) def get_context_data(self, form, **kwargs): """ diff --git a/django/contrib/staticfiles/finders.py b/django/contrib/staticfiles/finders.py index 7d266d95a0..d4efd1a8d8 100644 --- a/django/contrib/staticfiles/finders.py +++ b/django/contrib/staticfiles/finders.py @@ -1,8 +1,9 @@ +from collections import OrderedDict import os + from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.files.storage import default_storage, Storage, FileSystemStorage -from django.utils.datastructures import SortedDict from django.utils.functional import empty, memoize, LazyObject from django.utils.module_loading import import_by_path from django.utils._os import safe_join @@ -11,7 +12,7 @@ from django.utils import six from django.contrib.staticfiles import utils from django.contrib.staticfiles.storage import AppStaticStorage -_finders = SortedDict() +_finders = OrderedDict() class BaseFinder(object): @@ -47,7 +48,7 @@ class FileSystemFinder(BaseFinder): # List of locations with static files self.locations = [] # Maps dir paths to an appropriate storage instance - self.storages = SortedDict() + self.storages = OrderedDict() if not isinstance(settings.STATICFILES_DIRS, (list, tuple)): raise ImproperlyConfigured( "Your STATICFILES_DIRS setting is not a tuple or list; " @@ -118,7 +119,7 @@ class AppDirectoriesFinder(BaseFinder): # The list of apps that are handled self.apps = [] # Mapping of app module paths to storage instances - self.storages = SortedDict() + self.storages = OrderedDict() if apps is None: apps = settings.INSTALLED_APPS for app in apps: diff --git a/django/contrib/staticfiles/management/commands/collectstatic.py b/django/contrib/staticfiles/management/commands/collectstatic.py index 7c3de80e93..c1e9fa811b 100644 --- a/django/contrib/staticfiles/management/commands/collectstatic.py +++ b/django/contrib/staticfiles/management/commands/collectstatic.py @@ -2,12 +2,12 @@ from __future__ import unicode_literals import os import sys +from collections import OrderedDict from optparse import make_option from django.core.files.storage import FileSystemStorage from django.core.management.base import CommandError, NoArgsCommand from django.utils.encoding import smart_text -from django.utils.datastructures import SortedDict from django.utils.six.moves import input from django.contrib.staticfiles import finders, storage @@ -97,7 +97,7 @@ class Command(NoArgsCommand): else: handler = self.copy_file - found_files = SortedDict() + found_files = OrderedDict() for finder in finders.get_finders(): for path, storage in finder.list(self.ignore_patterns): # Prefix the relative path if the source storage contains it diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index f6d0a37a11..1242afe411 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals +from collections import OrderedDict import hashlib from importlib import import_module import os @@ -16,7 +17,6 @@ from django.core.cache import (get_cache, InvalidCacheBackendError, from django.core.exceptions import ImproperlyConfigured from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage, get_storage_class -from django.utils.datastructures import SortedDict from django.utils.encoding import force_bytes, force_text from django.utils.functional import LazyObject from django.utils._os import upath @@ -64,7 +64,7 @@ class CachedFilesMixin(object): except InvalidCacheBackendError: # Use the default backend self.cache = default_cache - self._patterns = SortedDict() + self._patterns = OrderedDict() for extension, patterns in self.patterns: for pattern in patterns: if isinstance(pattern, (tuple, list)): @@ -202,7 +202,7 @@ class CachedFilesMixin(object): def post_process(self, paths, dry_run=False, **options): """ - Post process the given SortedDict of files (called from collectstatic). + Post process the given OrderedDict of files (called from collectstatic). Processing is actually two separate operations: diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index 100c1872a7..fd9418a728 100644 --- a/django/core/management/commands/dumpdata.py +++ b/django/core/management/commands/dumpdata.py @@ -1,10 +1,11 @@ +from collections import OrderedDict +from optparse import make_option + from django.core.exceptions import ImproperlyConfigured from django.core.management.base import BaseCommand, CommandError from django.core import serializers from django.db import router, DEFAULT_DB_ALIAS -from django.utils.datastructures import SortedDict -from optparse import make_option class Command(BaseCommand): option_list = BaseCommand.option_list + ( @@ -66,11 +67,11 @@ class Command(BaseCommand): if len(app_labels) == 0: if primary_keys: raise CommandError("You can only use --pks option with one model") - app_list = SortedDict((app, None) for app in get_apps() if app not in excluded_apps) + app_list = OrderedDict((app, None) for app in get_apps() if app not in excluded_apps) else: if len(app_labels) > 1 and primary_keys: raise CommandError("You can only use --pks option with one model") - app_list = SortedDict() + app_list = OrderedDict() for label in app_labels: try: app_label, model_label = label.split('.') diff --git a/django/core/management/commands/inspectdb.py b/django/core/management/commands/inspectdb.py index dc26bb11d2..2cfea028ec 100644 --- a/django/core/management/commands/inspectdb.py +++ b/django/core/management/commands/inspectdb.py @@ -1,12 +1,12 @@ from __future__ import unicode_literals +from collections import OrderedDict import keyword import re from optparse import make_option from django.core.management.base import NoArgsCommand, CommandError from django.db import connections, DEFAULT_DB_ALIAS -from django.utils.datastructures import SortedDict class Command(NoArgsCommand): @@ -69,7 +69,7 @@ class Command(NoArgsCommand): used_column_names = [] # Holds column names used in the table so far for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)): comment_notes = [] # Holds Field notes, to be displayed in a Python comment. - extra_params = SortedDict() # Holds Field parameters such as 'db_column'. + extra_params = OrderedDict() # Holds Field parameters such as 'db_column'. column_name = row[0] is_relation = i in relations @@ -193,7 +193,7 @@ class Command(NoArgsCommand): description, this routine will return the given field type name, as well as any additional keyword parameters and notes for the field. """ - field_params = SortedDict() + field_params = OrderedDict() field_notes = [] try: diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py index 7c8d8b5d8f..d51699e95a 100644 --- a/django/core/management/commands/syncdb.py +++ b/django/core/management/commands/syncdb.py @@ -1,3 +1,4 @@ +from collections import OrderedDict from importlib import import_module from optparse import make_option import itertools @@ -9,7 +10,6 @@ from django.core.management.base import NoArgsCommand from django.core.management.color import no_style from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal, emit_pre_sync_signal from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS -from django.utils.datastructures import SortedDict class Command(NoArgsCommand): @@ -76,7 +76,7 @@ class Command(NoArgsCommand): return not ((converter(opts.db_table) in tables) or (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)) - manifest = SortedDict( + manifest = OrderedDict( (app_name, list(filter(model_installed, model_list))) for app_name, model_list in all_models ) diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py index e0bfb9d879..f4c64f7b7d 100644 --- a/django/db/models/deletion.py +++ b/django/db/models/deletion.py @@ -1,8 +1,8 @@ +from collections import OrderedDict from operator import attrgetter from django.db import connections, transaction, IntegrityError from django.db.models import signals, sql -from django.utils.datastructures import SortedDict from django.utils import six @@ -234,7 +234,7 @@ class Collector(object): found = True if not found: return - self.data = SortedDict([(model, self.data[model]) + self.data = OrderedDict([(model, self.data[model]) for model in sorted_models]) def delete(self): diff --git a/django/db/models/loading.py b/django/db/models/loading.py index 83682035d2..f858f25c1d 100644 --- a/django/db/models/loading.py +++ b/django/db/models/loading.py @@ -1,5 +1,7 @@ "Utilities for loading models and the modules that contain them." +from collections import OrderedDict +import copy import imp from importlib import import_module import os @@ -7,7 +9,6 @@ import sys from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.utils.datastructures import SortedDict from django.utils.module_loading import module_has_submodule from django.utils._os import upath from django.utils import six @@ -17,6 +18,14 @@ __all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models', MODELS_MODULE_NAME = 'models' +class ModelDict(OrderedDict): + """ + We need to special-case the deepcopy for this, as the keys are modules, + which can't be deep copied. + """ + def __deepcopy__(self, memo): + return self.__class__([(key, copy.deepcopy(value, memo)) + for key, value in self.items()]) class UnavailableApp(Exception): pass @@ -31,14 +40,14 @@ class AppCache(object): # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531. __shared_state = dict( # Keys of app_store are the model modules for each application. - app_store=SortedDict(), + app_store=ModelDict(), # Mapping of installed app_labels to model modules for that app. app_labels={}, # Mapping of app_labels to a dictionary of model names to model code. # May contain apps that are not installed. - app_models=SortedDict(), + app_models=ModelDict(), # Mapping of app_labels to errors raised when trying to import the app. app_errors={}, @@ -244,12 +253,12 @@ class AppCache(object): if app_mod: if app_mod in self.app_store: app_list = [self.app_models.get(self._label_for(app_mod), - SortedDict())] + ModelDict())] else: app_list = [] else: if only_installed: - app_list = [self.app_models.get(app_label, SortedDict()) + app_list = [self.app_models.get(app_label, ModelDict()) for app_label in six.iterkeys(self.app_labels)] else: app_list = six.itervalues(self.app_models) @@ -298,7 +307,7 @@ class AppCache(object): # Store as 'name: model' pair in a dictionary # in the app_models dictionary model_name = model._meta.model_name - model_dict = self.app_models.setdefault(app_label, SortedDict()) + model_dict = self.app_models.setdefault(app_label, ModelDict()) if model_name in model_dict: # The same model may be imported via different paths (e.g. # appname.models and project.appname.models). We use the source diff --git a/django/db/models/options.py b/django/db/models/options.py index d072d67fb3..95cefec1bf 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from collections import OrderedDict import re from bisect import bisect import warnings @@ -11,7 +12,6 @@ from django.db.models.fields.proxy import OrderWrt from django.db.models.loading import get_models, app_cache_ready from django.utils import six from django.utils.functional import cached_property -from django.utils.datastructures import SortedDict from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible from django.utils.translation import activate, deactivate_all, get_language, string_concat @@ -58,7 +58,7 @@ class Options(object): # concrete models, the concrete_model is always the class itself. self.concrete_model = None self.swappable = None - self.parents = SortedDict() + self.parents = OrderedDict() self.auto_created = False # To handle various inheritance situations, we need to track where @@ -332,7 +332,7 @@ class Options(object): return list(six.iteritems(self._m2m_cache)) def _fill_m2m_cache(self): - cache = SortedDict() + cache = OrderedDict() for parent in self.parents: for field, model in parent._meta.get_m2m_with_model(): if model: @@ -474,7 +474,7 @@ class Options(object): return [t for t in cache.items() if all(p(*t) for p in predicates)] def _fill_related_objects_cache(self): - cache = SortedDict() + cache = OrderedDict() parent_list = self.get_parent_list() for parent in self.parents: for obj, model in parent._meta.get_all_related_objects_with_model(include_hidden=True): @@ -519,7 +519,7 @@ class Options(object): return list(six.iteritems(cache)) def _fill_related_many_to_many_cache(self): - cache = SortedDict() + cache = OrderedDict() parent_list = self.get_parent_list() for parent in self.parents: for obj, model in parent._meta.get_all_related_m2m_objects_with_model(): diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index fa7ae51d9c..de431204bd 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -7,9 +7,9 @@ databases). The abstraction barrier only works one way: this module has to know all about the internals of models in order to get the information it needs. """ +from collections import OrderedDict import copy -from django.utils.datastructures import SortedDict from django.utils.encoding import force_text from django.utils.tree import Node from django.utils import six @@ -142,7 +142,7 @@ class Query(object): self.select_related = False # SQL aggregate-related attributes - self.aggregates = SortedDict() # Maps alias -> SQL aggregate function + self.aggregates = OrderedDict() # Maps alias -> SQL aggregate function self.aggregate_select_mask = None self._aggregate_select_cache = None @@ -152,7 +152,7 @@ class Query(object): # These are for extensions. The contents are more or less appended # verbatim to the appropriate clause. - self.extra = SortedDict() # Maps col_alias -> (col_sql, params). + self.extra = OrderedDict() # Maps col_alias -> (col_sql, params). self.extra_select_mask = None self._extra_select_cache = None @@ -741,7 +741,7 @@ class Query(object): self.group_by = [relabel_column(col) for col in self.group_by] self.select = [SelectInfo(relabel_column(s.col), s.field) for s in self.select] - self.aggregates = SortedDict( + self.aggregates = OrderedDict( (key, relabel_column(col)) for key, col in self.aggregates.items()) # 2. Rename the alias in the internal table/alias datastructures. @@ -795,7 +795,7 @@ class Query(object): assert current < ord('Z') prefix = chr(current + 1) self.alias_prefix = prefix - change_map = SortedDict() + change_map = OrderedDict() for pos, alias in enumerate(self.tables): if alias in exceptions: continue @@ -1638,7 +1638,7 @@ class Query(object): # dictionary with their parameters in 'select_params' so that # subsequent updates to the select dictionary also adjust the # parameters appropriately. - select_pairs = SortedDict() + select_pairs = OrderedDict() if select_params: param_iter = iter(select_params) else: @@ -1651,7 +1651,7 @@ class Query(object): entry_params.append(next(param_iter)) pos = entry.find("%s", pos + 2) select_pairs[name] = (entry, entry_params) - # This is order preserving, since self.extra_select is a SortedDict. + # This is order preserving, since self.extra_select is an OrderedDict. self.extra.update(select_pairs) if where or params: self.where.add(ExtraWhere(where, params), AND) @@ -1760,7 +1760,7 @@ class Query(object): self._extra_select_cache = None def _aggregate_select(self): - """The SortedDict of aggregate columns that are not masked, and should + """The OrderedDict of aggregate columns that are not masked, and should be used in the SELECT clause. This result is cached for optimization purposes. @@ -1768,7 +1768,7 @@ class Query(object): if self._aggregate_select_cache is not None: return self._aggregate_select_cache elif self.aggregate_select_mask is not None: - self._aggregate_select_cache = SortedDict([ + self._aggregate_select_cache = OrderedDict([ (k, v) for k, v in self.aggregates.items() if k in self.aggregate_select_mask ]) @@ -1781,7 +1781,7 @@ class Query(object): if self._extra_select_cache is not None: return self._extra_select_cache elif self.extra_select_mask is not None: - self._extra_select_cache = SortedDict([ + self._extra_select_cache = OrderedDict([ (k, v) for k, v in self.extra.items() if k in self.extra_select_mask ]) diff --git a/django/forms/forms.py b/django/forms/forms.py index 31e51e7c34..9905eee7c7 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -4,6 +4,7 @@ Form classes from __future__ import unicode_literals +from collections import OrderedDict import copy import warnings @@ -11,7 +12,6 @@ from django.core.exceptions import ValidationError from django.forms.fields import Field, FileField from django.forms.util import flatatt, ErrorDict, ErrorList from django.forms.widgets import Media, media_property, TextInput, Textarea -from django.utils.datastructures import SortedDict from django.utils.html import conditional_escape, format_html from django.utils.encoding import smart_text, force_text, python_2_unicode_compatible from django.utils.safestring import mark_safe @@ -55,7 +55,7 @@ def get_declared_fields(bases, attrs, with_base_fields=True): if hasattr(base, 'declared_fields'): fields = list(six.iteritems(base.declared_fields)) + fields - return SortedDict(fields) + return OrderedDict(fields) class DeclarativeFieldsMetaclass(type): """ diff --git a/django/forms/models.py b/django/forms/models.py index 83954b0b22..a5b82e521d 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -5,6 +5,7 @@ and database field objects. from __future__ import unicode_literals +from collections import OrderedDict import warnings from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, FieldError @@ -15,7 +16,6 @@ from django.forms.util import ErrorList from django.forms.widgets import (SelectMultiple, HiddenInput, MultipleHiddenInput, media_property, CheckboxSelectMultiple) from django.utils.encoding import smart_text, force_text -from django.utils.datastructures import SortedDict from django.utils import six from django.utils.text import get_text_list, capfirst from django.utils.translation import ugettext_lazy as _, ugettext, string_concat @@ -142,7 +142,7 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, formfield_callback=None, localized_fields=None, labels=None, help_texts=None, error_messages=None): """ - Returns a ``SortedDict`` containing form fields for the given model. + Returns a ``OrderedDict`` 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. @@ -199,9 +199,9 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None, field_list.append((f.name, formfield)) else: ignored.append(f.name) - field_dict = SortedDict(field_list) + field_dict = OrderedDict(field_list) if fields: - field_dict = SortedDict( + field_dict = OrderedDict( [(f, field_dict.get(f)) for f in fields if ((not exclude) or (exclude and f not in exclude)) and (f not in ignored)] ) diff --git a/django/middleware/locale.py b/django/middleware/locale.py index 4e0a4753ce..cd5d2cce71 100644 --- a/django/middleware/locale.py +++ b/django/middleware/locale.py @@ -1,12 +1,13 @@ "This is the locale selecting middleware that will look at accept headers" +from collections import OrderedDict + from django.conf import settings from django.core.urlresolvers import (is_valid_path, get_resolver, LocaleRegexURLResolver) from django.http import HttpResponseRedirect from django.utils.cache import patch_vary_headers from django.utils import translation -from django.utils.datastructures import SortedDict class LocaleMiddleware(object): @@ -19,7 +20,7 @@ class LocaleMiddleware(object): """ def __init__(self): - self._supported_languages = SortedDict(settings.LANGUAGES) + self._supported_languages = OrderedDict(settings.LANGUAGES) self._is_language_prefix_patterns_used = False for url_pattern in get_resolver(None).url_patterns: if isinstance(url_pattern, LocaleRegexURLResolver): diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 3b1638392c..d6447ec0c7 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -1,7 +1,7 @@ import copy +import warnings from django.utils import six - class MergeDict(object): """ A simple class for creating new "virtual" dictionaries that actually look @@ -124,6 +124,10 @@ class SortedDict(dict): return instance def __init__(self, data=None): + warnings.warn( + "SortedDict is deprecated and will be removed in Django 1.9.", + PendingDeprecationWarning, stacklevel=2 + ) if data is None or isinstance(data, dict): data = data or [] super(SortedDict, self).__init__(data) diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 91a73f1e68..11463d8f9b 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -1,6 +1,7 @@ """Translation helper functions.""" from __future__ import unicode_literals +from collections import OrderedDict import locale import os import re @@ -10,7 +11,6 @@ from importlib import import_module from threading import local import warnings -from django.utils.datastructures import SortedDict from django.utils.encoding import force_str, force_text from django.utils.functional import memoize from django.utils._os import upath @@ -369,7 +369,7 @@ def get_supported_language_variant(lang_code, supported=None, strict=False): """ if supported is None: from django.conf import settings - supported = SortedDict(settings.LANGUAGES) + supported = OrderedDict(settings.LANGUAGES) if lang_code: # if fr-CA is not supported, try fr-ca; if that fails, fallback to fr. generic_lang_code = lang_code.split('-')[0] @@ -396,7 +396,7 @@ def get_language_from_path(path, supported=None, strict=False): """ if supported is None: from django.conf import settings - supported = SortedDict(settings.LANGUAGES) + supported = OrderedDict(settings.LANGUAGES) regex_match = language_code_prefix_re.match(path) if not regex_match: return None @@ -418,7 +418,7 @@ def get_language_from_request(request, check_path=False): """ global _accepted from django.conf import settings - supported = SortedDict(settings.LANGUAGES) + supported = OrderedDict(settings.LANGUAGES) if check_path: lang_code = get_language_from_path(request.path_info, supported) diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 3d6cd48b00..9b0dbf8ffa 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -423,6 +423,9 @@ these changes. * FastCGI support via the ``runfcgi`` management command will be removed. Please deploy your project using WSGI. +* ``django.utils.datastructures.SortedDict`` will be removed. Use + :class:`collections.OrderedDict` from the Python standard library instead. + 2.0 --- diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 95dad202fa..c29ac987a6 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -977,14 +977,13 @@ of the arguments is required, but you should use at least one of them. ``select_params`` parameter. Since ``select_params`` is a sequence and the ``select`` attribute is a dictionary, some care is required so that the parameters are matched up correctly with the extra select pieces. - In this situation, you should use a - :class:`django.utils.datastructures.SortedDict` for the ``select`` - value, not just a normal Python dictionary. + In this situation, you should use a :class:`collections.OrderedDict` for + the ``select`` value, not just a normal Python dictionary. This will work, for example:: Blog.objects.extra( - select=SortedDict([('a', '%s'), ('b', '%s')]), + select=OrderedDict([('a', '%s'), ('b', '%s')]), select_params=('one', 'two')) The only thing to be careful about when using select parameters in diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 6b614e0c83..599797bedb 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -105,6 +105,10 @@ to distinguish caches by the ``Accept-language`` header. .. class:: SortedDict +.. deprecated:: 1.7 + ``SortedDict`` is deprecated and will be removed in Django 1.9. Use + :class:`collections.OrderedDict` instead. + The :class:`django.utils.datastructures.SortedDict` class is a dictionary that keeps its keys in the order in which they're inserted. diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index d21cb45428..91b932aff1 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -167,6 +167,13 @@ on all Python versions. Since ``unittest2`` became the standard library's Python versions, this module isn't useful anymore. It has been deprecated. Use :mod:`unittest` instead. +``django.utils.datastructures.SortedDict`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As :class:`~collections.OrderedDict` was added to the standard library in +Python 2.7, :class:`~django.utils.datastructures.SortedDict` is no longer +needed and has been deprecated. + Custom SQL location for models package ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/extra_regress/tests.py b/tests/extra_regress/tests.py index 921f33aab9..ae8f1b4423 100644 --- a/tests/extra_regress/tests.py +++ b/tests/extra_regress/tests.py @@ -1,10 +1,10 @@ from __future__ import unicode_literals +from collections import OrderedDict import datetime from django.contrib.auth.models import User from django.test import TestCase -from django.utils.datastructures import SortedDict from .models import TestObject, Order, RevisionableModel @@ -74,7 +74,7 @@ class ExtraRegressTests(TestCase): # select portions. Applies when portions are updated or otherwise # moved around. qs = User.objects.extra( - select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), + select=OrderedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select_params=(1, 3) ) qs = qs.extra(select={"beta": 4}) @@ -180,100 +180,100 @@ class ExtraRegressTests(TestCase): obj.save() self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()), [{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}] ) # Extra clauses after an empty values clause are still included self.assertEqual( - list(TestObject.objects.values().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), + list(TestObject.objects.values().extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [{'bar': 'second', 'third': 'third', 'second': 'second', 'whiz': 'third', 'foo': 'first', 'id': obj.pk, 'first': 'first'}] ) # Extra columns are ignored if not mentioned in the values() clause self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')), [{'second': 'second', 'first': 'first'}] ) # Extra columns after a non-empty values() clause are ignored self.assertEqual( - list(TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), + list(TestObject.objects.values('first', 'second').extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [{'second': 'second', 'first': 'first'}] ) # Extra columns can be partially returned self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')), [{'second': 'second', 'foo': 'first', 'first': 'first'}] ) # Also works if only extra columns are included self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')), [{'foo': 'first', 'whiz': 'third'}] ) # Values list works the same way # All columns are returned for an empty values_list() self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), [('first', 'second', 'third', obj.pk, 'first', 'second', 'third')] ) # Extra columns after an empty values_list() are still included self.assertEqual( - list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), + list(TestObject.objects.values_list().extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [('first', 'second', 'third', obj.pk, 'first', 'second', 'third')] ) # Extra columns ignored completely if not mentioned in values_list() self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')), [('first', 'second')] ) # Extra columns after a non-empty values_list() clause are ignored completely self.assertEqual( - list(TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), + list(TestObject.objects.values_list('first', 'second').extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [('first', 'second')] ) self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)), ['second'] ) # Only the extra columns specified in the values_list() are returned self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')), [('first', 'second', 'third')] ) # ...also works if only extra columns are included self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')), [('first', 'third')] ) self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)), ['third'] ) # ... and values are returned in the order they are specified self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')), [('third', 'first')] ) self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')), [('first', obj.pk)] ) self.assertEqual( - list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')), + list(TestObject.objects.extra(select=OrderedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')), [('third', 'first', 'second', obj.pk)] ) diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 025fcd8608..6e03b0d7f6 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +from collections import OrderedDict import datetime from operator import attrgetter import pickle @@ -14,7 +15,6 @@ from django.db.models.sql.where import WhereNode, EverythingNode, NothingNode from django.db.models.sql.datastructures import EmptyResultSet from django.test import TestCase, skipUnlessDBFeature from django.test.utils import str_prefix -from django.utils.datastructures import SortedDict from .models import ( Annotation, Article, Author, Celebrity, Child, Cover, Detail, DumbCategory, @@ -499,7 +499,7 @@ class Queries1Tests(BaseQuerysetTest): ) def test_ticket2902(self): - # Parameters can be given to extra_select, *if* you use a SortedDict. + # Parameters can be given to extra_select, *if* you use an OrderedDict. # (First we need to know which order the keys fall in "naturally" on # your system, so we can put things in the wrong way around from @@ -513,7 +513,7 @@ class Queries1Tests(BaseQuerysetTest): # This slightly odd comparison works around the fact that PostgreSQL will # return 'one' and 'two' as strings, not Unicode objects. It's a side-effect of # using constants here and not a real concern. - d = Item.objects.extra(select=SortedDict(s), select_params=params).values('a', 'b')[0] + d = Item.objects.extra(select=OrderedDict(s), select_params=params).values('a', 'b')[0] self.assertEqual(d, {'a': 'one', 'b': 'two'}) # Order by the number of tags attached to an item. @@ -1987,7 +1987,7 @@ class ValuesQuerysetTests(BaseQuerysetTest): def test_extra_values(self): # testing for ticket 14930 issues - qs = Number.objects.extra(select=SortedDict([('value_plus_x', 'num+%s'), + qs = Number.objects.extra(select=OrderedDict([('value_plus_x', 'num+%s'), ('value_minus_x', 'num-%s')]), select_params=(1, 2)) qs = qs.order_by('value_minus_x') -- cgit v1.3 From e8183a8193ebc80fdc6fef789813c2c0c3615e5c Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Fri, 2 Aug 2013 14:26:19 +0100 Subject: Fixed #20842 and #20845 - Added a note on order_by() and improved prefetch_related() docs. --- docs/ref/models/querysets.txt | 103 +++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 36 deletions(-) (limited to 'docs/ref/models') diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index c29ac987a6..570682be04 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -110,7 +110,7 @@ described here. .. admonition:: You can't share pickles between versions - Pickles of QuerySets are only valid for the version of Django that + Pickles of ``QuerySets`` are only valid for the version of Django that was used to generate them. If you generate a pickle using Django version N, there is no guarantee that pickle will be readable with Django version N+1. Pickles should not be used as part of a long-term @@ -298,14 +298,30 @@ Be cautious when ordering by fields in related models if you are also using :meth:`distinct()`. See the note in :meth:`distinct` for an explanation of how related model ordering can change the expected results. -It is permissible to specify a multi-valued field to order the results by (for -example, a :class:`~django.db.models.ManyToManyField` field). Normally -this won't be a sensible thing to do and it's really an advanced usage -feature. However, if you know that your queryset's filtering or available data -implies that there will only be one ordering piece of data for each of the main -items you are selecting, the ordering may well be exactly what you want to do. -Use ordering on multi-valued fields with care and make sure the results are -what you expect. +.. note:: + It is permissible to specify a multi-valued field to order the results by + (for example, a :class:`~django.db.models.ManyToManyField` field, or the + reverse relation of a :class:`~django.db.models.ForeignKey` field). + + Consider this case:: + + class Event(Model): + parent = models.ForeignKey('self', related_name='children') + date = models.DateField() + + Event.objects.order_by('children__date') + + Here, there could potentially be multiple ordering data for each ``Event``; + each ``Event`` with multiple ``children`` will be returned multiple times + into the new ``QuerySet`` that ``order_by()`` creates. In other words, + using ``order_by()`` on the ``QuerySet`` could return more items than you + were working on to begin with - which is probably neither expected nor + useful. + + Thus, take care when using multi-valued field to order the results. **If** + you can be sure that there will only be one ordering piece of data for each + of the items you're ordering, this approach should not present problems. If + not, make sure the results are what you expect. There's no way to specify whether ordering should be case sensitive. With respect to case-sensitivity, Django will order results however your database @@ -386,7 +402,7 @@ field names, the database will only compare the specified field names. .. note:: When you specify field names, you *must* provide an ``order_by()`` in the - QuerySet, and the fields in ``order_by()`` must start with the fields in + ``QuerySet``, and the fields in ``order_by()`` must start with the fields in ``distinct()``, in the same order. For example, ``SELECT DISTINCT ON (a)`` gives you the first row for each @@ -787,8 +803,8 @@ stop the deluge of database queries that is caused by accessing related objects, but the strategy is quite different. ``select_related`` works by creating a SQL join and including the fields of the -related object in the SELECT statement. For this reason, ``select_related`` gets -the related objects in the same database query. However, to avoid the much +related object in the ``SELECT`` statement. For this reason, ``select_related`` +gets the related objects in the same database query. However, to avoid the much larger result set that would result from joining across a 'many' relationship, ``select_related`` is limited to single-valued relationships - foreign key and one-to-one. @@ -817,39 +833,54 @@ For example, suppose you have these models:: return u"%s (%s)" % (self.name, u", ".join([topping.name for topping in self.toppings.all()])) -and run this code:: +and run:: >>> Pizza.objects.all() [u"Hawaiian (ham, pineapple)", u"Seafood (prawns, smoked salmon)"... -The problem with this code is that it will run a query on the Toppings table for -**every** item in the Pizza ``QuerySet``. Using ``prefetch_related``, this can -be reduced to two: +The problem with this is that every time ``Pizza.__unicode__()`` asks for +``self.toppings.all()`` it has to query the database, so +``Pizza.objects.all()`` will run a query on the Toppings table for **every** +item in the Pizza ``QuerySet``. + +We can reduce to just two queries using ``prefetch_related``: >>> Pizza.objects.all().prefetch_related('toppings') -All the relevant toppings will be fetched in a single query, and used to make -``QuerySets`` that have a pre-filled cache of the relevant results. These -``QuerySets`` are then used in the ``self.toppings.all()`` calls. +This implies a ``self.toppings.all()`` for each ``Pizza``; now each time +``self.toppings.all()`` is called, instead of having to go to the database for +the items, it will find them in a prefetched ``QuerySet`` cache that was +populated in a single query. + +That is, all the relevant toppings will have been fetched in a single query, +and used to make ``QuerySets`` that have a pre-filled cache of the relevant +results; these ``QuerySets`` are then used in the ``self.toppings.all()`` calls. + +The additional queries in ``prefetch_related()`` are executed after the +``QuerySet`` has begun to be evaluated and the primary query has been executed. -The additional queries are executed after the QuerySet has begun to be evaluated -and the primary query has been executed. Note that the result cache of the -primary QuerySet and all specified related objects will then be fully loaded -into memory, which is often avoided in other cases - even after a query has been -executed in the database, QuerySet normally tries to make uses of chunking -between the database to avoid loading all objects into memory before you need -them. +Note that the result cache of the primary ``QuerySet`` and all specified related +objects will then be fully loaded into memory. This changes the typical +behavior of ``QuerySets``, which normally try to avoid loading all objects into +memory before they are needed, even after a query has been executed in the +database. + +.. note:: -Also remember that, as always with QuerySets, any subsequent chained methods -which imply a different database query will ignore previously cached results, -and retrieve data using a fresh database query. So, if you write the following: + Remember that, as always with ``QuerySets``, any subsequent chained methods + which imply a different database query will ignore previously cached + results, and retrieve data using a fresh database query. So, if you write + the following: - >>> pizzas = Pizza.objects.prefetch_related('toppings') - >>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas] + >>> pizzas = Pizza.objects.prefetch_related('toppings') + >>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas] -...then the fact that ``pizza.toppings.all()`` has been prefetched will not help -you - in fact it hurts performance, since you have done a database query that -you haven't used. So use this feature with caution! + ...then the fact that ``pizza.toppings.all()`` has been prefetched will not + help you. The ``prefetch_related('toppings')`` implied + ``pizza.toppings.all()``, but ``pizza.toppings.filter()`` is a new and + different query. The prefetched cache can't help here; in fact it hurts + performance, since you have done a database query that you haven't used. So + use this feature with caution! You can also use the normal join syntax to do related fields of related fields. Suppose we have an additional model to the example above:: @@ -902,7 +933,7 @@ additional queries on the ``ContentType`` table if the relevant rows have not already been fetched. ``prefetch_related`` in most cases will be implemented using a SQL query that -uses the 'IN' operator. This means that for a large QuerySet a large 'IN' clause +uses the 'IN' operator. This means that for a large ``QuerySet`` a large 'IN' clause could be generated, which, depending on the database, might have performance problems of its own when it comes to parsing or executing the SQL query. Always profile for your use case! @@ -1273,7 +1304,7 @@ raw Takes a raw SQL query, executes it, and returns a ``django.db.models.query.RawQuerySet`` instance. This ``RawQuerySet`` instance -can be iterated over just like an normal QuerySet to provide object instances. +can be iterated over just like an normal ``QuerySet`` to provide object instances. See the :ref:`executing-raw-queries` for more information. -- cgit v1.3 From 94d7fed7750322e8b80402c1e731bab6d4509f2e Mon Sep 17 00:00:00 2001 From: Jimmy Song Date: Sun, 4 Aug 2013 19:40:16 +0000 Subject: Fixed #20859 - Clarified Model.clean() example. --- AUTHORS | 1 + docs/ref/models/instances.txt | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'docs/ref/models') diff --git a/AUTHORS b/AUTHORS index ade95df06a..130122c5ea 100644 --- a/AUTHORS +++ b/AUTHORS @@ -543,6 +543,7 @@ answer newbie questions, and generally made Django that much better: smurf@smurf.noris.de Vsevolod Solovyov George Song + Jimmy Song sopel Leo Soto Thomas Sorrel diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index 1e7ad1ffdf..cb8570afdc 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -140,15 +140,19 @@ attributes on your model if desired. For instance, you could use it to automatically provide a value for a field, or to do validation that requires access to more than a single field:: - def clean(self): - import datetime - from django.core.exceptions import ValidationError - # Don't allow draft entries to have a pub_date. - if self.status == 'draft' and self.pub_date is not None: - raise ValidationError('Draft entries may not have a publication date.') - # Set the pub_date for published items if it hasn't been set already. - if self.status == 'published' and self.pub_date is None: - self.pub_date = datetime.date.today() + import datetime + from django.core.exceptions import ValidationError + from django.db import models + + class Article(models.Model): + ... + def clean(self): + # Don't allow draft entries to have a pub_date. + if self.status == 'draft' and self.pub_date is not None: + raise ValidationError('Draft entries may not have a publication date.') + # Set the pub_date for published items if it hasn't been set already. + if self.status == 'published' and self.pub_date is None: + self.pub_date = datetime.date.today() Any :exc:`~django.core.exceptions.ValidationError` exceptions raised by ``Model.clean()`` will be stored in a special key error dictionary key, -- cgit v1.3 From c33d1ca1d98003de29cdecb6080b52c5c52139bd Mon Sep 17 00:00:00 2001 From: Dominic Rodger Date: Mon, 5 Aug 2013 17:23:26 +0100 Subject: Fixed #20852 - Fixed incorrectly generated left quotes in docs. Sphinx generates left single quotes for apostrophes after code markup, when right single quotes are required. The easiest way to fix this is just by inserting the unicode character for a right single quote. Instances of the problem were found by looking for ">‘" in the generated HTML. --- docs/howto/custom-management-commands.txt | 4 ++-- docs/howto/error-reporting.txt | 8 ++++---- docs/intro/tutorial05.txt | 2 +- docs/ref/class-based-views/mixins-date-based.txt | 2 +- docs/ref/contrib/admin/index.txt | 2 +- docs/ref/contrib/auth.txt | 2 +- docs/ref/contrib/comments/moderation.txt | 2 +- docs/ref/django-admin.txt | 2 +- docs/ref/models/options.txt | 2 +- docs/ref/models/querysets.txt | 2 +- docs/ref/settings.txt | 4 ++-- docs/ref/templates/api.txt | 2 +- docs/releases/1.3.txt | 4 ++-- docs/releases/1.6.txt | 4 ++-- docs/releases/1.7.txt | 2 +- docs/topics/class-based-views/mixins.txt | 2 +- docs/topics/db/managers.txt | 6 +++--- docs/topics/db/queries.txt | 2 +- docs/topics/db/tablespaces.txt | 2 +- docs/topics/forms/modelforms.txt | 4 ++-- 20 files changed, 30 insertions(+), 30 deletions(-) (limited to 'docs/ref/models') diff --git a/docs/howto/custom-management-commands.txt b/docs/howto/custom-management-commands.txt index 04ab9bae1b..2325e32ac2 100644 --- a/docs/howto/custom-management-commands.txt +++ b/docs/howto/custom-management-commands.txt @@ -193,7 +193,7 @@ Attributes ---------- All attributes can be set in your derived class and can be used in -:class:`BaseCommand`'s :ref:`subclasses`. +:class:`BaseCommand`’s :ref:`subclasses`. .. attribute:: BaseCommand.args @@ -267,7 +267,7 @@ the :meth:`~BaseCommand.handle` method must be implemented. .. admonition:: Implementing a constructor in a subclass If you implement ``__init__`` in your subclass of :class:`BaseCommand`, - you must call :class:`BaseCommand`'s ``__init__``. + you must call :class:`BaseCommand`’s ``__init__``. .. code-block:: python diff --git a/docs/howto/error-reporting.txt b/docs/howto/error-reporting.txt index 987a503e95..5ebed92187 100644 --- a/docs/howto/error-reporting.txt +++ b/docs/howto/error-reporting.txt @@ -120,8 +120,8 @@ Filtering sensitive information Error reports are really helpful for debugging errors, so it is generally useful to record as much relevant information about those errors as possible. For example, by default Django records the `full traceback`_ for the -exception raised, each `traceback frame`_'s local variables, and the -:class:`~django.http.HttpRequest`'s :ref:`attributes`. +exception raised, each `traceback frame`_’s local variables, and the +:class:`~django.http.HttpRequest`’s :ref:`attributes`. However, sometimes certain types of information may be too sensitive and thus may not be appropriate to be kept track of, for example a user's password or @@ -164,7 +164,7 @@ production environment (that is, where :setting:`DEBUG` is set to ``False``): .. admonition:: When using mutiple decorators If the variable you want to hide is also a function argument (e.g. - '``user``' in the following example), and if the decorated function has + '``user``’ in the following example), and if the decorated function has mutiple decorators, then make sure to place ``@sensitive_variables`` at the top of the decorator chain. This way it will also hide the function argument as it gets passed through the other decorators:: @@ -232,7 +232,7 @@ own filter class and tell Django to use it via the DEFAULT_EXCEPTION_REPORTER_FILTER = 'path.to.your.CustomExceptionReporterFilter' You may also control in a more granular way which filter to use within any -given view by setting the ``HttpRequest``'s ``exception_reporter_filter`` +given view by setting the ``HttpRequest``’s ``exception_reporter_filter`` attribute:: def my_view(request): diff --git a/docs/intro/tutorial05.txt b/docs/intro/tutorial05.txt index 39c3785f7c..63533d47ff 100644 --- a/docs/intro/tutorial05.txt +++ b/docs/intro/tutorial05.txt @@ -132,7 +132,7 @@ We identify a bug Fortunately, there's a little bug in the ``polls`` application for us to fix right away: the ``Poll.was_published_recently()`` method returns ``True`` if the ``Poll`` was published within the last day (which is correct) but also if -the ``Poll``'s ``pub_date`` field is in the future (which certainly isn't). +the ``Poll``’s ``pub_date`` field is in the future (which certainly isn't). You can see this in the Admin; create a poll whose date lies in the future; you'll see that the ``Poll`` change list claims it was published recently. diff --git a/docs/ref/class-based-views/mixins-date-based.txt b/docs/ref/class-based-views/mixins-date-based.txt index 1a1a4d531b..1162b2a194 100644 --- a/docs/ref/class-based-views/mixins-date-based.txt +++ b/docs/ref/class-based-views/mixins-date-based.txt @@ -225,7 +225,7 @@ DateMixin .. attribute:: date_field The name of the ``DateField`` or ``DateTimeField`` in the - ``QuerySet``'s model that the date-based archive should use to + ``QuerySet``’s model that the date-based archive should use to determine the list of objects to display on the page. When :doc:`time zone support ` is enabled and diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 04dc52a3a1..8feb574efd 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -1615,7 +1615,7 @@ in your own admin JavaScript without including a second copy, you can use the 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 -to add jQuery to your ``ModelAdmin``'s list of media resources unless you have +to add jQuery to your ``ModelAdmin``’s list of media resources unless you have a specifc need. For example, if you require the jQuery library to be in the global namespace (for example when using third-party jQuery plugins) or if you need a newer version of jQuery, you will have to include your own copy. diff --git a/docs/ref/contrib/auth.txt b/docs/ref/contrib/auth.txt index dfda8add4b..477ee10d5f 100644 --- a/docs/ref/contrib/auth.txt +++ b/docs/ref/contrib/auth.txt @@ -243,7 +243,7 @@ Manager methods be called. The ``extra_fields`` keyword arguments are passed through to the - :class:`~django.contrib.auth.models.User`'s ``__init__`` method to + :class:`~django.contrib.auth.models.User`’s ``__init__`` method to allow setting arbitrary fields on a :ref:`custom User model `. diff --git a/docs/ref/contrib/comments/moderation.txt b/docs/ref/contrib/comments/moderation.txt index 796e257200..5f0badfadb 100644 --- a/docs/ref/contrib/comments/moderation.txt +++ b/docs/ref/contrib/comments/moderation.txt @@ -54,7 +54,7 @@ following model, which would represent entries in a Weblog:: Now, suppose that we want the following steps to be applied whenever a new comment is posted on an ``Entry``: -1. If the ``Entry``'s ``enable_comments`` field is ``False``, the +1. If the ``Entry``’s ``enable_comments`` field is ``False``, the comment will simply be disallowed (i.e., immediately deleted). 2. If the ``enable_comments`` field is ``True``, the comment will be diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index ac02b4b5fb..fb9264559c 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -560,7 +560,7 @@ several lines in language files. .. django-admin-option:: --no-location -Use the ``--no-location`` option to not write '``#: filename:line``' +Use the ``--no-location`` option to not write '``#: filename:line``’ comment lines in language files. Note that using this option makes it harder for technically skilled translators to understand each message's context. diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt index 41baa8c106..7d751a53fe 100644 --- a/docs/ref/models/options.txt +++ b/docs/ref/models/options.txt @@ -90,7 +90,7 @@ Django quotes column and table names behind the scenes. The name of an orderable field in the model, typically a :class:`DateField`, :class:`DateTimeField`, or :class:`IntegerField`. This specifies the default - field to use in your model :class:`Manager`'s + field to use in your model :class:`Manager`’s :meth:`~django.db.models.query.QuerySet.latest` and :meth:`~django.db.models.query.QuerySet.earliest` methods. diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 570682be04..0f08022179 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -1924,7 +1924,7 @@ as_manager .. versionadded:: 1.7 Class method that returns an instance of :class:`~django.db.models.Manager` -with a copy of the ``QuerySet``'s methods. See +with a copy of the ``QuerySet``’s methods. See :ref:`create-manager-with-queryset-methods` for more details. .. _field-lookups: diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index c970311342..38d7275aed 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1744,7 +1744,7 @@ Default:: A tuple of template loader classes, specified as strings. Each ``Loader`` class knows how to import templates from a particular source. Optionally, a tuple can be -used instead of a string. The first item in the tuple should be the ``Loader``'s +used instead of a string. The first item in the tuple should be the ``Loader``’s module, subsequent items are passed to the ``Loader`` during initialization. See :doc:`/ref/templates/api`. @@ -2478,7 +2478,7 @@ files` for more details about usage. your static files from their permanent locations into one directory for ease of deployment; it is **not** a place to store your static files permanently. You should do that in directories that will be found by - :doc:`staticfiles`'s + :doc:`staticfiles`’s :setting:`finders`, which by default, are ``'static/'`` app sub-directories and any directories you include in :setting:`STATICFILES_DIRS`). diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index f7dd0121d1..87822fe7f8 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -708,7 +708,7 @@ class. Here are the template loaders that come with Django: with your own ``admin/base_site.html`` in ``myproject.polls``. You must then make sure that your ``myproject.polls`` comes *before* ``django.contrib.admin`` in :setting:`INSTALLED_APPS`, otherwise - ``django.contrib.admin``'s will be loaded first and yours will be ignored. + ``django.contrib.admin``’s will be loaded first and yours will be ignored. Note that the loader performs an optimization when it is first imported: it caches a list of which :setting:`INSTALLED_APPS` packages have a diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt index 060f099c52..6bf4c0db60 100644 --- a/docs/releases/1.3.txt +++ b/docs/releases/1.3.txt @@ -365,7 +365,7 @@ In earlier Django versions, when a model instance containing a file from the backend storage. This opened the door to several data-loss scenarios, including rolled-back transactions and fields on different models referencing the same file. In Django 1.3, when a model is deleted the -:class:`~django.db.models.FileField`'s ``delete()`` method won't be called. If +:class:`~django.db.models.FileField`’s ``delete()`` method won't be called. If you need cleanup of orphaned files, you'll need to handle it yourself (for instance, with a custom management command that can be run manually or scheduled to run periodically via e.g. cron). @@ -654,7 +654,7 @@ Password reset view now accepts ``from_email`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :func:`django.contrib.auth.views.password_reset` view now accepts a -``from_email`` parameter, which is passed to the ``password_reset_form``'s +``from_email`` parameter, which is passed to the ``password_reset_form``’s ``save()`` method as a keyword argument. If you are using this view with a custom password reset form, then you will need to ensure your form's ``save()`` method accepts this keyword argument. diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index b545cbcd64..c0f5c51194 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -183,7 +183,7 @@ Minor features * The :attr:`~django.views.generic.edit.DeletionMixin.success_url` of :class:`~django.views.generic.edit.DeletionMixin` is now interpolated with - its ``object``\'s ``__dict__``. + its ``object``’s ``__dict__``. * :class:`~django.http.HttpResponseRedirect` and :class:`~django.http.HttpResponsePermanentRedirect` now provide an ``url`` @@ -330,7 +330,7 @@ Minor features * :class:`~django.forms.ModelForm` fields can now override error messages defined in model fields by using the - :attr:`~django.forms.Field.error_messages` argument of a ``Field``'s + :attr:`~django.forms.Field.error_messages` argument of a ``Field``’s constructor. To take advantage of this new feature with your custom fields, :ref:`see the updated recommendation ` for raising a ``ValidationError``. diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index 28d9c9e1f1..161ae55436 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -141,7 +141,7 @@ Miscellaneous have a custom :class:`~django.core.files.uploadhandler.FileUploadHandler` that implements ``new_file()``, be sure it accepts this new parameter. -* :class:`ModelFormSet`'s no longer +* :class:`ModelFormSet`’s no longer delete instances when ``save(commit=False)`` is called. See :attr:`~django.forms.formsets.BaseFormSet.can_delete` for instructions on how to manually delete objects from deleted forms. diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt index e85ef5b063..fdd5d42c5d 100644 --- a/docs/topics/class-based-views/mixins.txt +++ b/docs/topics/class-based-views/mixins.txt @@ -288,7 +288,7 @@ object. In order to do this, we need to have two different querysets: ``Book`` queryset for use by :class:`~django.views.generic.list.ListView` Since we have access to the ``Publisher`` whose books we want to list, we - simply override ``get_queryset()`` and use the ``Publisher``'s + simply override ``get_queryset()`` and use the ``Publisher``’s :ref:`reverse foreign key manager`. ``Publisher`` queryset for use in :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()` diff --git a/docs/topics/db/managers.txt b/docs/topics/db/managers.txt index 3b83865e60..2d3f35db8b 100644 --- a/docs/topics/db/managers.txt +++ b/docs/topics/db/managers.txt @@ -100,7 +100,7 @@ access ``self.model`` to get the model class to which they're attached. Modifying initial Manager QuerySets ----------------------------------- -A ``Manager``'s base ``QuerySet`` returns all objects in the system. For +A ``Manager``’s base ``QuerySet`` returns all objects in the system. For example, using this model:: from django.db import models @@ -111,7 +111,7 @@ example, using this model:: ...the statement ``Book.objects.all()`` will return all books in the database. -You can override a ``Manager``\'s base ``QuerySet`` by overriding the +You can override a ``Manager``’s base ``QuerySet`` by overriding the ``Manager.get_queryset()`` method. ``get_queryset()`` should return a ``QuerySet`` with the properties you require. @@ -246,7 +246,7 @@ Creating ``Manager`` with ``QuerySet`` methods In lieu of the above approach which requires duplicating methods on both the ``QuerySet`` and the ``Manager``, :meth:`QuerySet.as_manager() ` can be used to create an instance -of ``Manager`` with a copy of a custom ``QuerySet``'s methods:: +of ``Manager`` with a copy of a custom ``QuerySet``’s methods:: class Person(models.Model): ... diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index 1d6052f938..9a0d0ce6b9 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -720,7 +720,7 @@ efficient code. In a newly created :class:`~django.db.models.query.QuerySet`, the cache is empty. The first time a :class:`~django.db.models.query.QuerySet` is evaluated -- and, hence, a database query happens -- Django saves the query results in -the :class:`~django.db.models.query.QuerySet`\'s cache and returns the results +the :class:`~django.db.models.query.QuerySet`’s cache and returns the results that have been explicitly requested (e.g., the next element, if the :class:`~django.db.models.query.QuerySet` is being iterated over). Subsequent evaluations of the :class:`~django.db.models.query.QuerySet` reuse the cached diff --git a/docs/topics/db/tablespaces.txt b/docs/topics/db/tablespaces.txt index 8bf1d07bca..115887f512 100644 --- a/docs/topics/db/tablespaces.txt +++ b/docs/topics/db/tablespaces.txt @@ -30,7 +30,7 @@ Declaring tablespaces for indexes --------------------------------- You can pass the :attr:`~django.db.models.Field.db_tablespace` option to a -``Field`` constructor to specify an alternate tablespace for the ``Field``'s +``Field`` constructor to specify an alternate tablespace for the ``Field``’s column index. If no index would be created for the column, the option is ignored. diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index 4ef7a6f074..a823c8acb5 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -392,7 +392,7 @@ these security concerns do not apply to you: model = Author fields = '__all__' -2. Set the ``exclude`` attribute of the ``ModelForm``'s inner ``Meta`` class to +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:: @@ -757,7 +757,7 @@ Specifying widgets to use in the form with ``widgets`` .. versionadded:: 1.6 Using the ``widgets`` parameter, you can specify a dictionary of values to -customize the ``ModelForm``'s widget class for a particular field. This +customize the ``ModelForm``’s widget class for a particular field. This works the same way as the ``widgets`` dictionary on the inner ``Meta`` class of a ``ModelForm`` works:: -- cgit v1.3 From fb26c4996a0c4d41aa80d28ce65ab050ffe6df6b Mon Sep 17 00:00:00 2001 From: Christopher Medrela Date: Wed, 7 Aug 2013 20:57:56 +0200 Subject: Fixed #20484 again -- added note to field documentation --- docs/ref/models/fields.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'docs/ref/models') diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 736a836924..01215884c4 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -883,6 +883,9 @@ are converted to lowercase. ``192.0.2.1``. Default is disabled. Can only be used when ``protocol`` is set to ``'both'``. +If you allow for blank values, you have to allow for null values since blank +values are stored as null. + ``NullBooleanField`` -------------------- -- cgit v1.3