Permalink
Browse files

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

  • Loading branch information...
2 parents 496b4d0 + ff723d8 commit 5c645ec81a872116533ee6d3928b750d3737b68a @alex alex committed Sep 14, 2013
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
@@ -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>
@@ -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)
@@ -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]
@@ -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.
@@ -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);
}
}
@@ -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,15 +101,15 @@ 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);
}
}
// 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() {
@@ -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 %}
@@ -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):
@@ -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
Oops, something went wrong. Retry.

0 comments on commit 5c645ec

Please sign in to comment.