Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of https://github.com/django/django

  • Loading branch information...
commit 5c645ec81a872116533ee6d3928b750d3737b68a 2 parents 496b4d0 + ff723d8
@alex alex authored
Showing with 1,299 additions and 590 deletions.
  1. +1 −0  AUTHORS
  2. +2 −29 django/contrib/admin/__init__.py
  3. +7 −5 django/contrib/admin/filters.py
  4. +49 −37 django/contrib/admin/sites.py
  5. +2 −1  django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js
  6. +37 −6 django/contrib/admin/static/admin/js/calendar.js
  7. +4 −2 django/contrib/admin/templates/admin/base.html
  8. +3 −3 django/contrib/auth/hashers.py
  9. +6 −6 django/contrib/auth/models.py
  10. +1 −2  django/contrib/comments/templatetags/comments.py
  11. +5 −5 django/contrib/gis/db/backends/base.py
  12. +4 −3 django/contrib/gis/db/backends/spatialite/introspection.py
  13. +1 −0  django/contrib/gis/tests/inspectapp/models.py
  14. +18 −0 django/contrib/gis/tests/inspectapp/tests.py
  15. +3 −4 django/contrib/humanize/tests.py
  16. +2 −2 django/contrib/messages/storage/base.py
  17. +6 −6 django/contrib/sessions/backends/base.py
  18. +2 −2 django/contrib/staticfiles/finders.py
  19. +6 −6 django/contrib/syndication/views.py
  20. +5 −5 django/core/cache/backends/base.py
  21. +9 −8 django/core/files/storage.py
  22. +1 −0  django/core/files/uploadedfile.py
  23. +2 −2 django/core/files/uploadhandler.py
  24. +1 −1  django/core/mail/backends/base.py
  25. +4 −4 django/core/management/base.py
  26. +11 −3 django/core/management/commands/inspectdb.py
  27. +6 −6 django/core/serializers/base.py
  28. +25 −22 django/db/backends/__init__.py
  29. +1 −0  django/db/backends/oracle/creation.py
  30. +1 −1  django/db/backends/schema.py
  31. +3 −3 django/db/migrations/operations/base.py
  32. +23 −17 django/db/migrations/writer.py
  33. +3 −1 django/db/models/deletion.py
  34. +2 −2 django/db/models/sql/compiler.py
  35. +45 −20 django/db/models/sql/query.py
  36. +5 −0 django/forms/fields.py
  37. +2 −1  django/forms/forms.py
  38. +12 −5 django/forms/util.py
  39. +1 −1  django/forms/widgets.py
  40. +2 −0  django/http/cookie.py
  41. +2 −1  django/http/response.py
  42. +9 −26 django/template/base.py
  43. +2 −0  django/template/defaulttags.py
  44. +1 −1  django/template/loader.py
  45. +1 −1  django/test/__init__.py
  46. +8 −4 django/test/runner.py
  47. +17 −11 django/test/testcases.py
  48. +2 −2 django/utils/archive.py
  49. +3 −4 django/utils/dateformat.py
  50. +5 −4 django/utils/dateparse.py
  51. +1 −1  django/utils/feedgenerator.py
  52. +1 −1  django/utils/functional.py
  53. +3 −5 django/utils/log.py
  54. +38 −0 django/utils/module_loading.py
  55. +1 −1  django/utils/regex_helper.py
  56. +48 −12 django/utils/timezone.py
  57. +4 −1 django/utils/translation/trans_real.py
  58. +16 −1 django/utils/tzinfo.py
  59. +1 −1  docs/howto/static-files/index.txt
  60. +2 −0  docs/internals/contributing/committing-code.txt
  61. +22 −4 docs/internals/contributing/writing-code/working-with-git.txt
  62. +4 −2 docs/internals/deprecation.txt
  63. +6 −16 docs/intro/reusable-apps.txt
  64. +3 −3 docs/intro/tutorial05.txt
  65. +1 −1  docs/ref/contrib/auth.txt
  66. +6 −4 docs/ref/contrib/gis/install/spatialite.txt
  67. +9 −0 docs/ref/django-admin.txt
  68. +14 −0 docs/ref/middleware.txt
  69. +6 −0 docs/ref/models/options.txt
  70. +12 −3 docs/ref/models/querysets.txt
  71. +1 −1  docs/ref/signals.txt
  72. +7 −2 docs/ref/templates/builtins.txt
  73. +20 −0 docs/ref/utils.txt
  74. +1 −1  docs/releases/1.0-porting-guide.txt
  75. +1 −1  docs/releases/1.1-beta-1.txt
  76. +0 −2  docs/releases/1.1.txt
  77. +1 −1  docs/releases/1.2.2.txt
  78. +3 −3 docs/releases/1.3-alpha-1.txt
  79. +3 −3 docs/releases/1.3.txt
  80. +1 −1  docs/releases/1.4.6.txt
  81. +25 −0 docs/releases/1.4.7.txt
  82. +1 −1  docs/releases/1.5.2.txt
  83. +50 −0 docs/releases/1.5.3.txt
  84. +22 −7 docs/releases/1.6.txt
  85. +44 −7 docs/releases/1.7.txt
  86. +2 −0  docs/releases/index.txt
  87. +1 −2  docs/topics/auth/customizing.txt
  88. +17 −17 docs/topics/cache.txt
  89. +11 −0 docs/topics/db/optimization.txt
  90. +23 −7 docs/topics/testing/advanced.txt
  91. +25 −26 docs/topics/testing/overview.txt
  92. +0 −1  tests/admin_custom_urls/__init__.py
  93. +49 −1 tests/admin_views/tests.py
  94. +73 −0 tests/admin_widgets/tests.py
  95. +2 −1  tests/aggregation/tests.py
  96. +2 −1  tests/aggregation_regress/tests.py
  97. +0 −1  tests/bash_completion/__init__.py
  98. +0 −1  tests/bash_completion/management/__init__.py
  99. +0 −1  tests/bash_completion/management/commands/__init__.py
  100. +0 −8 tests/comment_tests/tests/__init__.py
  101. +0 −1  tests/conditional_processing/__init__.py
  102. +0 −1  tests/delete_regress/__init__.py
  103. +0 −2  tests/dispatch/__init__.py
  104. +0 −6 tests/dispatch/tests/__init__.py
  105. +0 −1  tests/distinct_on_fields/__init__.py
  106. +1 −2  tests/distinct_on_fields/tests.py
  107. +2 −1  tests/expressions_regress/tests.py
  108. +0 −1  tests/file_storage/__init__.py
  109. +26 −12 tests/file_uploads/tests.py
  110. +0 −1  tests/files/__init__.py
  111. +0 −1  tests/files/tests.py
  112. +0 −2  tests/fixtures/__init__.py
  113. +11 −0 tests/fixtures/tests.py
  114. +0 −2  tests/fixtures_model_package/__init__.py
  115. +0 −19 tests/forms_tests/tests/__init__.py
  116. +32 −0 tests/forms_tests/tests/test_forms.py
  117. +16 −0 tests/httpwrappers/tests.py
  118. +1 −0  tests/i18n/tests.py
  119. +0 −1  tests/inspectdb/__init__.py
  120. +25 −2 tests/inspectdb/tests.py
  121. +3 −2 tests/introspection/tests.py
  122. +0 −1  tests/invalid_models/__init__.py
  123. +0 −1  tests/m2m_signals/__init__.py
  124. +0 −2  tests/m2m_through/__init__.py
  125. +0 −2  tests/m2m_through_regress/__init__.py
  126. +0 −2  tests/mail/__init__.py
  127. +2 −2 tests/mail/tests.py
  128. +0 −1  tests/max_lengths/__init__.py
  129. +4 −0 tests/migrations/test_writer.py
  130. +0 −1  tests/model_package/__init__.py
  131. +3 −3 tests/model_regress/tests.py
  132. +12 −0 tests/queryset_pickle/tests.py
  133. +0 −3  tests/requests/__init__.py
  134. +0 −1  tests/select_for_update/__init__.py
  135. +2 −1  tests/serializers/tests.py
  136. +3 −2 tests/syndication/feeds.py
  137. +23 −11 tests/syndication/tests.py
  138. +6 −6 tests/template_tests/filters.py
  139. +40 −1 tests/template_tests/test_parser.py
  140. +31 −0 tests/template_tests/tests.py
  141. +4 −0 tests/test_client/tests.py
  142. +2 −1  tests/test_client/urls.py
  143. +3 −0  tests/test_client/views.py
  144. +10 −1 tests/test_runner/test_discover_runner.py
  145. +2 −3 tests/timezones/tests.py
  146. +0 −2  tests/unmanaged_models/__init__.py
  147. +16 −29 tests/utils_tests/test_dateformat.py
  148. +4 −4 tests/utils_tests/test_dateparse.py
  149. +3 −3 tests/utils_tests/test_feedgenerator.py
  150. +3 −0  tests/utils_tests/test_module/__init__.py
  151. +8 −0 tests/utils_tests/test_module/another_bad_module.py
  152. +1 −0  tests/utils_tests/test_module/another_good_module.py
  153. +30 −1 tests/utils_tests/test_module_loading.py
  154. +4 −3 tests/utils_tests/test_timesince.py
  155. +2 −3 tests/utils_tests/test_timezone.py
  156. +8 −2 tests/utils_tests/test_tzinfo.py
  157. +0 −1  tests/view_tests/app0/__init__.py
  158. +0 −1  tests/view_tests/app1/__init__.py
  159. +0 −1  tests/view_tests/app2/__init__.py
  160. +0 −1  tests/view_tests/app3/__init__.py
  161. +0 −1  tests/view_tests/app4/__init__.py
  162. +0 −8 tests/view_tests/tests/__init__.py
View
1  AUTHORS
@@ -397,6 +397,7 @@ answer newbie questions, and generally made Django that much better:
Yann Malet
Frantisek Malina <vizualbod@vizualbod.com>
Mike Malone <mjmalone@gmail.com>
+ Curtis Maloney (FunkyBob) <curtis@tinbrain.net>
Martin Maney <http://www.chipy.org/Martin_Maney>
Michael Manfre <mmanfre@gmail.com>
Javier Mansilla <javimansilla@gmail.com>
View
31 django/contrib/admin/__init__.py
@@ -7,35 +7,8 @@
from django.contrib.admin.filters import (ListFilter, SimpleListFilter,
FieldListFilter, BooleanFieldListFilter, RelatedFieldListFilter,
ChoicesFieldListFilter, DateFieldListFilter, AllValuesFieldListFilter)
+from django.utils.module_loading import autodiscover_modules
def autodiscover():
- """
- Auto-discover INSTALLED_APPS admin.py modules and fail silently when
- not present. This forces an import on them to register any admin bits they
- may want.
- """
-
- import copy
- from importlib import import_module
- from django.conf import settings
- from django.utils.module_loading import module_has_submodule
-
- for app in settings.INSTALLED_APPS:
- mod = import_module(app)
- # Attempt to import the app's admin module.
- try:
- before_import_registry = copy.copy(site._registry)
- import_module('%s.admin' % app)
- except:
- # Reset the model registry to the state before the last import as
- # this import will have to reoccur on the next request and this
- # could raise NotRegistered and AlreadyRegistered exceptions
- # (see #8245).
- site._registry = before_import_registry
-
- # Decide whether to bubble up this error. If the app just
- # doesn't have an admin module, we can ignore the error
- # attempting to import it, otherwise we want it to bubble up.
- if module_has_submodule(mod, 'admin'):
- raise
+ autodiscover_modules('admin', register_to=site)
View
12 django/contrib/admin/filters.py
@@ -33,26 +33,26 @@ def has_output(self):
"""
Returns True if some choices would be output for this filter.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of ListFilter must provide a has_output() method')
def choices(self, cl):
"""
Returns choices ready to be output in the template.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of ListFilter must provide a choices() method')
def queryset(self, request, queryset):
"""
Returns the filtered queryset.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of ListFilter must provide a queryset() method')
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of ListFilter must provide an expected_parameters() method')
class SimpleListFilter(ListFilter):
@@ -89,7 +89,9 @@ def lookups(self, request, model_admin):
"""
Must be overridden to return a list of tuples (value, verbose value)
"""
- raise NotImplementedError
+ raise NotImplementedError(
+ 'The SimpleListFilter.lookups() method must be overridden to '
+ 'return a list of tuples (value, verbose value)')
def expected_parameters(self):
return [self.parameter_name]
View
86 django/contrib/admin/sites.py
@@ -6,7 +6,7 @@
from django.contrib.contenttypes import views as contenttype_views
from django.views.decorators.csrf import csrf_protect
from django.db.models.base import ModelBase
-from django.core.exceptions import ImproperlyConfigured
+from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.core.urlresolvers import reverse, NoReverseMatch
from django.template.response import TemplateResponse
from django.utils import six
@@ -232,14 +232,25 @@ def wrapper(*args, **kwargs):
url(r'^password_change/done/$', wrap(self.password_change_done, cacheable=True), name='password_change_done'),
url(r'^jsi18n/$', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'),
url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$', wrap(contenttype_views.shortcut), name='view_on_site'),
- url(r'^(?P<app_label>\w+)/$', wrap(self.app_index), name='app_list'),
)
- # Add in each model's views.
+ # Add in each model's views, and create a list of valid URLS for the
+ # app_index
+ valid_app_labels = []
for model, model_admin in six.iteritems(self._registry):
urlpatterns += patterns('',
url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls))
)
+ if model._meta.app_label not in valid_app_labels:
+ valid_app_labels.append(model._meta.app_label)
+
+ # If there were ModelAdmins registered, we should have a list of app
+ # labels for which we need to allow access to the app_index view,
+ if valid_app_labels:
+ regex = r'^(?P<app_label>' + '|'.join(valid_app_labels) + ')/$'
+ urlpatterns += patterns('',
+ url(regex, wrap(self.app_index), name='app_list'),
+ )
return urlpatterns
@property
@@ -399,44 +410,45 @@ def index(self, request, extra_context=None):
def app_index(self, request, app_label, extra_context=None):
user = request.user
has_module_perms = user.has_module_perms(app_label)
+ if not has_module_perms:
+ raise PermissionDenied
app_dict = {}
for model, model_admin in self._registry.items():
if app_label == model._meta.app_label:
- if has_module_perms:
- perms = model_admin.get_model_perms(request)
-
- # Check whether user has any perm for this module.
- # If so, add the module to the model_list.
- if True in perms.values():
- info = (app_label, model._meta.model_name)
- model_dict = {
- 'name': capfirst(model._meta.verbose_name_plural),
- 'object_name': model._meta.object_name,
- 'perms': perms,
+ perms = model_admin.get_model_perms(request)
+
+ # Check whether user has any perm for this module.
+ # If so, add the module to the model_list.
+ if True in perms.values():
+ info = (app_label, model._meta.model_name)
+ model_dict = {
+ 'name': capfirst(model._meta.verbose_name_plural),
+ 'object_name': model._meta.object_name,
+ 'perms': perms,
+ }
+ if perms.get('change'):
+ try:
+ model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
+ except NoReverseMatch:
+ pass
+ if perms.get('add'):
+ try:
+ model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name)
+ except NoReverseMatch:
+ pass
+ if app_dict:
+ app_dict['models'].append(model_dict),
+ else:
+ # First time around, now that we know there's
+ # something to display, add in the necessary meta
+ # information.
+ app_dict = {
+ 'name': app_label.title(),
+ 'app_label': app_label,
+ 'app_url': '',
+ 'has_module_perms': has_module_perms,
+ 'models': [model_dict],
}
- if perms.get('change', False):
- try:
- model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
- except NoReverseMatch:
- pass
- if perms.get('add', False):
- try:
- model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=self.name)
- except NoReverseMatch:
- pass
- if app_dict:
- app_dict['models'].append(model_dict),
- else:
- # First time around, now that we know there's
- # something to display, add in the necessary meta
- # information.
- app_dict = {
- 'name': app_label.title(),
- 'app_label': app_label,
- 'app_url': '',
- 'has_module_perms': has_module_perms,
- 'models': [model_dict],
- }
if not app_dict:
raise Http404('The requested admin page does not exist.')
# Sort the models alphabetically within each app.
View
3  django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js
@@ -293,8 +293,9 @@ var DateTimeShortcuts = {
var date_parts = inp.value.split('-');
var year = date_parts[0];
var month = parseFloat(date_parts[1]);
+ var selected = new Date(inp.value);
if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
- DateTimeShortcuts.calendars[num].drawDate(month, year);
+ DateTimeShortcuts.calendars[num].drawDate(month, year, selected);
}
}
View
43 django/contrib/admin/static/admin/js/calendar.js
@@ -27,13 +27,29 @@ var CalendarNamespace = {
}
return days;
},
- draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999
+ draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999
var today = new Date();
var todayDay = today.getDate();
var todayMonth = today.getMonth()+1;
var todayYear = today.getFullYear();
var todayClass = '';
+ // Use UTC functions here because the date field does not contain time
+ // and using the UTC function variants prevent the local time offset
+ // from altering the date, specifically the day field. For example:
+ //
+ // ```
+ // var x = new Date('2013-10-02');
+ // var day = x.getDate();
+ // ```
+ //
+ // The day variable above will be 1 instead of 2 in, say, US Pacific time
+ // zone.
+ var isSelectedMonth = false;
+ if (typeof selected != 'undefined') {
+ isSelectedMonth = (selected.getUTCFullYear() == year && (selected.getUTCMonth()+1) == month);
+ }
+
month = parseInt(month);
year = parseInt(year);
var calDiv = document.getElementById(div_id);
@@ -55,7 +71,7 @@ var CalendarNamespace = {
tableRow = quickElement('tr', tableBody);
for (var i = 0; i < startingPos; i++) {
var _cell = quickElement('td', tableRow, ' ');
- _cell.style.backgroundColor = '#f3f3f3';
+ _cell.className = "nonday";
}
// Draw days of month
@@ -69,6 +85,13 @@ var CalendarNamespace = {
} else {
todayClass='';
}
+
+ // use UTC function; see above for explanation.
+ if (isSelectedMonth && currentDay == selected.getUTCDate()) {
+ if (todayClass != '') todayClass += " ";
+ todayClass += "selected";
+ }
+
var cell = quickElement('td', tableRow, '', 'class', todayClass);
quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));');
@@ -78,7 +101,7 @@ var CalendarNamespace = {
// Draw blanks after end of month (optional, but makes for valid code)
while (tableRow.childNodes.length < 7) {
var _cell = quickElement('td', tableRow, ' ');
- _cell.style.backgroundColor = '#f3f3f3';
+ _cell.className = "nonday";
}
calDiv.appendChild(calTable);
@@ -86,7 +109,7 @@ var CalendarNamespace = {
}
// Calendar -- A calendar instance
-function Calendar(div_id, callback) {
+function Calendar(div_id, callback, selected) {
// div_id (string) is the ID of the element in which the calendar will
// be displayed
// callback (string) is the name of a JavaScript function that will be
@@ -97,14 +120,22 @@ function Calendar(div_id, callback) {
this.today = new Date();
this.currentMonth = this.today.getMonth() + 1;
this.currentYear = this.today.getFullYear();
+ if (typeof selected != 'undefined') {
+ this.selected = selected;
+ }
}
Calendar.prototype = {
drawCurrent: function() {
- CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback);
+ CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected);
},
- drawDate: function(month, year) {
+ drawDate: function(month, year, selected) {
this.currentMonth = month;
this.currentYear = year;
+
+ if(selected) {
+ this.selected = selected;
+ }
+
this.drawCurrent();
},
drawPreviousMonth: function() {
View
6 django/contrib/admin/templates/admin/base.html
@@ -26,8 +26,10 @@
</div>
{% if user.is_active and user.is_staff %}
<div id="user-tools">
- {% trans 'Welcome,' %}
- <strong>{% firstof user.get_short_name user.get_username %}</strong>.
+ {% block welcome-msg %}
+ {% trans 'Welcome,' %}
+ <strong>{% firstof user.get_short_name user.get_username %}</strong>.
+ {% endblock %}
{% block userlinks %}
{% url 'django-admindocs-docroot' as docsroot %}
{% if docsroot %}
View
6 django/contrib/auth/hashers.py
@@ -192,7 +192,7 @@ def verify(self, password, encoded):
"""
Checks if the given password is correct
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BasePasswordHasher must provide a verify() method')
def encode(self, password, salt):
"""
@@ -201,7 +201,7 @@ def encode(self, password, salt):
The result is normally formatted as "algorithm$salt$hash" and
must be fewer than 128 characters.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BasePasswordHasher must provide an encode() method')
def safe_summary(self, encoded):
"""
@@ -210,7 +210,7 @@ def safe_summary(self, encoded):
The result is a dictionary and will be used where the password field
must be displayed to construct a safe representation of the password.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BasePasswordHasher must provide a safe_summary() method')
class PBKDF2PasswordHasher(BasePasswordHasher):
View
12 django/contrib/auth/models.py
@@ -245,10 +245,10 @@ def has_usable_password(self):
return is_password_usable(self.password)
def get_full_name(self):
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_full_name() method')
def get_short_name(self):
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of AbstractBaseUser must provide a get_short_name() method.')
# A few helper functions for common logic between User and AnonymousUser.
@@ -441,16 +441,16 @@ def __hash__(self):
return 1 # instances always return the same hash value
def save(self):
- raise NotImplementedError
+ raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
def delete(self):
- raise NotImplementedError
+ raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
def set_password(self, raw_password):
- raise NotImplementedError
+ raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
def check_password(self, raw_password):
- raise NotImplementedError
+ raise NotImplementedError("Django doesn't provide a DB representation for AnonymousUser.")
def _get_groups(self):
return self._groups
View
3  django/contrib/comments/templatetags/comments.py
@@ -112,7 +112,7 @@ def get_target_ctype_pk(self, context):
def get_context_value_from_queryset(self, context, qs):
"""Subclasses should override this."""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseCommentNode must provide a get_context_value_from_queryset() method')
class CommentListNode(BaseCommentNode):
"""Insert a list of comments into the context."""
@@ -338,4 +338,3 @@ def get_comment_permalink(comment, anchor_pattern=None):
if anchor_pattern:
return comment.get_absolute_url(anchor_pattern)
return comment.get_absolute_url()
-
View
10 django/contrib/gis/db/backends/base.py
@@ -101,7 +101,7 @@ def geo_db_type(self, f):
Returns the database column type for the geometry field on
the spatial backend.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseSpatialOperations must provide a geo_db_type() method')
def get_distance(self, f, value, lookup_type):
"""
@@ -117,7 +117,7 @@ def get_geom_placeholder(self, f, value):
stored procedure call to the transformation function of the spatial
backend.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseSpatialOperations must provide a geo_db_placeholder() method')
def get_expression_column(self, evaluator):
"""
@@ -134,14 +134,14 @@ def spatial_aggregate_sql(self, agg):
raise NotImplementedError('Aggregate support not implemented for this spatial backend.')
def spatial_lookup_sql(self, lvalue, lookup_type, value, field):
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseSpatialOperations must a provide spatial_lookup_sql() method')
# Routines for getting the OGC-compliant models.
def geometry_columns(self):
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseSpatialOperations must a provide geometry_columns() method')
def spatial_ref_sys(self):
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseSpatialOperations must a provide spatial_ref_sys() method')
@python_2_unicode_compatible
class SpatialRefSysMixin(object):
View
7 django/contrib/gis/db/backends/spatialite/introspection.py
@@ -25,9 +25,10 @@ def get_geometry_type(self, table_name, geo_col):
cursor = self.connection.cursor()
try:
# Querying the `geometry_columns` table to get additional metadata.
- cursor.execute('SELECT "coord_dimension", "srid", "type" '
- 'FROM "geometry_columns" '
- 'WHERE "f_table_name"=%s AND "f_geometry_column"=%s',
+ type_col = 'type' if self.connection.ops.spatial_version < (4, 0, 0) else 'geometry_type'
+ cursor.execute('SELECT coord_dimension, srid, %s '
+ 'FROM geometry_columns '
+ 'WHERE f_table_name=%%s AND f_geometry_column=%%s' % type_col,
(table_name, geo_col))
row = cursor.fetchone()
if not row:
View
1  django/contrib/gis/tests/inspectapp/models.py
@@ -9,5 +9,6 @@ class AllOGRFields(models.Model):
f_datetime = models.DateTimeField()
f_time = models.TimeField()
geom = models.PolygonField()
+ point = models.PointField()
objects = models.GeoManager()
View
18 django/contrib/gis/tests/inspectapp/tests.py
@@ -3,11 +3,13 @@
import os
from unittest import skipUnless
+from django.core.management import call_command
from django.db import connections
from django.test import TestCase
from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.geometry.test_data import TEST_DATA
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
+from django.utils.six import StringIO
if HAS_GDAL:
from django.contrib.gis.gdal import Driver
@@ -17,6 +19,22 @@
@skipUnless(HAS_GDAL and HAS_SPATIAL_DB, "GDAL and spatial db are required.")
+class InspectDbTests(TestCase):
+ def test_geom_columns(self):
+ """
+ Test the geo-enabled inspectdb command.
+ """
+ out = StringIO()
+ call_command('inspectdb',
+ table_name_filter=lambda tn:tn.startswith('inspectapp_'),
+ stdout=out)
+ output = out.getvalue()
+ self.assertIn('geom = models.PolygonField()', output)
+ self.assertIn('point = models.PointField()', output)
+ self.assertIn('objects = models.GeoManager()', output)
+
+
+@skipUnless(HAS_GDAL and HAS_SPATIAL_DB, "GDAL and spatial db are required.")
class OGRInspectTest(TestCase):
maxDiff = 1024
View
7 django/contrib/humanize/tests.py
@@ -14,10 +14,9 @@
from django.test import TestCase
from django.test.utils import override_settings
from django.utils.html import escape
-from django.utils.timezone import utc
+from django.utils.timezone import utc, get_fixed_timezone
from django.utils import translation
from django.utils.translation import ugettext as _
-from django.utils import tzinfo
from i18n import TransRealMixin
@@ -153,8 +152,8 @@ def test_naturalday(self):
def test_naturalday_tz(self):
today = datetime.date.today()
- tz_one = tzinfo.FixedOffset(datetime.timedelta(hours=-12))
- tz_two = tzinfo.FixedOffset(datetime.timedelta(hours=12))
+ tz_one = get_fixed_timezone(-720)
+ tz_two = get_fixed_timezone(720)
# Can be today or yesterday
date_one = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_one)
View
4 django/contrib/messages/storage/base.py
@@ -105,7 +105,7 @@ def _get(self, *args, **kwargs):
just containing no messages) then ``None`` should be returned in
place of ``messages``.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseStorage must provide a _get() method')
def _store(self, messages, response, *args, **kwargs):
"""
@@ -116,7 +116,7 @@ def _store(self, messages, response, *args, **kwargs):
**This method must be implemented by a subclass.**
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseStorage must provide a _store() method')
def _prepare_messages(self, messages):
"""
View
12 django/contrib/sessions/backends/base.py
@@ -284,7 +284,7 @@ def exists(self, session_key):
"""
Returns True if the given session_key already exists.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of SessionBase must provide an exists() method')
def create(self):
"""
@@ -292,7 +292,7 @@ def create(self):
a unique key and will have saved the result once (with empty data)
before the method returns.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of SessionBase must provide a create() method')
def save(self, must_create=False):
"""
@@ -300,20 +300,20 @@ def save(self, must_create=False):
is created (otherwise a CreateError exception is raised). Otherwise,
save() can update an existing object with the same key.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of SessionBase must provide a save() method')
def delete(self, session_key=None):
"""
Deletes the session data under this key. If the key is None, the
current session key value is used.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of SessionBase must provide a delete() method')
def load(self):
"""
Loads the session data and returns a dictionary.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of SessionBase must provide a load() method')
@classmethod
def clear_expired(cls):
@@ -324,4 +324,4 @@ def clear_expired(cls):
NotImplementedError. If it isn't necessary, because the backend has
a built-in expiration mechanism, it should be a no-op.
"""
- raise NotImplementedError
+ raise NotImplementedError('This backend does not support clear_expired().')
View
4 django/contrib/staticfiles/finders.py
@@ -28,7 +28,7 @@ def find(self, path, all=False):
the first found file path will be returned; if set
to ``True`` a list of all found files paths is returned.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseFinder must provide a find() method')
def list(self, ignore_patterns):
"""
@@ -36,7 +36,7 @@ def list(self, ignore_patterns):
a two item iterable consisting of the relative path and storage
instance.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseFinder must provide a list() method')
class FileSystemFinder(BaseFinder):
View
12 django/contrib/syndication/views.py
@@ -7,12 +7,12 @@
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.http import HttpResponse, Http404
from django.template import loader, TemplateDoesNotExist, RequestContext
-from django.utils import feedgenerator, tzinfo
+from django.utils import feedgenerator
from django.utils.encoding import force_text, iri_to_uri, smart_text
from django.utils.html import escape
from django.utils.http import http_date
from django.utils import six
-from django.utils.timezone import is_naive
+from django.utils.timezone import get_default_timezone, is_naive, make_aware
def add_domain(domain, url, secure=False):
@@ -186,15 +186,15 @@ def get_feed(self, obj, request):
else:
author_email = author_link = None
+ tz = get_default_timezone()
+
pubdate = self.__get_dynamic_attr('item_pubdate', item)
if pubdate and is_naive(pubdate):
- ltz = tzinfo.LocalTimezone(pubdate)
- pubdate = pubdate.replace(tzinfo=ltz)
+ pubdate = make_aware(pubdate, tz)
updateddate = self.__get_dynamic_attr('item_updateddate', item)
if updateddate and is_naive(updateddate):
- ltz = tzinfo.LocalTimezone(updateddate)
- updateddate = updateddate.replace(tzinfo=ltz)
+ updateddate = make_aware(updateddate, tz)
feed.add_item(
title = title,
View
10 django/core/cache/backends/base.py
@@ -96,27 +96,27 @@ def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
Returns True if the value was stored, False otherwise.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseCache must provide an add() method')
def get(self, key, default=None, version=None):
"""
Fetch a given key from the cache. If the key does not exist, return
default, which itself defaults to None.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseCache must provide a get() method')
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
"""
Set a value in the cache. If timeout is given, that timeout will be
used for the key; otherwise the default cache timeout will be used.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseCache must provide a set() method')
def delete(self, key, version=None):
"""
Delete a key from the cache, failing silently.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseCache must provide a delete() method')
def get_many(self, keys, version=None):
"""
@@ -190,7 +190,7 @@ def delete_many(self, keys, version=None):
def clear(self):
"""Remove *all* values from the cache at once."""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseCache must provide a clear() method')
def validate_key(self, key):
"""
View
17 django/core/files/storage.py
@@ -92,55 +92,55 @@ def delete(self, name):
"""
Deletes the specified file from the storage system.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Storage must provide a delete() method')
def exists(self, name):
"""
Returns True if a file referened by the given name already exists in the
storage system, or False if the name is available for a new file.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Storage must provide a exists() method')
def listdir(self, path):
"""
Lists the contents of the specified path, returning a 2-tuple of lists;
the first item being directories, the second item being files.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Storage must provide a listdir() method')
def size(self, name):
"""
Returns the total size, in bytes, of the file specified by name.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Storage must provide a size() method')
def url(self, name):
"""
Returns an absolute URL where the file's contents can be accessed
directly by a Web browser.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Storage must provide a url() method')
def accessed_time(self, name):
"""
Returns the last accessed time (as datetime object) of the file
specified by name.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Storage must provide an accessed_time() method')
def created_time(self, name):
"""
Returns the creation time (as datetime object) of the file
specified by name.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Storage must provide a created_time() method')
def modified_time(self, name):
"""
Returns the last modified time (as datetime object) of the file
specified by name.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Storage must provide a modified_time() method')
class FileSystemStorage(Storage):
"""
@@ -215,6 +215,7 @@ def _save(self, name, content):
_file = os.fdopen(fd, mode)
_file.write(chunk)
finally:
+ content.close()
locks.unlock(fd)
if _file is not None:
_file.close()
View
1  django/core/files/uploadedfile.py
@@ -46,6 +46,7 @@ def _set_name(self, name):
# File names longer than 255 characters can cause problems on older OSes.
if len(name) > 255:
name, ext = os.path.splitext(name)
+ ext = ext[:255]
name = name[:255 - len(ext)] + ext
self._name = name
View
4 django/core/files/uploadhandler.py
@@ -104,7 +104,7 @@ def receive_data_chunk(self, raw_data, start):
Receive data from the streamed upload parser. ``start`` is the position
in the file of the chunk.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of FileUploadHandler must provide a recieve_data_chunk() method')
def file_complete(self, file_size):
"""
@@ -113,7 +113,7 @@ def file_complete(self, file_size):
Subclasses should return a valid ``UploadedFile`` object.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of FileUploadHandler must provide a file_complete() method')
def upload_complete(self):
"""
View
2  django/core/mail/backends/base.py
@@ -36,4 +36,4 @@ def send_messages(self, email_messages):
Sends one or more EmailMessage objects and returns the number of email
messages sent.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseEmailBackend must override send_messages() method')
View
8 django/core/management/base.py
@@ -325,7 +325,7 @@ def handle(self, *args, **options):
this method.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseCommand must provide a handle() method')
class AppCommand(BaseCommand):
@@ -361,7 +361,7 @@ def handle_app(self, app, **options):
the command line.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of AppCommand must provide a handle_app() method')
class LabelCommand(BaseCommand):
@@ -397,7 +397,7 @@ def handle_label(self, label, **options):
string as given on the command line.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of LabelCommand must provide a handle_label() method')
class NoArgsCommand(BaseCommand):
@@ -423,4 +423,4 @@ def handle_noargs(self, **options):
Perform this command's actions.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of NoArgsCommand must provide a handle_noargs() method')
View
14 django/core/management/commands/inspectdb.py
@@ -104,8 +104,11 @@ def handle_inspection(self, options):
# Don't output 'id = meta.AutoField(primary_key=True)', because
# that's assumed if it doesn't exist.
- if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}:
- continue
+ if att_name == 'id' and extra_params == {'primary_key': True}:
+ if field_type == 'AutoField(':
+ continue
+ elif field_type == 'IntegerField(' and not connection.features.can_introspect_autofield:
+ comment_notes.append('AutoField?')
# Add 'null' and 'blank', if the 'null_ok' flag was present in the
# table description.
@@ -117,7 +120,12 @@ def handle_inspection(self, options):
if not field_type in ('TextField(', 'CharField('):
extra_params['null'] = True
- field_desc = '%s = models.%s' % (att_name, field_type)
+ field_desc = '%s = %s%s' % (
+ att_name,
+ # Custom fields will have a dotted path
+ '' if '.' in field_type else 'models.',
+ field_type,
+ )
if extra_params:
if not field_desc.endswith('('):
field_desc += ', '
View
12 django/core/serializers/base.py
@@ -65,7 +65,7 @@ def start_serialization(self):
"""
Called when serializing of the queryset starts.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of Serializer must provide a start_serialization() method')
def end_serialization(self):
"""
@@ -77,7 +77,7 @@ def start_object(self, obj):
"""
Called when serializing of an object starts.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of Serializer must provide a start_object() method')
def end_object(self, obj):
"""
@@ -89,19 +89,19 @@ def handle_field(self, obj, field):
"""
Called to handle each individual (non-relational) field on an object.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of Serializer must provide an handle_field() method')
def handle_fk_field(self, obj, field):
"""
Called to handle a ForeignKey field.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of Serializer must provide an handle_fk_field() method')
def handle_m2m_field(self, obj, field):
"""
Called to handle a ManyToManyField.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of Serializer must provide an handle_m2m_field() method')
def getvalue(self):
"""
@@ -135,7 +135,7 @@ def __iter__(self):
def __next__(self):
"""Iteration iterface -- return the next item in the stream"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of Deserializer must provide a __next__() method')
class DeserializedObject(object):
"""
View
47 django/db/backends/__init__.py
@@ -84,19 +84,19 @@ def __hash__(self):
def get_connection_params(self):
"""Returns a dict of parameters suitable for get_new_connection."""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a get_connection_params() method')
def get_new_connection(self, conn_params):
"""Opens a connection to the database."""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a get_new_connection() method')
def init_connection_state(self):
"""Initializes the database connection settings."""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an init_connection_state() method')
def create_cursor(self):
"""Creates a cursor. Assumes that a connection is established."""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a create_cursor() method')
##### Backend-specific methods for creating connections #####
@@ -262,7 +262,7 @@ def _set_autocommit(self, autocommit):
"""
Backend-specific implementation to enable or disable autocommit.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a _set_autocommit() method')
##### Generic transaction management methods #####
@@ -440,7 +440,7 @@ def is_usable(self):
Tests if the database connection is usable.
This function may assume that self.connection is not None.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require an is_usable() method')
def close_if_unusable_or_obsolete(self):
"""
@@ -519,11 +519,11 @@ def _start_transaction_under_autocommit(self):
"""
Only required when autocommits_when_autocommit_is_off = True.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a _start_transaction_under_autocommit() method')
def schema_editor(self, *args, **kwargs):
"Returns a new instance of this backend's SchemaEditor"
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseWrapper may require a schema_editor() method')
class BaseDatabaseFeatures(object):
@@ -627,6 +627,9 @@ class BaseDatabaseFeatures(object):
# which can't do it for MyISAM tables
can_introspect_foreign_keys = True
+ # Can the backend introspect an AutoField, instead of an IntegerField?
+ can_introspect_autofield = False
+
# Support for the DISTINCT ON clause
can_distinct_on_fields = False
@@ -741,13 +744,13 @@ def date_extract_sql(self, lookup_type, field_name):
Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
extracts a value from the given date field field_name.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseOperations may require a date_extract_sql() method')
def date_interval_sql(self, sql, connector, timedelta):
"""
Implements the date interval functionality for expressions
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseOperations may require a date_interval_sql() method')
def date_trunc_sql(self, lookup_type, field_name):
"""
@@ -755,7 +758,7 @@ def date_trunc_sql(self, lookup_type, field_name):
truncates the given date field field_name to a date object with only
the given specificity.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetrunc_sql() method')
def datetime_cast_sql(self):
"""
@@ -772,7 +775,7 @@ def datetime_extract_sql(self, lookup_type, field_name, tzname):
'second', returns the SQL that extracts a value from the given
datetime field field_name, and a tuple of parameters.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_extract_sql() method')
def datetime_trunc_sql(self, lookup_type, field_name, tzname):
"""
@@ -781,7 +784,7 @@ def datetime_trunc_sql(self, lookup_type, field_name, tzname):
field_name to a datetime object with only the given specificity, and
a tuple of parameters.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseOperations may require a datetime_trunk_sql() method')
def deferrable_sql(self):
"""
@@ -916,7 +919,7 @@ def no_limit_value(self):
Returns the value to use for the LIMIT when we are wanting "LIMIT
infinity". Returns None if the limit clause can be omitted in this case.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseOperations may require a no_limit_value() method')
def pk_default_value(self):
"""
@@ -956,7 +959,7 @@ def quote_name(self, name):
Returns a quoted version of the given table, index or column name. Does
not quote the given name if it's already been quoted.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseOperations may require a quote_name() method')
def quote_parameter(self, value):
"""
@@ -982,7 +985,7 @@ def regex_lookup(self, lookup_type):
If the feature is not supported (or part of it is not supported), a
NotImplementedError exception can be raised.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseOperations may require a regex_lookup() method')
def savepoint_create_sql(self, sid):
"""
@@ -1028,7 +1031,7 @@ def sql_flush(self, style, tables, sequences, allow_cascade=False):
to tables with foreign keys pointing the tables being truncated.
PostgreSQL requires a cascade even if these tables are empty.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseOperations must provide a sql_flush() method')
def sequence_reset_by_name_sql(self, style, sequences):
"""
@@ -1245,7 +1248,7 @@ def get_table_list(self, cursor):
Returns an unsorted list of names of all tables that exist in the
database.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_table_list() method')
def django_table_names(self, only_existing=False):
"""
@@ -1322,7 +1325,7 @@ def get_key_columns(self, cursor, table_name):
Backends can override this to return a list of (column_name, referenced_table_name,
referenced_column_name) for all key columns in given table.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_key_columns() method')
def get_primary_key_column(self, cursor, table_name):
"""
@@ -1342,7 +1345,7 @@ def get_indexes(self, cursor, table_name):
Only single-column indexes are introspected.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_indexes() method')
def get_constraints(self, cursor, table_name):
"""
@@ -1361,7 +1364,7 @@ def get_constraints(self, cursor, table_name):
Some backends may return special constraint names that don't exist
if they don't name constraints of a certain type (e.g. SQLite)
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_constraints() method')
class BaseDatabaseClient(object):
@@ -1378,7 +1381,7 @@ def __init__(self, connection):
self.connection = connection
def runshell(self):
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseClient must provide a runshell() method')
class BaseDatabaseValidation(object):
View
1  django/db/backends/oracle/creation.py
@@ -181,6 +181,7 @@ def _create_test_user(self, cursor, parameters, verbosity):
IDENTIFIED BY %(password)s
DEFAULT TABLESPACE %(tblspace)s
TEMPORARY TABLESPACE %(tblspace_temp)s
+ QUOTA UNLIMITED ON %(tblspace)s
""",
"""GRANT CONNECT, RESOURCE TO %(user)s""",
]
View
2  django/db/backends/schema.py
@@ -148,7 +148,7 @@ def prepare_default(self, value):
"""
Only used for backends which have requires_literal_defaults feature
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of BaseDatabaseSchemaEditor for backends which have requires_literal_defaults must provide a prepare_default() method')
def effective_default(self, field):
"""
View
6 django/db/migrations/operations/base.py
@@ -38,14 +38,14 @@ def state_forwards(self, app_label, state):
Takes the state from the previous migration, and mutates it
so that it matches what this migration would perform.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Operation must provide a state_forwards() method')
def database_forwards(self, app_label, schema_editor, from_state, to_state):
"""
Performs the mutation on the database schema in the normal
(forwards) direction.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Operation must provide a database_forwards() method')
def database_backwards(self, app_label, schema_editor, from_state, to_state):
"""
@@ -53,7 +53,7 @@ def database_backwards(self, app_label, schema_editor, from_state, to_state):
direction - e.g. if this were CreateModel, it would in fact
drop the model's table.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Operation must provide a database_backwards() method')
def describe(self):
"""
View
40 django/db/migrations/writer.py
@@ -74,6 +74,26 @@ def path(self):
return os.path.join(basedir, self.filename)
@classmethod
+ def serialize_deconstructed(cls, path, args, kwargs):
+ module, name = path.rsplit(".", 1)
+ if module == "django.db.models":
+ imports = set(["from django.db import models"])
+ name = "models.%s" % name
+ else:
+ imports = set(["import %s" % module])
+ name = path
+ arg_strings = []
+ for arg in args:
+ arg_string, arg_imports = cls.serialize(arg)
+ arg_strings.append(arg_string)
+ imports.update(arg_imports)
+ for kw, arg in kwargs.items():
+ arg_string, arg_imports = cls.serialize(arg)
+ imports.update(arg_imports)
+ arg_strings.append("%s=%s" % (kw, arg_string))
+ return "%s(%s)" % (name, ", ".join(arg_strings)), imports
+
+ @classmethod
def serialize(cls, value):
"""
Serializes the value to a string that's parsable by Python, along
@@ -119,23 +139,7 @@ def serialize(cls, value):
# Django fields
elif isinstance(value, models.Field):
attr_name, path, args, kwargs = value.deconstruct()
- module, name = path.rsplit(".", 1)
- if module == "django.db.models":
- imports = set(["from django.db import models"])
- name = "models.%s" % name
- else:
- imports = set(["import %s" % module])
- name = path
- arg_strings = []
- for arg in args:
- arg_string, arg_imports = cls.serialize(arg)
- arg_strings.append(arg_string)
- imports.update(arg_imports)
- for kw, arg in kwargs.items():
- arg_string, arg_imports = cls.serialize(arg)
- imports.update(arg_imports)
- arg_strings.append("%s=%s" % (kw, arg_string))
- return "%s(%s)" % (name, ", ".join(arg_strings)), imports
+ return cls.serialize_deconstructed(path, args, kwargs)
# Functions
elif isinstance(value, (types.FunctionType, types.BuiltinFunctionType)):
# Special-cases, as these don't have im_class
@@ -152,6 +156,8 @@ def serialize(cls, value):
klass = value.im_class
module = klass.__module__
return "%s.%s.%s" % (module, klass.__name__, value.__name__), set(["import %s" % module])
+ elif hasattr(value, 'deconstruct'):
+ return cls.serialize_deconstructed(*value.deconstruct())
elif value.__name__ == '<lambda>':
raise ValueError("Cannot serialize function: lambda")
elif value.__module__ is None:
View
4 django/db/models/deletion.py
@@ -35,10 +35,12 @@ def set_on_delete(collector, field, sub_objs, using):
else:
def set_on_delete(collector, field, sub_objs, using):
collector.add_field_update(field, value, sub_objs)
+ set_on_delete.deconstruct = lambda: ('django.db.models.SET', (value,), {})
return set_on_delete
-SET_NULL = SET(None)
+def SET_NULL(collector, field, sub_objs, using):
+ collector.add_field_update(field, None, sub_objs)
def SET_DEFAULT(collector, field, sub_objs, using):
View
4 django/db/models/sql/compiler.py
@@ -391,7 +391,7 @@ def get_ordering(self):
if not distinct or elt in select_aliases:
result.append('%s %s' % (elt, order))
group_by.append((elt, []))
- elif get_order_dir(field)[0] not in self.query.extra:
+ elif not self.query._extra or get_order_dir(field)[0] not in self.query._extra:
# 'col' is of the form 'field' or 'field1__field2' or
# '-field1__field2__field', etc.
for table, cols, order in self.find_ordering_name(field,
@@ -987,7 +987,7 @@ def pre_sql_setup(self):
# We need to use a sub-select in the where clause to filter on things
# from other tables.
query = self.query.clone(klass=Query)
- query.extra = {}
+ query._extra = {}
query.select = []
query.add_fields([query.get_meta().pk.name])
# Recheck the count - it is possible that fiddling with the select
View
65 django/db/models/sql/query.py
@@ -143,7 +143,10 @@ def __init__(self, model, where=WhereNode):
self.select_related = False
# SQL aggregate-related attributes
- self.aggregates = OrderedDict() # Maps alias -> SQL aggregate function
+ # The _aggregates will be an OrderedDict when used. Due to the cost
+ # of creating OrderedDict this attribute is created lazily (in
+ # self.aggregates property).
+ self._aggregates = None # Maps alias -> SQL aggregate function
self.aggregate_select_mask = None
self._aggregate_select_cache = None
@@ -153,7 +156,9 @@ def __init__(self, model, where=WhereNode):
# These are for extensions. The contents are more or less appended
# verbatim to the appropriate clause.
- self.extra = OrderedDict() # Maps col_alias -> (col_sql, params).
+ # The _extra attribute is an OrderedDict, lazily created similarly to
+ # .aggregates
+ self._extra = None # Maps col_alias -> (col_sql, params).
self.extra_select_mask = None
self._extra_select_cache = None
@@ -165,6 +170,18 @@ def __init__(self, model, where=WhereNode):
# load.
self.deferred_loading = (set(), True)
+ @property
+ def extra(self):
+ if self._extra is None:
+ self._extra = OrderedDict()
+ return self._extra
+
+ @property
+ def aggregates(self):
+ if self._aggregates is None:
+ self._aggregates = OrderedDict()
+ return self._aggregates
+
def __str__(self):
"""
Returns the query as a string of SQL with the parameter values
@@ -245,7 +262,7 @@ def clone(self, klass=None, memo=None, **kwargs):
obj.select_for_update_nowait = self.select_for_update_nowait
obj.select_related = self.select_related
obj.related_select_cols = []
- obj.aggregates = self.aggregates.copy()
+ obj._aggregates = self._aggregates.copy() if self._aggregates is not None else None
if self.aggregate_select_mask is None:
obj.aggregate_select_mask = None
else:
@@ -257,7 +274,7 @@ def clone(self, klass=None, memo=None, **kwargs):
# used.
obj._aggregate_select_cache = None
obj.max_depth = self.max_depth
- obj.extra = self.extra.copy()
+ obj._extra = self._extra.copy() if self._extra is not None else None
if self.extra_select_mask is None:
obj.extra_select_mask = None
else:
@@ -344,7 +361,7 @@ def get_aggregation(self, using, force_subq=False):
# and move them to the outer AggregateQuery.
for alias, aggregate in self.aggregate_select.items():
if aggregate.is_summary:
- query.aggregate_select[alias] = aggregate.relabeled_clone(relabels)
+ query.aggregates[alias] = aggregate.relabeled_clone(relabels)
del obj.aggregate_select[alias]
try:
@@ -358,7 +375,7 @@ def get_aggregation(self, using, force_subq=False):
query = self
self.select = []
self.default_cols = False
- self.extra = {}
+ self._extra = {}
self.remove_inherited_models()
query.clear_ordering(True)
@@ -527,7 +544,7 @@ def combine(self, rhs, connector):
# It would be nice to be able to handle this, but the queries don't
# really make sense (or return consistent value sets). Not worth
# the extra complexity when you can write a real query instead.
- if self.extra and rhs.extra:
+ if self._extra and rhs._extra:
raise ValueError("When merging querysets using 'or', you "
"cannot have extra(select=...) on both sides.")
self.extra.update(rhs.extra)
@@ -756,8 +773,9 @@ def relabel_column(col):
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 = OrderedDict(
- (key, relabel_column(col)) for key, col in self.aggregates.items())
+ if self._aggregates:
+ self._aggregates = OrderedDict(
+ (key, relabel_column(col)) for key, col in self._aggregates.items())
# 2. Rename the alias in the internal table/alias datastructures.
for ident, aliases in self.join_map.items():
@@ -967,7 +985,7 @@ def add_aggregate(self, aggregate, model, alias, is_summary):
"""
opts = model._meta
field_list = aggregate.lookup.split(LOOKUP_SEP)
- if len(field_list) == 1 and aggregate.lookup in self.aggregates:
+ if len(field_list) == 1 and self._aggregates and aggregate.lookup in self.aggregates:
# Aggregate is over an annotation
field_name = field_list[0]
col = field_name
@@ -1049,7 +1067,7 @@ def solve_lookup_type(self, lookup):
lookup_parts = lookup.split(LOOKUP_SEP)
num_parts = len(lookup_parts)
if (len(lookup_parts) > 1 and lookup_parts[-1] in self.query_terms
- and lookup not in self.aggregates):
+ and (not self._aggregates or lookup not in self._aggregates)):
# Traverse the lookup query to distinguish related fields from
# lookup types.
lookup_model = self.model
@@ -1108,10 +1126,11 @@ def build_filter(self, filter_expr, branch_negated=False, current_negated=False,
value, lookup_type = self.prepare_lookup_value(value, lookup_type, can_reuse)
clause = self.where_class()
- for alias, aggregate in self.aggregates.items():
- if alias in (parts[0], LOOKUP_SEP.join(parts)):
- clause.add((aggregate, lookup_type, value), AND)
- return clause
+ if self._aggregates:
+ for alias, aggregate in self.aggregates.items():
+ if alias in (parts[0], LOOKUP_SEP.join(parts)):
+ clause.add((aggregate, lookup_type, value), AND)
+ return clause
opts = self.get_meta()
alias = self.get_initial_alias()
@@ -1170,6 +1189,8 @@ def need_having(self, obj):
Returns whether or not all elements of this q_object need to be put
together in the HAVING clause.
"""
+ if not self._aggregates:
+ return False
if not isinstance(obj, Node):
return (refs_aggregate(obj[0].split(LOOKUP_SEP), self.aggregates)
or (hasattr(obj[1], 'contains_aggregate')
@@ -1632,7 +1653,7 @@ def add_count_column(self):
# Set only aggregate to be the count column.
# Clear out the select cache to reflect the new unmasked aggregates.
- self.aggregates = {None: count}
+ self._aggregates = {None: count}
self.set_aggregate_mask(None)
self.group_by = None
@@ -1781,7 +1802,8 @@ def set_extra_mask(self, names):
self.extra_select_mask = set(names)
self._extra_select_cache = None
- def _aggregate_select(self):
+ @property
+ def aggregate_select(self):
"""The OrderedDict of aggregate columns that are not masked, and should
be used in the SELECT clause.
@@ -1789,6 +1811,8 @@ def _aggregate_select(self):
"""
if self._aggregate_select_cache is not None:
return self._aggregate_select_cache
+ elif not self._aggregates:
+ return {}
elif self.aggregate_select_mask is not None:
self._aggregate_select_cache = OrderedDict(
(k, v) for k, v in self.aggregates.items()
@@ -1797,11 +1821,13 @@ def _aggregate_select(self):
return self._aggregate_select_cache
else:
return self.aggregates
- aggregate_select = property(_aggregate_select)
- def _extra_select(self):
+ @property
+ def extra_select(self):
if self._extra_select_cache is not None:
return self._extra_select_cache
+ if not self._extra:
+ return {}
elif self.extra_select_mask is not None:
self._extra_select_cache = OrderedDict(
(k, v) for k, v in self.extra.items()
@@ -1810,7 +1836,6 @@ def _extra_select(self):
return self._extra_select_cache
else:
return self.extra
- extra_select = property(_extra_select)
def trim_start(self, names_with_path):
"""
View
5 django/forms/fields.py
@@ -977,6 +977,11 @@ def __init__(self, fields=(), *args, **kwargs):
f.required = False
self.fields = fields
+ def __deepcopy__(self, memo):
+ result = super(MultiValueField, self).__deepcopy__(memo)
+ result.fields = tuple([x.__deepcopy__(memo) for x in self.fields])
+ return result
+
def validate(self, value):
pass
View
3  django/forms/forms.py
@@ -185,7 +185,8 @@ def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_
'label': force_text(label),
'field': six.text_type(bf),
'help_text': help_text,
- 'html_class_attr': html_class_attr
+ 'html_class_attr': html_class_attr,
+ 'field_name': bf.html_name,
})
if top_errors:
View
17 django/forms/util.py
@@ -23,11 +23,18 @@ def flatatt(attrs):
The result is passed through 'mark_safe'.
"""
- if [v for v in attrs.values() if v is True or v is False]:
- warnings.warn(
- 'The meaning of boolean values for widget attributes will change in Django 1.8',
- DeprecationWarning
- )
+ for attr_name, value in attrs.items():
+ if type(value) is bool:
+ warnings.warn(
+ "In Django 1.8, widget attribute %(attr_name)s=%(bool_value)s "
+ "will %(action)s. To preserve current behavior, use the "
+ "string '%(bool_value)s' instead of the boolean value." % {
+ 'attr_name': attr_name,
+ 'action': "be rendered as '%s'" % attr_name if value else "not be rendered",
+ 'bool_value': value,
+ },
+ DeprecationWarning
+ )
return format_html_join('', ' {0}="{1}"', sorted(attrs.items()))
@python_2_unicode_compatible
View
2  django/forms/widgets.py
@@ -190,7 +190,7 @@ def render(self, name, value, attrs=None):
The 'value' given is not guaranteed to be valid input, so subclass
implementations should program defensively.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of Widget must provide a render() method')
def build_attrs(self, extra_attrs=None, **kwargs):
"Helper function for building an attribute dictionary."
View
2  django/http/cookie.py
@@ -64,6 +64,8 @@ def _BaseCookie__set(self, key, real_value, coded_value):
M.set(key, real_value, coded_value)
dict.__setitem__(self, key, M)
except http_cookies.CookieError:
+ if not hasattr(self, 'bad_cookies'):
+ self.bad_cookies = set()
self.bad_cookies.add(key)
dict.__setitem__(self, key, http_cookies.Morsel())
View
3  django/http/response.py
@@ -2,6 +2,7 @@
import datetime
import time
+import sys
from email.header import Header
try:
from urllib.parse import urlparse
@@ -160,7 +161,7 @@ def _convert_to_charset(self, value, charset, mime_encode=False):
except UnicodeError as e:
if mime_encode:
# Wrapping in str() is a workaround for #12422 under Python 2.
- value = str(Header(value, 'utf-8').encode())
+ value = str(Header(value, 'utf-8', maxlinelen=sys.maxsize).encode())
else:
e.reason += ', HTTP response headers must be in %s format' % charset
raise
View
35 django/template/base.py
@@ -99,7 +99,7 @@ def __init__(self, name):
self.name = name
def reload(self):
- raise NotImplementedError
+ raise NotImplementedError('subclasses of Origin must provide a reload() method')
def __str__(self):
return self.name
@@ -385,7 +385,7 @@ def top(self):
"""
Overload this method to do the actual parsing and return the result.
"""
- raise NotImplementedError()
+ raise NotImplementedError('subclasses of Tokenparser must provide a top() method')
def more(self):
"""
@@ -622,34 +622,17 @@ def resolve(self, context, ignore_failures=False):
def args_check(name, func, provided):
provided = list(provided)
- plen = len(provided)
+ # First argument, filter input, is implied.
+ plen = len(provided) + 1
# Check to see if a decorator is providing the real function.
func = getattr(func, '_decorated_function', func)
args, varargs, varkw, defaults = getargspec(func)
- # First argument is filter input.
- args.pop(0)
- if defaults:
- nondefs = args[:-len(defaults)]
- else:
- nondefs = args
- # Args without defaults must be provided.
- try:
- for arg in nondefs:
- provided.pop(0)
- except IndexError:
- # Not enough
- raise TemplateSyntaxError("%s requires %d arguments, %d provided" %
- (name, len(nondefs), plen))
-
- # Defaults can be overridden.
- defaults = list(defaults) if defaults else []
- try:
- for parg in provided:
- defaults.pop(0)
- except IndexError:
- # Too many.
+ alen = len(args)
+ dlen = len(defaults or [])
+ # Not enough OR Too many
+ if plen < (alen - dlen) or plen > alen:
raise TemplateSyntaxError("%s requires %d arguments, %d provided" %
- (name, len(nondefs), plen))
+ (name, alen - dlen, plen))
return True
args_check = staticmethod(args_check)
View
2  django/template/defaulttags.py
@@ -1,6 +1,7 @@
"""Default tags used by the template system, available to all templates."""
from __future__ import unicode_literals
+import os
import sys
import re
from datetime import datetime
@@ -328,6 +329,7 @@ def render(self, context):
return ''
def include_is_allowed(filepath):
+ filepath = os.path.abspath(filepath)
for root in settings.ALLOWED_INCLUDE_ROOTS:
if filepath.startswith(root):
return True
View
2  django/template/loader.py
@@ -61,7 +61,7 @@ def load_template_source(self, template_name, template_dirs=None):
name.
"""
- raise NotImplementedError
+ raise NotImplementedError('subclasses of BaseLoader must provide a load_template_source() method')
def reset(self):
"""