mirror of
				https://github.com/django/django.git
				synced 2025-10-31 03:54:51 +00:00 
			
		
		
		
	Merged recent trunk changes.
This commit is contained in:
		
						commit
						531e7715da
					
				
					 18 changed files with 144 additions and 111 deletions
				
			
		|  | @ -43,13 +43,28 @@ class LazySettings(LazyObject): | ||||||
|                 % (name, ENVIRONMENT_VARIABLE)) |                 % (name, ENVIRONMENT_VARIABLE)) | ||||||
| 
 | 
 | ||||||
|         self._wrapped = Settings(settings_module) |         self._wrapped = Settings(settings_module) | ||||||
| 
 |         self._configure_logging() | ||||||
| 
 | 
 | ||||||
|     def __getattr__(self, name): |     def __getattr__(self, name): | ||||||
|         if self._wrapped is empty: |         if self._wrapped is empty: | ||||||
|             self._setup(name) |             self._setup(name) | ||||||
|         return getattr(self._wrapped, name) |         return getattr(self._wrapped, name) | ||||||
| 
 | 
 | ||||||
|  |     def _configure_logging(self): | ||||||
|  |         """ | ||||||
|  |         Setup logging from LOGGING_CONFIG and LOGGING settings. | ||||||
|  |         """ | ||||||
|  |         if self.LOGGING_CONFIG: | ||||||
|  |             # First find the logging configuration function ... | ||||||
|  |             logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1) | ||||||
|  |             logging_config_module = importlib.import_module(logging_config_path) | ||||||
|  |             logging_config_func = getattr(logging_config_module, logging_config_func_name) | ||||||
|  | 
 | ||||||
|  |             # Backwards-compatibility shim for #16288 fix | ||||||
|  |             compat_patch_logging_config(self.LOGGING) | ||||||
|  | 
 | ||||||
|  |             # ... then invoke it with the logging settings | ||||||
|  |             logging_config_func(self.LOGGING) | ||||||
| 
 | 
 | ||||||
|     def configure(self, default_settings=global_settings, **options): |     def configure(self, default_settings=global_settings, **options): | ||||||
|         """ |         """ | ||||||
|  | @ -133,19 +148,6 @@ class Settings(BaseSettings): | ||||||
|             os.environ['TZ'] = self.TIME_ZONE |             os.environ['TZ'] = self.TIME_ZONE | ||||||
|             time.tzset() |             time.tzset() | ||||||
| 
 | 
 | ||||||
|         # Settings are configured, so we can set up the logger if required |  | ||||||
|         if self.LOGGING_CONFIG: |  | ||||||
|             # First find the logging configuration function ... |  | ||||||
|             logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1) |  | ||||||
|             logging_config_module = importlib.import_module(logging_config_path) |  | ||||||
|             logging_config_func = getattr(logging_config_module, logging_config_func_name) |  | ||||||
| 
 |  | ||||||
|             # Backwards-compatibility shim for #16288 fix |  | ||||||
|             compat_patch_logging_config(self.LOGGING) |  | ||||||
| 
 |  | ||||||
|             # ... then invoke it with the logging settings |  | ||||||
|             logging_config_func(self.LOGGING) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| class UserSettingsHolder(BaseSettings): | class UserSettingsHolder(BaseSettings): | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ | ||||||
| {% if change %}{% if not is_popup %} | {% if change %}{% if not is_popup %} | ||||||
|   <ul class="object-tools"> |   <ul class="object-tools"> | ||||||
|     {% block object-tools-items %} |     {% block object-tools-items %} | ||||||
|     <li><a href="{% url opts|admin_urlname:'history' original.pk %}" class="historylink">{% trans "History" %}</a></li> |     <li><a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% trans "History" %}</a></li> | ||||||
|     {% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%} |     {% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%} | ||||||
|     {% endblock %} |     {% endblock %} | ||||||
|   </ul> |   </ul> | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| {% load i18n %} | {% load i18n admin_urls %} | ||||||
| <div class="submit-row"> | <div class="submit-row"> | ||||||
| {% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" {{ onclick_attrib }}/>{% endif %} | {% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" {{ onclick_attrib }}/>{% endif %} | ||||||
| {% if show_delete_link %}<p class="deletelink-box"><a href="delete/" class="deletelink">{% trans "Delete" %}</a></p>{% endif %} | {% if show_delete_link %}<p class="deletelink-box"><a href="{% url opts|admin_urlname:'delete' original.pk|admin_urlquote %}" class="deletelink">{% trans "Delete" %}</a></p>{% endif %} | ||||||
| {% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" {{ onclick_attrib }}/>{%endif%} | {% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" {{ onclick_attrib }}/>{%endif%} | ||||||
| {% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" {{ onclick_attrib }} />{% endif %} | {% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" {{ onclick_attrib }}/>{% endif %} | ||||||
| {% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" {{ onclick_attrib }}/>{% endif %} | {% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" {{ onclick_attrib }}/>{% endif %} | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | @ -28,7 +28,8 @@ def submit_row(context): | ||||||
|     change = context['change'] |     change = context['change'] | ||||||
|     is_popup = context['is_popup'] |     is_popup = context['is_popup'] | ||||||
|     save_as = context['save_as'] |     save_as = context['save_as'] | ||||||
|     return { |     ctx = { | ||||||
|  |         'opts': opts, | ||||||
|         'onclick_attrib': (opts.get_ordered_objects() and change |         'onclick_attrib': (opts.get_ordered_objects() and change | ||||||
|                             and 'onclick="submitOrderForm();"' or ''), |                             and 'onclick="submitOrderForm();"' or ''), | ||||||
|         'show_delete_link': (not is_popup and context['has_delete_permission'] |         'show_delete_link': (not is_popup and context['has_delete_permission'] | ||||||
|  | @ -40,6 +41,9 @@ def submit_row(context): | ||||||
|         'is_popup': is_popup, |         'is_popup': is_popup, | ||||||
|         'show_save': True |         'show_save': True | ||||||
|     } |     } | ||||||
|  |     if context.get('original') is not None: | ||||||
|  |         ctx['original'] = context['original'] | ||||||
|  |     return ctx | ||||||
| 
 | 
 | ||||||
| @register.filter | @register.filter | ||||||
| def cell_count(inline_admin_form): | def cell_count(inline_admin_form): | ||||||
|  |  | ||||||
|  | @ -48,9 +48,9 @@ def prepare_lookup_value(key, value): | ||||||
| def quote(s): | def quote(s): | ||||||
|     """ |     """ | ||||||
|     Ensure that primary key values do not confuse the admin URLs by escaping |     Ensure that primary key values do not confuse the admin URLs by escaping | ||||||
|     any '/', '_' and ':' characters. Similar to urllib.quote, except that the |     any '/', '_' and ':' and similarly problematic characters. | ||||||
|     quoting is slightly different so that it doesn't get automatically |     Similar to urllib.quote, except that the quoting is slightly different so | ||||||
|     unquoted by the Web browser. |     that it doesn't get automatically unquoted by the Web browser. | ||||||
|     """ |     """ | ||||||
|     if not isinstance(s, six.string_types): |     if not isinstance(s, six.string_types): | ||||||
|         return s |         return s | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ from functools import reduce | ||||||
| 
 | 
 | ||||||
| from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured | from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured | ||||||
| from django.core.paginator import InvalidPage | from django.core.paginator import InvalidPage | ||||||
|  | from django.core.urlresolvers import reverse | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.db.models.fields import FieldDoesNotExist | from django.db.models.fields import FieldDoesNotExist | ||||||
| from django.utils.datastructures import SortedDict | from django.utils.datastructures import SortedDict | ||||||
|  | @ -376,4 +377,8 @@ class ChangeList(object): | ||||||
|             return qs |             return qs | ||||||
| 
 | 
 | ||||||
|     def url_for_result(self, result): |     def url_for_result(self, result): | ||||||
|         return "%s/" % quote(getattr(result, self.pk_attname)) |         pk = getattr(result, self.pk_attname) | ||||||
|  |         return reverse('admin:%s_%s_change' % (self.opts.app_label, | ||||||
|  |                                                self.opts.module_name), | ||||||
|  |                        args=(quote(pk),), | ||||||
|  |                        current_app=self.model_admin.admin_site.name) | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ from django.conf import settings | ||||||
| from django.contrib.formtools import preview, utils | from django.contrib.formtools import preview, utils | ||||||
| from django.contrib.formtools.wizard import FormWizard | from django.contrib.formtools.wizard import FormWizard | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
|  | from django.test.html import parse_html | ||||||
| from django.test.utils import override_settings | from django.test.utils import override_settings | ||||||
| from django.utils import unittest | from django.utils import unittest | ||||||
| 
 | 
 | ||||||
|  | @ -218,7 +219,6 @@ class DummyRequest(http.HttpRequest): | ||||||
| ) | ) | ||||||
| class WizardTests(TestCase): | class WizardTests(TestCase): | ||||||
|     urls = 'django.contrib.formtools.tests.urls' |     urls = 'django.contrib.formtools.tests.urls' | ||||||
|     input_re = re.compile('name="([^"]+)" value="([^"]+)"') |  | ||||||
|     wizard_step_data = ( |     wizard_step_data = ( | ||||||
|         { |         { | ||||||
|             '0-name': 'Pony', |             '0-name': 'Pony', | ||||||
|  | @ -409,14 +409,13 @@ class WizardTests(TestCase): | ||||||
|         """ |         """ | ||||||
|         Pull the appropriate field data from the context to pass to the next wizard step |         Pull the appropriate field data from the context to pass to the next wizard step | ||||||
|         """ |         """ | ||||||
|         previous_fields = response.context['previous_fields'] |         previous_fields = parse_html(response.context['previous_fields']) | ||||||
|         fields = {'wizard_step': response.context['step0']} |         fields = {'wizard_step': response.context['step0']} | ||||||
| 
 | 
 | ||||||
|         def grab(m): |         for input_field in previous_fields: | ||||||
|             fields[m.group(1)] = m.group(2) |             input_attrs = dict(input_field.attributes) | ||||||
|             return '' |             fields[input_attrs["name"]] = input_attrs["value"] | ||||||
| 
 | 
 | ||||||
|         self.input_re.sub(grab, previous_fields) |  | ||||||
|         return fields |         return fields | ||||||
| 
 | 
 | ||||||
|     def check_wizard_step(self, response, step_no): |     def check_wizard_step(self, response, step_no): | ||||||
|  | @ -428,7 +427,6 @@ class WizardTests(TestCase): | ||||||
|         """ |         """ | ||||||
|         step_count = len(self.wizard_step_data) |         step_count = len(self.wizard_step_data) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(response.status_code, 200) |  | ||||||
|         self.assertContains(response, 'Step %d of %d' % (step_no, step_count)) |         self.assertContains(response, 'Step %d of %d' % (step_no, step_count)) | ||||||
| 
 | 
 | ||||||
|         data = self.grab_field_data(response) |         data = self.grab_field_data(response) | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | import json | ||||||
|  | 
 | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.core import signing | from django.core import signing | ||||||
| from django.core.exceptions import SuspiciousOperation | from django.core.exceptions import SuspiciousOperation | ||||||
|  | @ -41,4 +43,5 @@ class TestCookieStorage(TestStorage, TestCase): | ||||||
|         storage.init_data() |         storage.init_data() | ||||||
|         storage.update_response(response) |         storage.update_response(response) | ||||||
|         unsigned_cookie_data = cookie_signer.unsign(response.cookies[storage.prefix].value) |         unsigned_cookie_data = cookie_signer.unsign(response.cookies[storage.prefix].value) | ||||||
|         self.assertEqual(unsigned_cookie_data, '{"step_files":{},"step":null,"extra_data":{},"step_data":{}}') |         self.assertEqual(json.loads(unsigned_cookie_data), | ||||||
|  |             {"step_files": {}, "step": None, "extra_data": {}, "step_data": {}}) | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ try: | ||||||
|     from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform |     from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform | ||||||
|     from django.contrib.gis.gdal.geometries import OGRGeometry |     from django.contrib.gis.gdal.geometries import OGRGeometry | ||||||
|     HAS_GDAL = True |     HAS_GDAL = True | ||||||
| except ImportError: | except Exception: | ||||||
|     HAS_GDAL = False |     HAS_GDAL = False | ||||||
| 
 | 
 | ||||||
| try: | try: | ||||||
|  |  | ||||||
|  | @ -286,7 +286,7 @@ cache is multi-process and thread-safe. To use it, set | ||||||
| 
 | 
 | ||||||
| The cache :setting:`LOCATION <CACHES-LOCATION>` is used to identify individual | The cache :setting:`LOCATION <CACHES-LOCATION>` is used to identify individual | ||||||
| memory stores. If you only have one locmem cache, you can omit the | memory stores. If you only have one locmem cache, you can omit the | ||||||
| :setting:`LOCATION <CACHES-LOCATION>`; however, if you have more that one local | :setting:`LOCATION <CACHES-LOCATION>`; however, if you have more than one local | ||||||
| memory cache, you will need to assign a name to at least one of them in | memory cache, you will need to assign a name to at least one of them in | ||||||
| order to keep them separate. | order to keep them separate. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -345,36 +345,6 @@ This logging configuration does the following things: | ||||||
|     printed to the console; ``ERROR`` and ``CRITICAL`` |     printed to the console; ``ERROR`` and ``CRITICAL`` | ||||||
|     messages will also be output via email. |     messages will also be output via email. | ||||||
| 
 | 
 | ||||||
| .. admonition:: Custom handlers and circular imports |  | ||||||
| 
 |  | ||||||
|     If your ``settings.py`` specifies a custom handler class and the file |  | ||||||
|     defining that class also imports ``settings.py`` a circular import will |  | ||||||
|     occur. |  | ||||||
| 
 |  | ||||||
|     For example, if ``settings.py`` contains the following config for |  | ||||||
|     :setting:`LOGGING`:: |  | ||||||
| 
 |  | ||||||
|         LOGGING = { |  | ||||||
|           'version': 1, |  | ||||||
|           'handlers': { |  | ||||||
|             'custom_handler': { |  | ||||||
|               'level': 'INFO', |  | ||||||
|               'class': 'myproject.logconfig.MyHandler', |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     and ``myproject/logconfig.py`` has the following line before the |  | ||||||
|     ``MyHandler`` definition:: |  | ||||||
| 
 |  | ||||||
|         from django.conf import settings |  | ||||||
| 
 |  | ||||||
|     then the ``dictconfig`` module will raise an exception like the following:: |  | ||||||
| 
 |  | ||||||
|         ValueError: Unable to configure handler 'custom_handler': |  | ||||||
|         Unable to configure handler 'custom_handler': |  | ||||||
|         'module' object has no attribute 'logconfig' |  | ||||||
| 
 |  | ||||||
| .. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects | .. _formatter documentation: http://docs.python.org/library/logging.html#formatter-objects | ||||||
| 
 | 
 | ||||||
| Custom logging configuration | Custom logging configuration | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ from django.contrib import admin | ||||||
| from django.contrib.admin.options import IncorrectLookupParameters | from django.contrib.admin.options import IncorrectLookupParameters | ||||||
| from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR | from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR | ||||||
| from django.contrib.auth.models import User | from django.contrib.auth.models import User | ||||||
|  | from django.core.urlresolvers import reverse | ||||||
| from django.template import Context, Template | from django.template import Context, Template | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.test.client import RequestFactory | from django.test.client import RequestFactory | ||||||
|  | @ -65,7 +66,8 @@ class ChangeListTests(TestCase): | ||||||
|         template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') |         template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') | ||||||
|         context = Context({'cl': cl}) |         context = Context({'cl': cl}) | ||||||
|         table_output = template.render(context) |         table_output = template.render(context) | ||||||
|         row_html = '<tbody><tr class="row1"><th><a href="%d/">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % new_child.id |         link = reverse('admin:admin_changelist_child_change', args=(new_child.id,)) | ||||||
|  |         row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">(None)</td></tr></tbody>' % link | ||||||
|         self.assertFalse(table_output.find(row_html) == -1, |         self.assertFalse(table_output.find(row_html) == -1, | ||||||
|             'Failed to find expected row element: %s' % table_output) |             'Failed to find expected row element: %s' % table_output) | ||||||
| 
 | 
 | ||||||
|  | @ -87,7 +89,8 @@ class ChangeListTests(TestCase): | ||||||
|         template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') |         template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') | ||||||
|         context = Context({'cl': cl}) |         context = Context({'cl': cl}) | ||||||
|         table_output = template.render(context) |         table_output = template.render(context) | ||||||
|         row_html = '<tbody><tr class="row1"><th><a href="%d/">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % new_child.id |         link = reverse('admin:admin_changelist_child_change', args=(new_child.id,)) | ||||||
|  |         row_html = '<tbody><tr class="row1"><th><a href="%s">name</a></th><td class="nowrap">Parent object</td></tr></tbody>' % link | ||||||
|         self.assertFalse(table_output.find(row_html) == -1, |         self.assertFalse(table_output.find(row_html) == -1, | ||||||
|             'Failed to find expected row element: %s' % table_output) |             'Failed to find expected row element: %s' % table_output) | ||||||
| 
 | 
 | ||||||
|  | @ -425,7 +428,8 @@ class ChangeListTests(TestCase): | ||||||
|         request = self._mocked_authenticated_request('/child/', superuser) |         request = self._mocked_authenticated_request('/child/', superuser) | ||||||
|         response = m.changelist_view(request) |         response = m.changelist_view(request) | ||||||
|         for i in range(1, 10): |         for i in range(1, 10): | ||||||
|             self.assertContains(response, '<a href="%s/">%s</a>' % (i, i)) |             link = reverse('admin:admin_changelist_child_change', args=(i,)) | ||||||
|  |             self.assertContains(response, '<a href="%s">%s</a>' % (link, i)) | ||||||
| 
 | 
 | ||||||
|         list_display = m.get_list_display(request) |         list_display = m.get_list_display(request) | ||||||
|         list_display_links = m.get_list_display_links(request, list_display) |         list_display_links = m.get_list_display_links(request, list_display) | ||||||
|  |  | ||||||
|  | @ -40,12 +40,5 @@ | ||||||
|     "fields": { |     "fields": { | ||||||
|       "description": "An action with a name suspected of being a XSS attempt" |       "description": "An action with a name suspected of being a XSS attempt" | ||||||
|     } |     } | ||||||
|   }, |  | ||||||
|   { |  | ||||||
|     "pk": "The name of an action",  |  | ||||||
|     "model": "admin_custom_urls.action",  |  | ||||||
|     "fields": { |  | ||||||
|       "description": "A generic action" |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| from __future__ import absolute_import, unicode_literals | from __future__ import absolute_import, unicode_literals | ||||||
| 
 | 
 | ||||||
|  | from django.contrib.admin.util import quote | ||||||
| from django.core.urlresolvers import reverse | from django.core.urlresolvers import reverse | ||||||
| from django.template.response import TemplateResponse | from django.template.response import TemplateResponse | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
|  | @ -67,7 +68,7 @@ class AdminCustomUrlsTest(TestCase): | ||||||
| 
 | 
 | ||||||
|         # Ditto, but use reverse() to build the URL |         # Ditto, but use reverse() to build the URL | ||||||
|         url = reverse('admin:%s_action_change' % Action._meta.app_label, |         url = reverse('admin:%s_action_change' % Action._meta.app_label, | ||||||
|                 args=('add',)) |                 args=(quote('add'),)) | ||||||
|         response = self.client.get(url) |         response = self.client.get(url) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertContains(response, 'Change action') |         self.assertContains(response, 'Change action') | ||||||
|  | @ -75,19 +76,8 @@ class AdminCustomUrlsTest(TestCase): | ||||||
|         # Should correctly get the change_view for the model instance with the |         # Should correctly get the change_view for the model instance with the | ||||||
|         # funny-looking PK (the one wth a 'path/to/html/document.html' value) |         # funny-looking PK (the one wth a 'path/to/html/document.html' value) | ||||||
|         url = reverse('admin:%s_action_change' % Action._meta.app_label, |         url = reverse('admin:%s_action_change' % Action._meta.app_label, | ||||||
|                 args=("path/to/html/document.html",)) |                 args=(quote("path/to/html/document.html"),)) | ||||||
|         response = self.client.get(url) |         response = self.client.get(url) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertContains(response, 'Change action') |         self.assertContains(response, 'Change action') | ||||||
|         self.assertContains(response, 'value="path/to/html/document.html"') |         self.assertContains(response, 'value="path/to/html/document.html"') | ||||||
| 
 |  | ||||||
|     def testChangeViewHistoryButton(self): |  | ||||||
|         url = reverse('admin:%s_action_change' % Action._meta.app_label, |  | ||||||
|                 args=('The name of an action',)) |  | ||||||
|         response = self.client.get(url) |  | ||||||
|         self.assertEqual(response.status_code, 200) |  | ||||||
|         expected_link = reverse('admin:%s_action_history' % |  | ||||||
|                                 Action._meta.app_label, |  | ||||||
|                                 args=('The name of an action',)) |  | ||||||
|         self.assertContains(response, '<a href="%s" class="historylink"' % |  | ||||||
|                             expected_link) |  | ||||||
|  |  | ||||||
|  | @ -261,19 +261,21 @@ class AdminViewBasicTest(TestCase): | ||||||
|         p1 = Person.objects.create(name="Chris", gender=1, alive=True) |         p1 = Person.objects.create(name="Chris", gender=1, alive=True) | ||||||
|         p2 = Person.objects.create(name="Chris", gender=2, alive=True) |         p2 = Person.objects.create(name="Chris", gender=2, alive=True) | ||||||
|         p3 = Person.objects.create(name="Bob", gender=1, alive=True) |         p3 = Person.objects.create(name="Bob", gender=1, alive=True) | ||||||
|         link = '<a href="%s/' |         link1 = reverse('admin:admin_views_person_change', args=(p1.pk,)) | ||||||
|  |         link2 = reverse('admin:admin_views_person_change', args=(p2.pk,)) | ||||||
|  |         link3 = reverse('admin:admin_views_person_change', args=(p3.pk,)) | ||||||
| 
 | 
 | ||||||
|         # Sort by name, gender |         # Sort by name, gender | ||||||
|         # This hard-codes the URL because it'll fail if it runs against the |         # This hard-codes the URL because it'll fail if it runs against the | ||||||
|         # 'admin2' custom admin (which doesn't have the Person model). |         # 'admin2' custom admin (which doesn't have the Person model). | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '1.2'}) |         response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '1.2'}) | ||||||
|         self.assertContentBefore(response, link % p3.id, link % p1.id) |         self.assertContentBefore(response, link3, link1) | ||||||
|         self.assertContentBefore(response, link % p1.id, link % p2.id) |         self.assertContentBefore(response, link1, link2) | ||||||
| 
 | 
 | ||||||
|         # Sort by gender descending, name |         # Sort by gender descending, name | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '-2.1'}) |         response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '-2.1'}) | ||||||
|         self.assertContentBefore(response, link % p2.id, link % p3.id) |         self.assertContentBefore(response, link2, link3) | ||||||
|         self.assertContentBefore(response, link % p3.id, link % p1.id) |         self.assertContentBefore(response, link3, link1) | ||||||
| 
 | 
 | ||||||
|     def testChangeListSortingPreserveQuerySetOrdering(self): |     def testChangeListSortingPreserveQuerySetOrdering(self): | ||||||
|         """ |         """ | ||||||
|  | @ -285,37 +287,41 @@ class AdminViewBasicTest(TestCase): | ||||||
|         p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80) |         p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80) | ||||||
|         p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70) |         p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70) | ||||||
|         p3 = Person.objects.create(name="Chris", gender=2, alive=False, age=60) |         p3 = Person.objects.create(name="Chris", gender=2, alive=False, age=60) | ||||||
|         link = '<a href="%s/' |         link1 = reverse('admin:admin_views_person_change', args=(p1.pk,)) | ||||||
|  |         link2 = reverse('admin:admin_views_person_change', args=(p2.pk,)) | ||||||
|  |         link3 = reverse('admin:admin_views_person_change', args=(p3.pk,)) | ||||||
| 
 | 
 | ||||||
|         # This hard-codes the URL because it'll fail if it runs against the |         # This hard-codes the URL because it'll fail if it runs against the | ||||||
|         # 'admin2' custom admin (which doesn't have the Person model). |         # 'admin2' custom admin (which doesn't have the Person model). | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/person/', {}) |         response = self.client.get('/test_admin/admin/admin_views/person/', {}) | ||||||
|         self.assertContentBefore(response, link % p3.id, link % p2.id) |         self.assertContentBefore(response, link3, link2) | ||||||
|         self.assertContentBefore(response, link % p2.id, link % p1.id) |         self.assertContentBefore(response, link2, link1) | ||||||
| 
 | 
 | ||||||
|     def testChangeListSortingModelMeta(self): |     def testChangeListSortingModelMeta(self): | ||||||
|         # Test ordering on Model Meta is respected |         # Test ordering on Model Meta is respected | ||||||
| 
 | 
 | ||||||
|         l1 = Language.objects.create(iso='ur', name='Urdu') |         l1 = Language.objects.create(iso='ur', name='Urdu') | ||||||
|         l2 = Language.objects.create(iso='ar', name='Arabic') |         l2 = Language.objects.create(iso='ar', name='Arabic') | ||||||
|         link = '<a href="%s/' |         link1 = reverse('admin:admin_views_language_change', args=(quote(l1.pk),)) | ||||||
|  |         link2 = reverse('admin:admin_views_language_change', args=(quote(l2.pk),)) | ||||||
| 
 | 
 | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/language/', {}) |         response = self.client.get('/test_admin/admin/admin_views/language/', {}) | ||||||
|         self.assertContentBefore(response, link % l2.pk, link % l1.pk) |         self.assertContentBefore(response, link2, link1) | ||||||
| 
 | 
 | ||||||
|         # Test we can override with query string |         # Test we can override with query string | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/language/', {'o': '-1'}) |         response = self.client.get('/test_admin/admin/admin_views/language/', {'o': '-1'}) | ||||||
|         self.assertContentBefore(response, link % l1.pk, link % l2.pk) |         self.assertContentBefore(response, link1, link2) | ||||||
| 
 | 
 | ||||||
|     def testChangeListSortingOverrideModelAdmin(self): |     def testChangeListSortingOverrideModelAdmin(self): | ||||||
|         # Test ordering on Model Admin is respected, and overrides Model Meta |         # Test ordering on Model Admin is respected, and overrides Model Meta | ||||||
|         dt = datetime.datetime.now() |         dt = datetime.datetime.now() | ||||||
|         p1 = Podcast.objects.create(name="A", release_date=dt) |         p1 = Podcast.objects.create(name="A", release_date=dt) | ||||||
|         p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10)) |         p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10)) | ||||||
|  |         link1 = reverse('admin:admin_views_podcast_change', args=(p1.pk,)) | ||||||
|  |         link2 = reverse('admin:admin_views_podcast_change', args=(p2.pk,)) | ||||||
| 
 | 
 | ||||||
|         link = '<a href="%s/' |  | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/podcast/', {}) |         response = self.client.get('/test_admin/admin/admin_views/podcast/', {}) | ||||||
|         self.assertContentBefore(response, link % p1.pk, link % p2.pk) |         self.assertContentBefore(response, link1, link2) | ||||||
| 
 | 
 | ||||||
|     def testMultipleSortSameField(self): |     def testMultipleSortSameField(self): | ||||||
|         # Check that we get the columns we expect if we have two columns |         # Check that we get the columns we expect if we have two columns | ||||||
|  | @ -323,14 +329,16 @@ class AdminViewBasicTest(TestCase): | ||||||
|         dt = datetime.datetime.now() |         dt = datetime.datetime.now() | ||||||
|         p1 = Podcast.objects.create(name="A", release_date=dt) |         p1 = Podcast.objects.create(name="A", release_date=dt) | ||||||
|         p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10)) |         p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10)) | ||||||
|  |         link1 = reverse('admin:admin_views_podcast_change', args=(quote(p1.pk),)) | ||||||
|  |         link2 = reverse('admin:admin_views_podcast_change', args=(quote(p2.pk),)) | ||||||
| 
 | 
 | ||||||
|         link = '<a href="%s/' |  | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/podcast/', {}) |         response = self.client.get('/test_admin/admin/admin_views/podcast/', {}) | ||||||
|         self.assertContentBefore(response, link % p1.pk, link % p2.pk) |         self.assertContentBefore(response, link1, link2) | ||||||
| 
 | 
 | ||||||
|         p1 = ComplexSortedPerson.objects.create(name="Bob", age=10) |         p1 = ComplexSortedPerson.objects.create(name="Bob", age=10) | ||||||
|         p2 = ComplexSortedPerson.objects.create(name="Amy", age=20) |         p2 = ComplexSortedPerson.objects.create(name="Amy", age=20) | ||||||
|         link = '<a href="%s/' |         link1 = reverse('admin:admin_views_complexsortedperson_change', args=(p1.pk,)) | ||||||
|  |         link2 = reverse('admin:admin_views_complexsortedperson_change', args=(p2.pk,)) | ||||||
| 
 | 
 | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/complexsortedperson/', {}) |         response = self.client.get('/test_admin/admin/admin_views/complexsortedperson/', {}) | ||||||
|         # Should have 5 columns (including action checkbox col) |         # Should have 5 columns (including action checkbox col) | ||||||
|  | @ -343,7 +351,7 @@ class AdminViewBasicTest(TestCase): | ||||||
|         self.assertContentBefore(response, 'Name', 'Colored name') |         self.assertContentBefore(response, 'Name', 'Colored name') | ||||||
| 
 | 
 | ||||||
|         # Check sorting - should be by name |         # Check sorting - should be by name | ||||||
|         self.assertContentBefore(response, link % p2.id, link % p1.id) |         self.assertContentBefore(response, link2, link1) | ||||||
| 
 | 
 | ||||||
|     def testSortIndicatorsAdminOrder(self): |     def testSortIndicatorsAdminOrder(self): | ||||||
|         """ |         """ | ||||||
|  | @ -462,10 +470,12 @@ class AdminViewBasicTest(TestCase): | ||||||
|         for rows corresponding to instances of a model in which a named group |         for rows corresponding to instances of a model in which a named group | ||||||
|         has been used in the choices option of a field. |         has been used in the choices option of a field. | ||||||
|         """ |         """ | ||||||
|  |         link1 = reverse('admin:admin_views_fabric_change', args=(1,), current_app=self.urlbit) | ||||||
|  |         link2 = reverse('admin:admin_views_fabric_change', args=(2,), current_app=self.urlbit) | ||||||
|         response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit) |         response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit) | ||||||
|         fail_msg = "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group." |         fail_msg = "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group." | ||||||
|         self.assertContains(response, '<a href="1/">Horizontal</a>', msg_prefix=fail_msg, html=True) |         self.assertContains(response, '<a href="%s">Horizontal</a>' % link1, msg_prefix=fail_msg, html=True) | ||||||
|         self.assertContains(response, '<a href="2/">Vertical</a>', msg_prefix=fail_msg, html=True) |         self.assertContains(response, '<a href="%s">Vertical</a>' % link2, msg_prefix=fail_msg, html=True) | ||||||
| 
 | 
 | ||||||
|     def testNamedGroupFieldChoicesFilter(self): |     def testNamedGroupFieldChoicesFilter(self): | ||||||
|         """ |         """ | ||||||
|  | @ -1375,9 +1385,12 @@ class AdminViewStringPrimaryKeyTest(TestCase): | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
| 
 | 
 | ||||||
|     def test_changelist_to_changeform_link(self): |     def test_changelist_to_changeform_link(self): | ||||||
|         "The link from the changelist referring to the changeform of the object should be quoted" |         "Link to the changeform of the object in changelist should use reverse() and be quoted -- #18072" | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/') |         prefix = '/test_admin/admin/admin_views/modelwithstringprimarykey/' | ||||||
|         should_contain = """<th><a href="%s/">%s</a></th></tr>""" % (escape(quote(self.pk)), escape(self.pk)) |         response = self.client.get(prefix) | ||||||
|  |         # this URL now comes through reverse(), thus iri_to_uri encoding | ||||||
|  |         pk_final_url = escape(iri_to_uri(quote(self.pk))) | ||||||
|  |         should_contain = """<th><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, escape(self.pk)) | ||||||
|         self.assertContains(response, should_contain) |         self.assertContains(response, should_contain) | ||||||
| 
 | 
 | ||||||
|     def test_recentactions_link(self): |     def test_recentactions_link(self): | ||||||
|  | @ -1445,6 +1458,18 @@ class AdminViewStringPrimaryKeyTest(TestCase): | ||||||
|         should_contain = '/%s/" class="viewsitelink">' % model.pk |         should_contain = '/%s/" class="viewsitelink">' % model.pk | ||||||
|         self.assertContains(response, should_contain) |         self.assertContains(response, should_contain) | ||||||
| 
 | 
 | ||||||
|  |     def test_change_view_history_link(self): | ||||||
|  |         """Object history button link should work and contain the pk value quoted.""" | ||||||
|  |         url = reverse('admin:%s_modelwithstringprimarykey_change' % | ||||||
|  |                           ModelWithStringPrimaryKey._meta.app_label, | ||||||
|  |                       args=(quote(self.pk),)) | ||||||
|  |         response = self.client.get(url) | ||||||
|  |         self.assertEqual(response.status_code, 200) | ||||||
|  |         expected_link = reverse('admin:%s_modelwithstringprimarykey_history' % | ||||||
|  |                                     ModelWithStringPrimaryKey._meta.app_label, | ||||||
|  |                                 args=(quote(self.pk),)) | ||||||
|  |         self.assertContains(response, '<a href="%s" class="historylink"' % expected_link) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) | @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) | ||||||
| class SecureViewTests(TestCase): | class SecureViewTests(TestCase): | ||||||
|  | @ -2028,12 +2053,14 @@ class AdminViewListEditable(TestCase): | ||||||
|         """ |         """ | ||||||
|         story1 = OtherStory.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...') |         story1 = OtherStory.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...') | ||||||
|         story2 = OtherStory.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...') |         story2 = OtherStory.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...') | ||||||
|  |         link1 = reverse('admin:admin_views_otherstory_change', args=(story1.pk,)) | ||||||
|  |         link2 = reverse('admin:admin_views_otherstory_change', args=(story2.pk,)) | ||||||
|         response = self.client.get('/test_admin/admin/admin_views/otherstory/') |         response = self.client.get('/test_admin/admin/admin_views/otherstory/') | ||||||
|         self.assertContains(response, 'id="id_form-0-id"', 1)  # Only one hidden field, in a separate place than the table. |         self.assertContains(response, 'id="id_form-0-id"', 1)  # Only one hidden field, in a separate place than the table. | ||||||
|         self.assertContains(response, 'id="id_form-1-id"', 1) |         self.assertContains(response, 'id="id_form-1-id"', 1) | ||||||
|         self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True) |         self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True) | ||||||
|         self.assertContains(response, '<th><a href="%d/">%d</a></th>' % (story1.id, story1.id), 1) |         self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link1, story1.id), 1) | ||||||
|         self.assertContains(response, '<th><a href="%d/">%d</a></th>' % (story2.id, story2.id), 1) |         self.assertContains(response, '<th><a href="%s">%d</a></th>' % (link2, story2.id), 1) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) | @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								tests/regressiontests/logging_tests/logconfig.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/regressiontests/logging_tests/logconfig.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | import logging | ||||||
|  | 
 | ||||||
|  | from django.conf import settings | ||||||
|  | 
 | ||||||
|  | class MyHandler(logging.Handler): | ||||||
|  |     def __init__(self): | ||||||
|  |         logging.Handler.__init__(self) | ||||||
|  |         self.config = settings.LOGGING | ||||||
|  | @ -10,6 +10,8 @@ from django.test import TestCase, RequestFactory | ||||||
| from django.test.utils import override_settings | from django.test.utils import override_settings | ||||||
| from django.utils.log import CallbackFilter, RequireDebugFalse | from django.utils.log import CallbackFilter, RequireDebugFalse | ||||||
| 
 | 
 | ||||||
|  | from ..admin_scripts.tests import AdminScriptTestCase | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # logging config prior to using filter with mail_admins | # logging config prior to using filter with mail_admins | ||||||
| OLD_LOGGING = { | OLD_LOGGING = { | ||||||
|  | @ -253,3 +255,30 @@ class AdminEmailHandlerTest(TestCase): | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(len(mail.outbox), 1) |         self.assertEqual(len(mail.outbox), 1) | ||||||
|         self.assertEqual(mail.outbox[0].subject, expected_subject) |         self.assertEqual(mail.outbox[0].subject, expected_subject) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SettingsConfigTest(AdminScriptTestCase): | ||||||
|  |     """ | ||||||
|  |     Test that accessing settings in a custom logging handler does not trigger | ||||||
|  |     a circular import error. | ||||||
|  |     """ | ||||||
|  |     def setUp(self): | ||||||
|  |         log_config = """{ | ||||||
|  |     'version': 1, | ||||||
|  |     'handlers': { | ||||||
|  |         'custom_handler': { | ||||||
|  |             'level': 'INFO', | ||||||
|  |             'class': 'logging_tests.logconfig.MyHandler', | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }""" | ||||||
|  |         self.write_settings('settings.py', sdict={'LOGGING': log_config}) | ||||||
|  | 
 | ||||||
|  |     def tearDown(self): | ||||||
|  |         self.remove_settings('settings.py') | ||||||
|  | 
 | ||||||
|  |     def test_circular_dependency(self): | ||||||
|  |         # validate is just an example command to trigger settings configuration | ||||||
|  |         out, err = self.run_manage(['validate']) | ||||||
|  |         self.assertNoOutput(err) | ||||||
|  |         self.assertOutput(out, "0 errors found") | ||||||
|  |  | ||||||
|  | @ -154,4 +154,4 @@ class TestUtilsHtml(unittest.TestCase): | ||||||
|             ("<a>x</a> <p><b>y</b></p>", "a b", "x <p>y</p>"), |             ("<a>x</a> <p><b>y</b></p>", "a b", "x <p>y</p>"), | ||||||
|         ) |         ) | ||||||
|         for value, tags, output in items: |         for value, tags, output in items: | ||||||
|             self.assertEquals(f(value, tags), output) |             self.assertEqual(f(value, tags), output) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Russell Keith-Magee
						Russell Keith-Magee