diff --git a/django/contrib/staticfiles/management/commands/runserver.py b/django/contrib/staticfiles/management/commands/runserver.py new file mode 100644 index 0000000000..e13875994b --- /dev/null +++ b/django/contrib/staticfiles/management/commands/runserver.py @@ -0,0 +1,27 @@ +from optparse import make_option + +from django.conf import settings +from django.core.management.commands.runserver import BaseRunserverCommand + +from django.contrib.staticfiles.handlers import StaticFilesHandler + +class Command(BaseRunserverCommand): + option_list = BaseRunserverCommand.option_list + ( + make_option('--nostatic', action="store_false", dest='use_static_handler', default=True, + help='Tells Django to NOT automatically serve static files at STATICFILES_URL.'), + make_option('--insecure', action="store_true", dest='insecure_serving', default=False, + help='Allows serving static files even if DEBUG is False.'), + ) + help = "Starts a lightweight Web server for development, including static files serving." + + def get_handler(self, *args, **options): + """ + Returns the static files serving handler. + """ + handler = super(Command, self).get_handler(*args, **options) + use_static_handler = options.get('use_static_handler', True) + insecure_serving = options.get('insecure_serving', False) + if (settings.DEBUG and use_static_handler or + (use_static_handler and insecure_serving)): + handler = StaticFilesHandler(handler) + return handler diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py index c76946cb7b..892250fdba 100644 --- a/django/core/management/commands/runserver.py +++ b/django/core/management/commands/runserver.py @@ -1,20 +1,16 @@ from optparse import make_option import os import sys -import warnings from django.core.management.base import BaseCommand, CommandError +from django.core.handlers.wsgi import WSGIHandler +from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException +from django.utils import autoreload -class Command(BaseCommand): +class BaseRunserverCommand(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--noreload', action='store_false', dest='use_reloader', default=True, help='Tells Django to NOT use the auto-reloader.'), - make_option('--nostatic', action="store_false", dest='use_static_handler', default=True, - help='Tells Django to NOT automatically serve static files at STATICFILES_URL.'), - make_option('--insecure', action="store_true", dest='insecure_serving', default=False, - help='Allows serving static files even if DEBUG is False.'), - make_option('--adminmedia', dest='admin_media_path', default='', - help='Specifies the directory from which to serve admin media.'), ) help = "Starts a lightweight Web server for development." args = '[optional port number, or ipaddr:port]' @@ -22,79 +18,98 @@ class Command(BaseCommand): # Validation is called explicitly each time the server is reloaded. requires_model_validation = False + def get_handler(self, *args, **options): + """ + Returns the default WSGI handler for the runner. + """ + return WSGIHandler() + def handle(self, addrport='', *args, **options): - import django - from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException - from django.core.handlers.wsgi import WSGIHandler - from django.contrib.staticfiles.handlers import StaticFilesHandler if args: raise CommandError('Usage is runserver %s' % self.args) if not addrport: - addr = '' - port = '8000' + self.addr = '' + self.port = '8000' else: try: - addr, port = addrport.split(':') + self.addr, self.port = addrport.split(':') except ValueError: - addr, port = '', addrport - if not addr: - addr = '127.0.0.1' + self.addr, self.port = '', addrport + if not self.addr: + self.addr = '127.0.0.1' - if not port.isdigit(): - raise CommandError("%r is not a valid port number." % port) + if not self.port.isdigit(): + raise CommandError("%r is not a valid port number." % self.port) + self.run(*args, **options) + + def run(self, *args, **options): + """ + Runs the server, using the autoreloader if needed + """ use_reloader = options.get('use_reloader', True) - admin_media_path = options.get('admin_media_path', '') - shutdown_message = options.get('shutdown_message', '') - use_static_handler = options.get('use_static_handler', True) - insecure_serving = options.get('insecure_serving', False) - quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' - - def inner_run(): - from django.conf import settings - from django.utils import translation - print "Validating models..." - self.validate(display_num_errors=True) - print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE) - print "Development server is running at http://%s:%s/" % (addr, port) - print "Quit the server with %s." % quit_command - - # django.core.management.base forces the locale to en-us. We should - # set it up correctly for the first request (particularly important - # in the "--noreload" case). - translation.activate(settings.LANGUAGE_CODE) - - try: - handler = WSGIHandler() - allow_serving = (settings.DEBUG and use_static_handler or - (use_static_handler and insecure_serving)) - if (allow_serving and - "django.contrib.staticfiles" in settings.INSTALLED_APPS): - handler = StaticFilesHandler(handler) - # serve admin media like old-school (deprecation pending) - handler = AdminMediaHandler(handler, admin_media_path) - run(addr, int(port), handler) - except WSGIServerException, e: - # Use helpful error messages instead of ugly tracebacks. - ERRORS = { - 13: "You don't have permission to access that port.", - 98: "That port is already in use.", - 99: "That IP address can't be assigned-to.", - } - try: - error_text = ERRORS[e.args[0].args[0]] - except (AttributeError, KeyError): - error_text = str(e) - sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n') - # Need to use an OS exit because sys.exit doesn't work in a thread - os._exit(1) - except KeyboardInterrupt: - if shutdown_message: - print shutdown_message - sys.exit(0) if use_reloader: - from django.utils import autoreload - autoreload.main(inner_run) + autoreload.main(self.inner_run, args, options) else: - inner_run() + self.inner_run(*args, **options) + + def inner_run(self, *args, **options): + from django.conf import settings + from django.utils import translation + + shutdown_message = options.get('shutdown_message', '') + quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' + + self.stdout.write("Validating models...\n\n") + self.validate(display_num_errors=True) + self.stdout.write(( + "Django version %(version)s, using settings %(settings)r\n" + "Development server is running at http://%(addr)s:%(port)s/\n" + "Quit the server with %(quit_command)s.\n" + ) % { + "version": self.get_version(), + "settings": settings.SETTINGS_MODULE, + "addr": self.addr, + "port": self.port, + "quit_command": quit_command, + }) + # django.core.management.base forces the locale to en-us. We should + # set it up correctly for the first request (particularly important + # in the "--noreload" case). + translation.activate(settings.LANGUAGE_CODE) + + try: + handler = self.get_handler(*args, **options) + run(self.addr, int(self.port), handler) + except WSGIServerException, e: + # Use helpful error messages instead of ugly tracebacks. + ERRORS = { + 13: "You don't have permission to access that port.", + 98: "That port is already in use.", + 99: "That IP address can't be assigned-to.", + } + try: + error_text = ERRORS[e.args[0].args[0]] + except (AttributeError, KeyError): + error_text = str(e) + sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n') + # Need to use an OS exit because sys.exit doesn't work in a thread + os._exit(1) + except KeyboardInterrupt: + if shutdown_message: + self.stdout.write("%s\n" % shutdown_message) + sys.exit(0) + +class Command(BaseRunserverCommand): + option_list = BaseRunserverCommand.option_list + ( + make_option('--adminmedia', dest='admin_media_path', default='', + help='Specifies the directory from which to serve admin media.'), + ) + + def get_handler(self, *args, **options): + """ + Serves admin media like old-school (deprecation pending). + """ + handler = super(Command, self).get_handler(*args, **options) + return AdminMediaHandler(handler, options.get('admin_media_path', '')) diff --git a/docs/man/django-admin.1 b/docs/man/django-admin.1 index 539d88c5c5..2197af6650 100644 --- a/docs/man/django-admin.1 +++ b/docs/man/django-admin.1 @@ -164,6 +164,12 @@ Do not prompt the user for input. .I \-\-noreload Disable the development server's auto\-reloader. .TP +.I \-\-nostatic +Disable automatic serving of static files from STATICFILES_URL. +.TP +.I \-\-insecure +Enables serving of static files even if DEBUG is False. +.TP .I \-\-verbosity=VERBOSITY Verbosity level: 0=minimal output, 1=normal output, 2=all output. .TP diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index a91e2749a5..43e55acab2 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -684,7 +684,9 @@ Example usage:: .. django-admin-option:: --nostatic Use the ``--nostatic`` option to disable serving of static files with the -:doc:`staticfiles ` app entirely. +:doc:`staticfiles ` app entirely. This option is +only available if the :doc:`staticfiles ` app is +in your project's :setting:`INSTALLED_APPS` setting. Example usage:: @@ -696,7 +698,9 @@ Use the ``--insecure`` option to force serving of static files with the :doc:`staticfiles ` app even if the :setting:`DEBUG` setting is ``False``. By using this you acknowledge the fact that it's **grossly inefficient** and probably **insecure**. This is only intended for -local development, and should **never be used in production**. +local development, should **never be used in production** and is only +available if the :doc:`staticfiles ` app is +in your project's :setting:`INSTALLED_APPS` setting. See the :doc:`reference documentation of the app ` for more details and learn how to :doc:`manage and deploy static files