Permalink
Checking mergeability…
Don’t worry, you can still create the pull request.
Comparing changes
Open a pull request
12
contributors
Unified
Split
Showing
with
2,380 additions
and 1,112 deletions.
- +1 −0 MANIFEST.in
- +1 −1 django/apps/base.py
- +8 −5 django/apps/registry.py
- +7 −7 django/contrib/auth/management/__init__.py
- +1 −0 django/contrib/auth/tests/test_templates.py
- +5 −1 django/contrib/auth/tests/urls.py
- +3 −2 django/contrib/contenttypes/generic.py
- +4 −4 django/contrib/contenttypes/management.py
- +1 −3 django/contrib/gis/db/backends/mysql/operations.py
- +1 −4 django/contrib/gis/db/backends/oracle/operations.py
- +1 −4 django/contrib/gis/db/backends/postgis/operations.py
- +1 −4 django/contrib/gis/db/backends/spatialite/operations.py
- +15 −0 django/contrib/gis/db/models/constants.py
- +6 −0 django/contrib/gis/db/models/fields.py
- +28 −0 django/contrib/gis/db/models/lookups.py
- +1 −1 django/contrib/gis/db/models/sql/compiler.py
- +1 −13 django/contrib/gis/db/models/sql/query.py
- +1 −1 django/contrib/staticfiles/finders.py
- +11 −3 django/contrib/staticfiles/storage.py
- +1 −0 django/core/mail/backends/smtp.py
- +2 −1 django/core/management/__init__.py
- +3 −2 django/core/management/commands/makemessages.py
- +4 −3 django/core/management/sql.py
- +4 −2 django/db/backends/__init__.py
- +3 −0 django/db/backends/oracle/base.py
- +12 −6 django/db/backends/postgresql_psycopg2/base.py
- +5 −0 django/db/backends/sqlite3/base.py
- +1 −1 django/db/migrations/questioner.py
- +1 −0 django/db/models/__init__.py
- +5 −4 django/db/models/aggregates.py
- +7 −5 django/db/models/fields/__init__.py
- +22 −14 django/db/models/fields/related.py
- +317 −0 django/db/models/lookups.py
- +2 −2 django/db/models/signals.py
- +9 −1 django/db/models/sql/aggregates.py
- +46 −32 django/db/models/sql/compiler.py
- +16 −7 django/db/models/sql/datastructures.py
- +3 −3 django/db/models/sql/expressions.py
- +111 −68 django/db/models/sql/query.py
- +8 −12 django/db/models/sql/subqueries.py
- +18 −9 django/db/models/sql/where.py
- +36 −27 django/dispatch/dispatcher.py
- +254 −0 django/dispatch/license.python.txt
- +0 −261 django/dispatch/saferef.py
- +69 −0 django/dispatch/weakref_backports.py
- +6 −2 django/forms/fields.py
- +1 −1 django/http/response.py
- +9 −9 django/test/client.py
- +12 −1 django/utils/html_parser.py
- +0 −13 django/utils/module_loading.py
- +6 −0 docs/howto/custom-model-fields.txt
- +11 −64 docs/howto/jython.txt
- +2 −1 docs/index.txt
- +1 −12 docs/internals/committers.txt
- +2 −5 docs/internals/contributing/bugs-and-features.txt
- +3 −0 docs/internals/deprecation.txt
- +26 −0 docs/misc/design-philosophies.txt
- +3 −2 docs/ref/class-based-views/base.txt
- +8 −3 docs/ref/contrib/gis/tutorial.txt
- +1 −1 docs/ref/files/storage.txt
- +6 −0 docs/ref/forms/fields.txt
- +336 −0 docs/ref/models/custom-lookups.txt
- +7 −0 docs/ref/models/fields.txt
- +1 −0 docs/ref/models/index.txt
- +4 −0 docs/ref/models/querysets.txt
- +6 −2 docs/ref/models/relations.txt
- +2 −2 docs/ref/signals.txt
- +3 −1 docs/ref/templates/builtins.txt
- +33 −0 docs/releases/1.7.txt
- +9 −8 docs/topics/auth/default.txt
- +4 −0 docs/topics/cache.txt
- +4 −1 docs/topics/db/examples/many_to_many.txt
- +7 −2 docs/topics/templates.txt
- +3 −3 docs/topics/testing/tools.txt
- +1 −1 setup.cfg
- +6 −4 tests/admin_custom_urls/tests.py
- +3 −2 tests/admin_scripts/tests.py
- +17 −13 tests/admin_widgets/tests.py
- +1 −1 tests/aggregation/tests.py
- +29 −10 tests/backends/tests.py
- +1 −1 tests/contenttypes_tests/models.py
- 0 tests/custom_lookups/__init__.py
- +13 −0 tests/custom_lookups/models.py
- +279 −0 tests/custom_lookups/tests.py
- +0 −76 tests/dispatch/tests/test_saferef.py
- +20 −1 tests/expressions/tests.py
- +6 −0 tests/field_deconstruction/tests.py
- +17 −15 tests/file_uploads/tests.py
- +6 −0 tests/forms_tests/tests/test_fields.py
- +13 −8 tests/forms_tests/tests/test_widgets.py
- +25 −5 tests/mail/tests.py
- +3 −3 tests/middleware/extra_urls.py
- +12 −11 tests/middleware/tests.py
- +3 −3 tests/migrate_signals/tests.py
- +6 −0 tests/model_inheritance_regress/tests.py
- +0 −3 tests/null_queries/tests.py
- +10 −3 tests/queries/tests.py
- +2 −2 tests/resolve_url/tests.py
- +6 −0 tests/resolve_url/urls.py
- +25 −23 tests/template_tests/tests.py
- +73 −68 tests/test_client/tests.py
- +7 −4 tests/test_client/urls.py
- +2 −2 tests/test_client/views.py
- +150 −117 tests/test_client_regress/tests.py
- +14 −12 tests/test_client_regress/urls.py
- +2 −2 tests/test_client_regress/views.py
- +6 −31 tests/urls.py
- +4 −5 tests/utils_tests/test_dateformat.py
- +1 −1 tests/view_tests/models.py
- +9 −8 tests/view_tests/tests/test_debug.py
- +9 −8 tests/view_tests/tests/test_defaults.py
- +18 −15 tests/view_tests/tests/test_i18n.py
- +9 −8 tests/view_tests/tests/test_static.py
| @@ -6,6 +6,7 @@ include MANIFEST.in | ||
| include django/contrib/gis/gdal/LICENSE | ||
| include django/contrib/gis/geos/LICENSE | ||
| include django/dispatch/license.txt | ||
| include django/dispatch/license.python.txt | ||
| recursive-include docs * | ||
| recursive-include scripts * | ||
| recursive-include extras * | ||
| @@ -99,7 +99,7 @@ def create(cls, entry): | ||
| raise ImproperlyConfigured( | ||
| "'%s' must supply a name attribute." % entry) | ||
| # Ensure app_names points to a valid module. | ||
| # Ensure app_name points to a valid module. | ||
| app_module = import_module(app_name) | ||
| # Entry is a path to an app config class. | ||
| @@ -1,11 +1,11 @@ | ||
| from collections import Counter, defaultdict, OrderedDict | ||
| import os | ||
| import sys | ||
| import threading | ||
| import warnings | ||
| from django.core.exceptions import ImproperlyConfigured | ||
| from django.utils import lru_cache | ||
| from django.utils.module_loading import import_lock | ||
| from django.utils._os import upath | ||
| from .base import AppConfig | ||
| @@ -44,6 +44,9 @@ def __init__(self, installed_apps=()): | ||
| # Whether the registry is populated. | ||
| self.ready = False | ||
| # Lock for thread-safe population. | ||
| self._lock = threading.Lock() | ||
| # Pending lookups for lazy relations. | ||
| self._pending_lookups = {} | ||
| @@ -61,10 +64,10 @@ def populate(self, installed_apps=None): | ||
| """ | ||
| if self.ready: | ||
| return | ||
| # Since populate() may be a side effect of imports, and since it will | ||
| # itself import modules, an ABBA deadlock between threads would be | ||
| # possible if we didn't take the import lock. See #18251. | ||
| with import_lock(): | ||
| # populate() might be called by two threads in parallel on servers | ||
| # that create threads before initializing the WSGI callable. | ||
| with self._lock: | ||
| if self.ready: | ||
| return | ||
| @@ -60,7 +60,7 @@ def _check_permission_clashing(custom, builtin, ctype): | ||
| pool.add(codename) | ||
| def create_permissions(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs): | ||
| def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs): | ||
| if not app_config.models_module: | ||
| return | ||
| @@ -69,7 +69,7 @@ def create_permissions(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ | ||
| except LookupError: | ||
| return | ||
| if not router.allow_migrate(db, Permission): | ||
| if not router.allow_migrate(using, Permission): | ||
| return | ||
| from django.contrib.contenttypes.models import ContentType | ||
| @@ -82,15 +82,15 @@ def create_permissions(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ | ||
| for klass in app_config.get_models(): | ||
| # Force looking up the content types in the current database | ||
| # before creating foreign keys to them. | ||
| ctype = ContentType.objects.db_manager(db).get_for_model(klass) | ||
| ctype = ContentType.objects.db_manager(using).get_for_model(klass) | ||
| ctypes.add(ctype) | ||
| for perm in _get_all_permissions(klass._meta, ctype): | ||
| searched_perms.append((ctype, perm)) | ||
| # Find all the Permissions that have a content_type for a model we're | ||
| # looking for. We don't need to check for codenames since we already have | ||
| # a list of the ones we're going to create. | ||
| all_perms = set(Permission.objects.using(db).filter( | ||
| all_perms = set(Permission.objects.using(using).filter( | ||
| content_type__in=ctypes, | ||
| ).values_list( | ||
| "content_type", "codename" | ||
| @@ -113,13 +113,13 @@ def create_permissions(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ | ||
| verbose_name_max_length, | ||
| ) | ||
| ) | ||
| Permission.objects.using(db).bulk_create(perms) | ||
| Permission.objects.using(using).bulk_create(perms) | ||
| if verbosity >= 2: | ||
| for perm in perms: | ||
| print("Adding permission '%s'" % perm) | ||
| def create_superuser(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs): | ||
| def create_superuser(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs): | ||
| try: | ||
| apps.get_model('auth', 'Permission') | ||
| except LookupError: | ||
| @@ -139,7 +139,7 @@ def create_superuser(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_AL | ||
| confirm = input('Please enter either "yes" or "no": ') | ||
| continue | ||
| if confirm == 'yes': | ||
| call_command("createsuperuser", interactive=True, database=db) | ||
| call_command("createsuperuser", interactive=True, database=using) | ||
| break | ||
| @@ -17,6 +17,7 @@ | ||
| PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), | ||
| ) | ||
| class AuthTemplateTests(TestCase): | ||
| urls = 'django.contrib.auth.tests.urls' | ||
| def test_titles(self): | ||
| rf = RequestFactory() | ||
| @@ -1,4 +1,5 @@ | ||
| from django.conf.urls import patterns, url | ||
| from django.conf.urls import patterns, url, include | ||
| from django.contrib import admin | ||
| from django.contrib.auth import context_processors | ||
| from django.contrib.auth.forms import AuthenticationForm | ||
| from django.contrib.auth.urls import urlpatterns | ||
| @@ -98,4 +99,7 @@ def custom_request_auth_login(request): | ||
| (r'^auth_processor_messages/$', auth_processor_messages), | ||
| (r'^custom_request_auth_login/$', custom_request_auth_login), | ||
| url(r'^userpage/(.+)/$', userpage, name="userpage"), | ||
| # This line is only required to render the password reset with is_admin=True | ||
| (r'^admin/', include(admin.site.urls)), | ||
| ) | ||
| @@ -12,7 +12,7 @@ | ||
| from django.db.models import signals | ||
| from django.db.models.fields.related import ForeignObject, ForeignObjectRel | ||
| from django.db.models.related import PathInfo | ||
| from django.db.models.sql.where import Constraint | ||
| from django.db.models.sql.datastructures import Col | ||
| from django.forms import ModelForm, ALL_FIELDS | ||
| from django.forms.models import (BaseModelFormSet, modelformset_factory, | ||
| modelform_defines_fields) | ||
| @@ -236,7 +236,8 @@ def get_extra_restriction(self, where_class, alias, remote_alias): | ||
| field = self.rel.to._meta.get_field_by_name(self.content_type_field_name)[0] | ||
| contenttype_pk = self.get_content_type().pk | ||
| cond = where_class() | ||
| cond.add((Constraint(remote_alias, field.column, field), 'exact', contenttype_pk), 'AND') | ||
| lookup = field.get_lookup('exact')(Col(remote_alias, field, field), contenttype_pk) | ||
| cond.add(lookup, 'AND') | ||
| return cond | ||
| def bulk_related_objects(self, objs, using=DEFAULT_DB_ALIAS): | ||
| @@ -6,7 +6,7 @@ | ||
| from django.utils.six.moves import input | ||
| def update_contenttypes(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs): | ||
| def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs): | ||
| """ | ||
| Creates content types for models in the given app, removing any model | ||
| entries that no longer have a matching model class. | ||
| @@ -19,7 +19,7 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, db=DEFAULT_DB | ||
| except LookupError: | ||
| return | ||
| if not router.allow_migrate(db, ContentType): | ||
| if not router.allow_migrate(using, ContentType): | ||
| return | ||
| ContentType.objects.clear_cache() | ||
| @@ -36,7 +36,7 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, db=DEFAULT_DB | ||
| # Get all the content types | ||
| content_types = dict( | ||
| (ct.model, ct) | ||
| for ct in ContentType.objects.using(db).filter(app_label=app_label) | ||
| for ct in ContentType.objects.using(using).filter(app_label=app_label) | ||
| ) | ||
| to_remove = [ | ||
| ct | ||
| @@ -53,7 +53,7 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, db=DEFAULT_DB | ||
| for (model_name, model) in six.iteritems(app_models) | ||
| if model_name not in content_types | ||
| ] | ||
| ContentType.objects.using(db).bulk_create(cts) | ||
| ContentType.objects.using(using).bulk_create(cts) | ||
| if verbosity >= 2: | ||
| for ct in cts: | ||
| print("Adding content type '%s | %s'" % (ct.app_label, ct.model)) | ||
| @@ -49,9 +49,7 @@ def get_geom_placeholder(self, value, srid): | ||
| return placeholder | ||
| def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn): | ||
| alias, col, db_type = lvalue | ||
| geo_col = '%s.%s' % (qn(alias), qn(col)) | ||
| geo_col, db_type = lvalue | ||
| lookup_info = self.geometry_functions.get(lookup_type, False) | ||
| if lookup_info: | ||
| @@ -231,10 +231,7 @@ def transform_value(val, srid): | ||
| def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn): | ||
| "Returns the SQL WHERE clause for use in Oracle spatial SQL construction." | ||
| alias, col, db_type = lvalue | ||
| # Getting the quoted table name as `geo_col`. | ||
| geo_col = '%s.%s' % (qn(alias), qn(col)) | ||
| geo_col, db_type = lvalue | ||
| # See if a Oracle Geometry function matches the lookup type next | ||
| lookup_info = self.geometry_functions.get(lookup_type, False) | ||
| @@ -478,10 +478,7 @@ def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn): | ||
| (alias, col, db_type), the lookup type string, lookup value, and | ||
| the geometry field. | ||
| """ | ||
| alias, col, db_type = lvalue | ||
| # Getting the quoted geometry column. | ||
| geo_col = '%s.%s' % (qn(alias), qn(col)) | ||
| geo_col, db_type = lvalue | ||
| if lookup_type in self.geometry_operators: | ||
| if field.geography and not lookup_type in self.geography_operators: | ||
| @@ -324,10 +324,7 @@ def spatial_lookup_sql(self, lvalue, lookup_type, value, field, qn): | ||
| [a tuple of (alias, column, db_type)], lookup type, lookup | ||
| value, the model field, and the quoting function. | ||
| """ | ||
| alias, col, db_type = lvalue | ||
| # Getting the quoted field as `geo_col`. | ||
| geo_col = '%s.%s' % (qn(alias), qn(col)) | ||
| geo_col, db_type = lvalue | ||
| if lookup_type in self.geometry_functions: | ||
| # See if a SpatiaLite geometry function matches the lookup type. | ||
| @@ -0,0 +1,15 @@ | ||
| from django.db.models.sql.constants import QUERY_TERMS | ||
| GIS_LOOKUPS = { | ||
| 'bbcontains', 'bboverlaps', 'contained', 'contains', | ||
| 'contains_properly', 'coveredby', 'covers', 'crosses', 'disjoint', | ||
| 'distance_gt', 'distance_gte', 'distance_lt', 'distance_lte', | ||
| 'dwithin', 'equals', 'exact', | ||
| 'intersects', 'overlaps', 'relate', 'same_as', 'touches', 'within', | ||
| 'left', 'right', 'overlaps_left', 'overlaps_right', | ||
| 'overlaps_above', 'overlaps_below', | ||
| 'strictly_above', 'strictly_below' | ||
| } | ||
| ALL_TERMS = GIS_LOOKUPS | QUERY_TERMS | ||
| __all__ = ['ALL_TERMS', 'GIS_LOOKUPS'] |
| @@ -2,6 +2,8 @@ | ||
| from django.db.models.sql.expressions import SQLEvaluator | ||
| from django.utils.translation import ugettext_lazy as _ | ||
| from django.contrib.gis import forms | ||
| from django.contrib.gis.db.models.constants import GIS_LOOKUPS | ||
| from django.contrib.gis.db.models.lookups import GISLookup | ||
| from django.contrib.gis.db.models.proxy import GeometryProxy | ||
| from django.contrib.gis.geometry.backend import Geometry, GeometryException | ||
| from django.utils import six | ||
| @@ -284,6 +286,10 @@ def get_placeholder(self, value, connection): | ||
| """ | ||
| return connection.ops.get_geom_placeholder(self, value) | ||
| for lookup_name in GIS_LOOKUPS: | ||
| lookup = type(lookup_name, (GISLookup,), {'lookup_name': lookup_name}) | ||
| GeometryField.register_lookup(lookup) | ||
| # The OpenGIS Geometry Type Fields | ||
| class PointField(GeometryField): | ||
| @@ -0,0 +1,28 @@ | ||
| from django.db.models.lookups import Lookup | ||
| from django.db.models.sql.expressions import SQLEvaluator | ||
| class GISLookup(Lookup): | ||
| def as_sql(self, qn, connection): | ||
| from django.contrib.gis.db.models.sql import GeoWhereNode | ||
| # We use the same approach as was used by GeoWhereNode. It would | ||
| # be a good idea to upgrade GIS to use similar code that is used | ||
| # for other lookups. | ||
| if isinstance(self.rhs, SQLEvaluator): | ||
| # Make sure the F Expression destination field exists, and | ||
| # set an `srid` attribute with the same as that of the | ||
| # destination. | ||
| geo_fld = GeoWhereNode._check_geo_field(self.rhs.opts, self.rhs.expression.name) | ||
| if not geo_fld: | ||
| raise ValueError('No geographic field found in expression.') | ||
| self.rhs.srid = geo_fld.srid | ||
| db_type = self.lhs.output_type.db_type(connection=connection) | ||
| params = self.lhs.output_type.get_db_prep_lookup( | ||
| self.lookup_name, self.rhs, connection=connection) | ||
| lhs_sql, lhs_params = self.process_lhs(qn, connection) | ||
| # lhs_params not currently supported. | ||
| assert not lhs_params | ||
| data = (lhs_sql, db_type) | ||
| spatial_sql, spatial_params = connection.ops.spatial_lookup_sql( | ||
| data, self.lookup_name, self.rhs, self.lhs.output_type, qn) | ||
| return spatial_sql, spatial_params + params |
| @@ -133,7 +133,7 @@ def get_default_columns(self, with_aliases=False, col_aliases=None, | ||
| if table in only_load and field.column not in only_load[table]: | ||
| continue | ||
| if as_pairs: | ||
| result.append((alias, field.column)) | ||
| result.append((alias, field)) | ||
| aliases.add(alias) | ||
| continue | ||
| # This part of the function is customized for GeoQuery. We | ||
| @@ -1,6 +1,7 @@ | ||
| from django.db import connections | ||
| from django.db.models.query import sql | ||
| from django.contrib.gis.db.models.constants import ALL_TERMS | ||
| from django.contrib.gis.db.models.fields import GeometryField | ||
| from django.contrib.gis.db.models.sql import aggregates as gis_aggregates | ||
| from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField | ||
| @@ -9,19 +10,6 @@ | ||
| from django.contrib.gis.measure import Area, Distance | ||
| ALL_TERMS = set([ | ||
| 'bbcontains', 'bboverlaps', 'contained', 'contains', | ||
| 'contains_properly', 'coveredby', 'covers', 'crosses', 'disjoint', | ||
| 'distance_gt', 'distance_gte', 'distance_lt', 'distance_lte', | ||
| 'dwithin', 'equals', 'exact', | ||
| 'intersects', 'overlaps', 'relate', 'same_as', 'touches', 'within', | ||
| 'left', 'right', 'overlaps_left', 'overlaps_right', | ||
| 'overlaps_above', 'overlaps_below', | ||
| 'strictly_above', 'strictly_below' | ||
| ]) | ||
| ALL_TERMS.update(sql.constants.QUERY_TERMS) | ||
| class GeoQuery(sql.Query): | ||
| """ | ||
| A single spatial SQL query. | ||
| @@ -57,7 +57,7 @@ def __init__(self, app_names=None, *args, **kwargs): | ||
| prefix, root = root | ||
| else: | ||
| prefix = '' | ||
| if os.path.abspath(settings.STATIC_ROOT) == os.path.abspath(root): | ||
| if settings.STATIC_ROOT and os.path.abspath(settings.STATIC_ROOT) == os.path.abspath(root): | ||
| raise ImproperlyConfigured( | ||
| "The STATICFILES_DIRS setting should " | ||
| "not contain the STATIC_ROOT setting") | ||
| @@ -32,13 +32,21 @@ def __init__(self, location=None, base_url=None, *args, **kwargs): | ||
| location = settings.STATIC_ROOT | ||
| if base_url is None: | ||
| base_url = settings.STATIC_URL | ||
| check_settings(base_url) | ||
| super(StaticFilesStorage, self).__init__(location, base_url, | ||
| *args, **kwargs) | ||
| # FileSystemStorage fallbacks to MEDIA_ROOT when location | ||
| # is empty, so we restore the empty value. | ||
| if not location: | ||
| self.base_location = None | ||
| self.location = None | ||
| def path(self, name): | ||
| if not self.location: | ||
| raise ImproperlyConfigured("You're using the staticfiles app " | ||
| "without having set the STATIC_ROOT " | ||
| "setting to a filesystem path.") | ||
| check_settings(base_url) | ||
| super(StaticFilesStorage, self).__init__(location, base_url, | ||
| *args, **kwargs) | ||
| return super(StaticFilesStorage, self).path(name) | ||
| class CachedFilesMixin(object): | ||
| @@ -57,6 +57,7 @@ def open(self): | ||
| self.connection.ehlo() | ||
| if self.username and self.password: | ||
| self.connection.login(self.username, self.password) | ||
| return True | ||
| except smtplib.SMTPException: | ||
| if not self.fail_silently: | ||
| raise | ||
Oops, something went wrong.