diff --git a/django/__init__.py b/django/__init__.py index 3025f721bd..c9c3ecf17a 100644 --- a/django/__init__.py +++ b/django/__init__.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.utils.version import get_version VERSION = (1, 10, 0, 'alpha', 0) @@ -5,14 +7,21 @@ VERSION = (1, 10, 0, 'alpha', 0) __version__ = get_version(VERSION) -def setup(): +def setup(set_prefix=True): """ Configure the settings (this happens as a side effect of accessing the first setting), configure logging and populate the app registry. + Set the thread-local urlresolvers script prefix if `set_prefix` is True. """ from django.apps import apps from django.conf import settings + from django.core.urlresolvers import set_script_prefix + from django.utils.encoding import force_text from django.utils.log import configure_logging configure_logging(settings.LOGGING_CONFIG, settings.LOGGING) + if set_prefix: + set_script_prefix( + '/' if settings.FORCE_SCRIPT_NAME is None else force_text(settings.FORCE_SCRIPT_NAME) + ) apps.populate(settings.INSTALLED_APPS) diff --git a/django/core/wsgi.py b/django/core/wsgi.py index 62aa43bda5..e0ded3db54 100644 --- a/django/core/wsgi.py +++ b/django/core/wsgi.py @@ -10,5 +10,5 @@ def get_wsgi_application(): Allows us to avoid making django.core.handlers.WSGIHandler public API, in case the internal WSGI implementation changes or moves in the future. """ - django.setup() + django.setup(set_prefix=False) return WSGIHandler() diff --git a/docs/ref/applications.txt b/docs/ref/applications.txt index 498e10c954..ba0ddeb532 100644 --- a/docs/ref/applications.txt +++ b/docs/ref/applications.txt @@ -332,14 +332,20 @@ application registry. .. currentmodule:: django -.. function:: setup() +.. function:: setup(set_script=True) Configures Django by: * Loading the settings. * Setting up logging. + * If ``set_script`` is True, setting the URL resolver script prefix to + :setting:`FORCE_SCRIPT_NAME` if defined, or ``/`` otherwise. * Initializing the application registry. + .. versionchanged:: 1.10 + + The ability to set the URL resolver script prefix is new. + This function is called automatically: * When running an HTTP server via Django's WSGI support. diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index dba54e26b4..705cecbb57 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1408,7 +1408,14 @@ Default: ``None`` If not ``None``, this will be used as the value of the ``SCRIPT_NAME`` environment variable in any HTTP request. This setting can be used to override the server-provided value of ``SCRIPT_NAME``, which may be a rewritten version -of the preferred value or not supplied at all. +of the preferred value or not supplied at all. It is also used by +:func:`django.setup()` to set the URL resolver script prefix outside of the +request/response cycle (e.g. in management commands and standalone scripts) to +generate correct URLs when ``SCRIPT_NAME`` is not ``/``. + +.. versionchanged:: 1.10 + + The setting's use in :func:`django.setup()` was added. .. setting:: FORMAT_MODULE_PATH diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt index b291dcfd41..bea1dc10d1 100644 --- a/docs/releases/1.10.txt +++ b/docs/releases/1.10.txt @@ -209,7 +209,10 @@ Tests URLs ^^^^ -* ... +* An addition in :func:`django.setup()` allows URL resolving that happens + outside of the request/response cycle (e.g. in management commands and + standalone scripts) to take :setting:`FORCE_SCRIPT_NAME` into account when it + is set. Validators ^^^^^^^^^^ diff --git a/tests/user_commands/management/commands/reverse_url.py b/tests/user_commands/management/commands/reverse_url.py new file mode 100644 index 0000000000..f2064bf05d --- /dev/null +++ b/tests/user_commands/management/commands/reverse_url.py @@ -0,0 +1,10 @@ +from django.core.management.base import BaseCommand +from django.core.urlresolvers import reverse + + +class Command(BaseCommand): + """ + This command returns a URL from a reverse() call. + """ + def handle(self, *args, **options): + return reverse('some_url') diff --git a/tests/user_commands/tests.py b/tests/user_commands/tests.py index 772da22bed..048ac4d963 100644 --- a/tests/user_commands/tests.py +++ b/tests/user_commands/tests.py @@ -1,5 +1,7 @@ import os +from admin_scripts.tests import AdminScriptTestCase + from django.apps import apps from django.core import management from django.core.management import BaseCommand, CommandError, find_commands @@ -159,6 +161,23 @@ class CommandTests(SimpleTestCase): BaseCommand.check = saved_check +class CommandRunTests(AdminScriptTestCase): + """ + Tests that need to run by simulating the command line, not by call_command. + """ + def tearDown(self): + self.remove_settings('settings.py') + + def test_script_prefix_set_in_commands(self): + self.write_settings('settings.py', apps=['user_commands'], sdict={ + 'ROOT_URLCONF': '"user_commands.urls"', + 'FORCE_SCRIPT_NAME': '"/PREFIX/"', + }) + out, err = self.run_manage(['reverse_url']) + self.assertNoOutput(err) + self.assertEqual(out.strip(), '/PREFIX/some/url/') + + class UtilsTests(SimpleTestCase): def test_no_existent_external_program(self): diff --git a/tests/user_commands/urls.py b/tests/user_commands/urls.py new file mode 100644 index 0000000000..fe20693dce --- /dev/null +++ b/tests/user_commands/urls.py @@ -0,0 +1,5 @@ +from django.conf.urls import url + +urlpatterns = [ + url(r'^some/url/$', lambda req:req, name='some_url'), +]