2008-09-22 14:03:24 +08:00
"""
Base classes for writing management commands ( named commands which can
be executed through ` ` django - admin . py ` ` or ` ` manage . py ` ` ) .
"""
2013-10-15 02:24:57 +08:00
from __future__ import unicode_literals
2007-10-14 00:02:54 +08:00
import os
import sys
2011-12-23 06:38:02 +08:00
2007-10-14 00:02:54 +08:00
from optparse import make_option , OptionParser
2007-09-10 05:57:59 +08:00
import django
2013-02-23 23:28:45 +08:00
from django . core . management . color import color_style , no_style
2012-08-30 04:40:51 +08:00
from django . utils . encoding import force_str
2012-07-21 16:01:46 +08:00
from django . utils . six import StringIO
2007-08-16 14:06:55 +08:00
2011-12-23 06:38:02 +08:00
2007-08-16 14:06:55 +08:00
class CommandError ( Exception ) :
2008-09-22 14:03:24 +08:00
"""
Exception class indicating a problem while executing a management
command .
If this exception is raised during the execution of a management
command , it will be caught and turned into a nicely - printed error
message to the appropriate output stream ( i . e . , stderr ) ; as a
result , raising this exception ( with a sensible description of the
error ) is the preferred way to indicate that something has gone
wrong in the execution of a command .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2007-08-16 14:06:55 +08:00
pass
2011-12-23 06:38:02 +08:00
2007-09-22 00:19:20 +08:00
def handle_default_options ( options ) :
"""
2008-09-22 14:03:24 +08:00
Include any default options that all commands should accept here
so that ManagementUtility can handle them before searching for
user commands .
2010-03-18 21:24:11 +08:00
2007-09-22 00:19:20 +08:00
"""
if options . settings :
os . environ [ ' DJANGO_SETTINGS_MODULE ' ] = options . settings
if options . pythonpath :
sys . path . insert ( 0 , options . pythonpath )
2007-10-14 00:40:20 +08:00
2011-12-23 06:38:02 +08:00
2012-05-19 19:51:54 +08:00
class OutputWrapper ( object ) :
"""
Wrapper around stdout / stderr
"""
2012-05-26 17:43:37 +08:00
def __init__ ( self , out , style_func = None , ending = ' \n ' ) :
2012-05-19 19:51:54 +08:00
self . _out = out
self . style_func = None
if hasattr ( out , ' isatty ' ) and out . isatty ( ) :
self . style_func = style_func
2012-05-26 17:43:37 +08:00
self . ending = ending
2012-05-19 19:51:54 +08:00
def __getattr__ ( self , name ) :
return getattr ( self . _out , name )
2012-05-26 17:43:37 +08:00
def write ( self , msg , style_func = None , ending = None ) :
2013-05-17 22:33:36 +08:00
ending = self . ending if ending is None else ending
2012-05-19 19:51:54 +08:00
if ending and not msg . endswith ( ending ) :
msg + = ending
2012-05-26 17:43:37 +08:00
style_func = [ f for f in ( style_func , self . style_func , lambda x : x )
if f is not None ] [ 0 ]
2012-08-30 04:40:51 +08:00
self . _out . write ( force_str ( style_func ( msg ) ) )
2012-05-19 19:51:54 +08:00
2007-08-16 14:06:55 +08:00
class BaseCommand ( object ) :
2008-09-22 14:03:24 +08:00
"""
The base class from which all management commands ultimately
derive .
Use this class if you want access to all of the mechanisms which
parse the command - line arguments and work out what code to call in
response ; if you don ' t need to change any of that behavior,
consider using one of the subclasses defined in this file .
If you are interested in overriding / customizing various aspects of
the command - parsing and - execution behavior , the normal flow works
as follows :
1. ` ` django - admin . py ` ` or ` ` manage . py ` ` loads the command class
and calls its ` ` run_from_argv ( ) ` ` method .
2. The ` ` run_from_argv ( ) ` ` method calls ` ` create_parser ( ) ` ` to get
an ` ` OptionParser ` ` for the arguments , parses them , performs
any environment changes requested by options like
` ` pythonpath ` ` , and then calls the ` ` execute ( ) ` ` method ,
passing the parsed arguments .
3. The ` ` execute ( ) ` ` method attempts to carry out the command by
calling the ` ` handle ( ) ` ` method with the parsed arguments ; any
output produced by ` ` handle ( ) ` ` will be printed to standard
output and , if the command is intended to produce a block of
SQL statements , will be wrapped in ` ` BEGIN ` ` and ` ` COMMIT ` ` .
2012-05-27 02:50:44 +08:00
4. If ` ` handle ( ) ` ` or ` ` execute ( ) ` ` raised any exception ( e . g .
` ` CommandError ` ` ) , ` ` run_from_argv ( ) ` ` will instead print an error
message to ` ` stderr ` ` .
2008-09-22 14:03:24 +08:00
Thus , the ` ` handle ( ) ` ` method is typically the starting point for
subclasses ; many built - in commands and command types either place
all of their logic in ` ` handle ( ) ` ` , or perform some additional
parsing work in ` ` handle ( ) ` ` and then delegate from it to more
specialized methods as needed .
Several attributes affect behavior at various steps along the way :
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
` ` args ` `
A string listing the arguments accepted by the command ,
suitable for use in help messages ; e . g . , a command which takes
a list of application names might set this to ' <appname
appname . . . > ' .
` ` can_import_settings ` `
A boolean indicating whether the command needs to be able to
import Django settings ; if ` ` True ` ` , ` ` execute ( ) ` ` will verify
that this is possible before proceeding . Default value is
` ` True ` ` .
` ` help ` `
A short description of the command , which will be printed in
help messages .
` ` option_list ` `
This is the list of ` ` optparse ` ` options which will be fed
into the command ' s ``OptionParser`` for parsing arguments.
` ` output_transaction ` `
A boolean indicating whether the command outputs SQL
statements ; if ` ` True ` ` , the output will automatically be
wrapped with ` ` BEGIN ; ` ` and ` ` COMMIT ; ` ` . Default value is
` ` False ` ` .
` ` requires_model_validation ` `
A boolean ; if ` ` True ` ` , validation of installed models will be
performed prior to executing the command . Default value is
` ` True ` ` . To validate an individual application ' s models
rather than all applications ' models, call
` ` self . validate ( app ) ` ` from ` ` handle ( ) ` ` , where ` ` app ` ` is the
application ' s Python module.
2010-03-18 21:24:11 +08:00
2013-02-04 07:53:48 +08:00
` ` leave_locale_alone ` `
A boolean indicating whether the locale set in settings should be
preserved during the execution of the command instead of being
forcibly set to ' en-us ' .
Default value is ` ` False ` ` .
Make sure you know what you are doing if you decide to change the value
2013-02-04 19:55:45 +08:00
of this option in your custom command if it creates database content
that is locale - sensitive and such content shouldn ' t contain any
translations ( like it happens e . g . with django . contrim . auth
permissions ) as making the locale differ from the de facto default
' en-us ' might cause unintended effects .
2013-02-04 07:53:48 +08:00
This option can ' t be False when the can_import_settings option is set
to False too because attempting to set the locale needs access to
settings . This condition will generate a CommandError .
2008-09-22 14:03:24 +08:00
"""
2007-08-16 14:06:55 +08:00
# Metadata about this command.
2007-09-10 05:57:59 +08:00
option_list = (
2008-10-02 20:57:13 +08:00
make_option ( ' -v ' , ' --verbosity ' , action = ' store ' , dest = ' verbosity ' , default = ' 1 ' ,
2010-08-07 14:58:14 +08:00
type = ' choice ' , choices = [ ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' ] ,
2011-12-04 01:34:52 +08:00
help = ' Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output ' ) ,
2007-09-10 05:57:59 +08:00
make_option ( ' --settings ' ,
help = ' The Python path to a settings module, e.g. " myproject.settings.main " . If this isn \' t provided, the DJANGO_SETTINGS_MODULE environment variable will be used. ' ) ,
make_option ( ' --pythonpath ' ,
help = ' A directory to add to the Python path, e.g. " /home/djangoprojects/myproject " . ' ) ,
2007-12-17 19:09:32 +08:00
make_option ( ' --traceback ' , action = ' store_true ' ,
2013-05-19 00:04:45 +08:00
help = ' Raise on exception ' ) ,
2013-02-23 23:28:45 +08:00
make_option ( ' --no-color ' , action = ' store_true ' , dest = ' no_color ' , default = False ,
help = " Don ' t colorize the command output. " ) ,
2007-09-10 05:57:59 +08:00
)
2007-08-16 14:06:55 +08:00
help = ' '
args = ' '
# Configuration shortcuts that alter various logic.
can_import_settings = True
requires_model_validation = True
2011-12-23 06:38:02 +08:00
output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"
2013-02-04 07:53:48 +08:00
leave_locale_alone = False
2007-08-16 14:06:55 +08:00
def __init__ ( self ) :
self . style = color_style ( )
2007-09-10 05:57:59 +08:00
def get_version ( self ) :
"""
2008-09-22 14:03:24 +08:00
Return the Django version , which should be correct for all
built - in Django commands . User - supplied commands should
override this method .
2010-03-18 21:24:11 +08:00
2007-09-10 05:57:59 +08:00
"""
return django . get_version ( )
2007-09-11 12:24:35 +08:00
def usage ( self , subcommand ) :
2008-09-22 14:03:24 +08:00
"""
Return a brief description of how to use this command , by
default from the attribute ` ` self . help ` ` .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2007-09-11 12:24:35 +08:00
usage = ' %% prog %s [options] %s ' % ( subcommand , self . args )
2007-09-10 05:57:59 +08:00
if self . help :
return ' %s \n \n %s ' % ( usage , self . help )
else :
return usage
2007-09-11 12:24:35 +08:00
def create_parser ( self , prog_name , subcommand ) :
2008-09-22 14:03:24 +08:00
"""
Create and return the ` ` OptionParser ` ` which will be used to
parse the arguments to this command .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2007-09-10 05:57:59 +08:00
return OptionParser ( prog = prog_name ,
2007-09-11 12:24:35 +08:00
usage = self . usage ( subcommand ) ,
2007-09-10 05:57:59 +08:00
version = self . get_version ( ) ,
option_list = self . option_list )
2007-09-11 12:24:35 +08:00
def print_help ( self , prog_name , subcommand ) :
2008-09-22 14:03:24 +08:00
"""
Print the help message for this command , derived from
` ` self . usage ( ) ` ` .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2007-09-11 12:24:35 +08:00
parser = self . create_parser ( prog_name , subcommand )
2007-09-10 05:57:59 +08:00
parser . print_help ( )
2007-09-11 11:46:35 +08:00
def run_from_argv ( self , argv ) :
2008-09-22 14:03:24 +08:00
"""
Set up any environment changes requested ( e . g . , Python path
2012-05-27 02:50:44 +08:00
and Django settings ) , then run this command . If the
command raises a ` ` CommandError ` ` , intercept it and print it sensibly
2013-05-19 00:04:45 +08:00
to stderr . If the ` ` - - traceback ` ` option is present or the raised
` ` Exception ` ` is not ` ` CommandError ` ` , raise it .
2008-09-22 14:03:24 +08:00
"""
2007-09-11 12:24:35 +08:00
parser = self . create_parser ( argv [ 0 ] , argv [ 1 ] )
options , args = parser . parse_args ( argv [ 2 : ] )
2007-09-22 00:19:20 +08:00
handle_default_options ( options )
2012-05-27 02:50:44 +08:00
try :
self . execute ( * args , * * options . __dict__ )
except Exception as e :
2013-05-19 00:04:45 +08:00
if options . traceback or not isinstance ( e , CommandError ) :
raise
2013-01-25 21:52:24 +08:00
# self.stderr is not guaranteed to be set here
stderr = getattr ( self , ' stderr ' , OutputWrapper ( sys . stderr , self . style . ERROR ) )
2013-05-19 00:04:45 +08:00
stderr . write ( ' %s : %s ' % ( e . __class__ . __name__ , e ) )
2012-05-27 02:50:44 +08:00
sys . exit ( 1 )
2007-09-10 05:57:59 +08:00
2007-08-16 14:06:55 +08:00
def execute ( self , * args , * * options ) :
2008-09-22 14:03:24 +08:00
"""
Try to execute this command , performing model validation if
needed ( as controlled by the attribute
2012-05-27 02:50:44 +08:00
` ` self . requires_model_validation ` ` , except if force - skipped ) .
2008-09-22 14:03:24 +08:00
"""
2012-05-19 19:51:54 +08:00
self . stdout = OutputWrapper ( options . get ( ' stdout ' , sys . stdout ) )
2013-02-23 23:28:45 +08:00
if options . get ( ' no_color ' ) :
self . style = no_style ( )
self . stderr = OutputWrapper ( options . get ( ' stderr ' , sys . stderr ) )
else :
self . stderr = OutputWrapper ( options . get ( ' stderr ' , sys . stderr ) , self . style . ERROR )
2012-05-19 19:51:54 +08:00
2007-08-16 14:06:55 +08:00
if self . can_import_settings :
2013-10-18 19:25:30 +08:00
from django . conf import settings # NOQA
2013-02-04 07:53:48 +08:00
saved_locale = None
if not self . leave_locale_alone :
# Only mess with locales if we can assume we have a working
# settings file, because django.utils.translation requires settings
# (The final saying about whether the i18n machinery is active will be
# found in the value of the USE_I18N setting)
if not self . can_import_settings :
raise CommandError ( " Incompatible values of ' leave_locale_alone ' "
" ( %s ) and ' can_import_settings ' ( %s ) command "
" options. " % ( self . leave_locale_alone ,
self . can_import_settings ) )
# Switch to US English, because django-admin.py creates database
# content like permissions, and those shouldn't contain any
# translations.
2012-05-27 02:50:44 +08:00
from django . utils import translation
2013-02-04 07:53:48 +08:00
saved_locale = translation . get_language ( )
2012-05-27 02:50:44 +08:00
translation . activate ( ' en-us ' )
2011-12-11 18:03:09 +08:00
2007-08-16 14:06:55 +08:00
try :
2012-05-01 13:40:04 +08:00
if self . requires_model_validation and not options . get ( ' skip_validation ' ) :
2007-08-16 14:06:55 +08:00
self . validate ( )
output = self . handle ( * args , * * options )
if output :
if self . output_transaction :
2010-05-28 21:46:12 +08:00
# This needs to be imported here, because it relies on
# settings.
from django . db import connections , DEFAULT_DB_ALIAS
connection = connections [ options . get ( ' database ' , DEFAULT_DB_ALIAS ) ]
2007-08-20 08:24:03 +08:00
if connection . ops . start_transaction_sql ( ) :
2012-05-19 19:51:54 +08:00
self . stdout . write ( self . style . SQL_KEYWORD ( connection . ops . start_transaction_sql ( ) ) )
2010-06-05 13:26:04 +08:00
self . stdout . write ( output )
2007-08-16 14:06:55 +08:00
if self . output_transaction :
2012-05-19 19:51:54 +08:00
self . stdout . write ( ' \n ' + self . style . SQL_KEYWORD ( " COMMIT; " ) )
2012-03-30 00:33:48 +08:00
finally :
2013-02-04 07:53:48 +08:00
if saved_locale is not None :
translation . activate ( saved_locale )
2007-08-16 14:06:55 +08:00
2007-08-27 08:30:37 +08:00
def validate ( self , app = None , display_num_errors = False ) :
2007-08-16 14:06:55 +08:00
"""
Validates the given app , raising CommandError for any errors .
2010-03-18 21:24:11 +08:00
2007-08-16 14:06:55 +08:00
If app is None , then this will validate all installed apps .
2010-03-18 21:24:11 +08:00
2007-08-16 14:06:55 +08:00
"""
from django . core . management . validation import get_validation_errors
2012-07-21 16:01:46 +08:00
s = StringIO ( )
2007-08-16 14:06:55 +08:00
num_errors = get_validation_errors ( s , app )
if num_errors :
s . seek ( 0 )
error_text = s . read ( )
raise CommandError ( " One or more models did not validate: \n %s " % error_text )
2007-08-27 08:30:37 +08:00
if display_num_errors :
2013-05-17 22:33:36 +08:00
self . stdout . write ( " %s error %s found " % ( num_errors , ' ' if num_errors == 1 else ' s ' ) )
2007-08-16 14:06:55 +08:00
def handle ( self , * args , * * options ) :
2008-09-22 14:03:24 +08:00
"""
The actual logic of the command . Subclasses must implement
this method .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2013-09-07 02:24:52 +08:00
raise NotImplementedError ( ' subclasses of BaseCommand must provide a handle() method ' )
2007-08-16 14:06:55 +08:00
2011-12-23 06:38:02 +08:00
2007-08-16 14:06:55 +08:00
class AppCommand ( BaseCommand ) :
2008-09-22 14:03:24 +08:00
"""
A management command which takes one or more installed application
names as arguments , and does something with each of them .
Rather than implementing ` ` handle ( ) ` ` , subclasses must implement
` ` handle_app ( ) ` ` , which will be called once for each application .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2007-09-10 05:57:59 +08:00
args = ' <appname appname ...> '
2007-08-16 14:06:55 +08:00
def handle ( self , * app_labels , * * options ) :
2013-12-22 18:35:17 +08:00
from django . apps import app_cache
2007-08-16 14:06:55 +08:00
if not app_labels :
raise CommandError ( ' Enter at least one appname. ' )
2013-12-19 17:26:18 +08:00
# Populate models and don't use only_with_models_module=True when
# calling get_app_config() to tell apart missing apps from apps
# without a model module -- which can't be supported with the legacy
# API since it passes the models module to handle_app().
app_cache . populate_models ( )
2007-08-16 14:06:55 +08:00
try :
2013-12-15 01:51:58 +08:00
app_configs = [ app_cache . get_app_config ( app_label ) for app_label in app_labels ]
2013-12-14 18:11:52 +08:00
except ( LookupError , ImportError ) as e :
2007-08-16 14:06:55 +08:00
raise CommandError ( " %s . Are you sure your INSTALLED_APPS setting is correct? " % e )
output = [ ]
2013-12-15 01:51:58 +08:00
for app_config in app_configs :
if app_config . models_module is None :
raise CommandError (
" AppCommand cannot handle app %r because it doesn ' t have "
" a models module. " % app_config . label )
app_output = self . handle_app ( app_config . models_module , * * options )
2007-08-16 14:06:55 +08:00
if app_output :
output . append ( app_output )
return ' \n ' . join ( output )
def handle_app ( self , app , * * options ) :
2008-09-22 14:03:24 +08:00
"""
Perform the command ' s actions for ``app``, which will be the
Python module corresponding to an application name given on
the command line .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2013-09-07 02:24:52 +08:00
raise NotImplementedError ( ' subclasses of AppCommand must provide a handle_app() method ' )
2007-08-16 14:06:55 +08:00
2011-12-23 06:38:02 +08:00
2007-08-16 22:34:01 +08:00
class LabelCommand ( BaseCommand ) :
2008-09-22 14:03:24 +08:00
"""
A management command which takes one or more arbitrary arguments
( labels ) on the command line , and does something with each of
them .
Rather than implementing ` ` handle ( ) ` ` , subclasses must implement
` ` handle_label ( ) ` ` , which will be called once for each label .
If the arguments should be names of installed applications , use
` ` AppCommand ` ` instead .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2007-09-10 05:57:59 +08:00
args = ' <label label ...> '
2007-08-16 22:34:01 +08:00
label = ' label '
2007-08-17 03:14:09 +08:00
2007-08-16 22:34:01 +08:00
def handle ( self , * labels , * * options ) :
if not labels :
raise CommandError ( ' Enter at least one %s . ' % self . label )
output = [ ]
for label in labels :
label_output = self . handle_label ( label , * * options )
if label_output :
output . append ( label_output )
return ' \n ' . join ( output )
def handle_label ( self , label , * * options ) :
2008-09-22 14:03:24 +08:00
"""
Perform the command ' s actions for ``label``, which will be the
string as given on the command line .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2013-09-07 02:24:52 +08:00
raise NotImplementedError ( ' subclasses of LabelCommand must provide a handle_label() method ' )
2007-08-16 22:34:01 +08:00
2011-12-23 06:38:02 +08:00
2007-08-16 22:34:01 +08:00
class NoArgsCommand ( BaseCommand ) :
2008-09-22 14:03:24 +08:00
"""
A command which takes no arguments on the command line .
Rather than implementing ` ` handle ( ) ` ` , subclasses must implement
` ` handle_noargs ( ) ` ` ; ` ` handle ( ) ` ` itself is overridden to ensure
no arguments are passed to the command .
Attempting to pass arguments will raise ` ` CommandError ` ` .
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2007-08-16 22:34:01 +08:00
args = ' '
def handle ( self , * args , * * options ) :
2007-10-14 02:45:49 +08:00
if args :
2007-08-16 22:34:01 +08:00
raise CommandError ( " Command doesn ' t accept any arguments " )
return self . handle_noargs ( * * options )
def handle_noargs ( self , * * options ) :
2008-09-22 14:03:24 +08:00
"""
Perform this command ' s actions.
2010-03-18 21:24:11 +08:00
2008-09-22 14:03:24 +08:00
"""
2013-09-07 02:24:52 +08:00
raise NotImplementedError ( ' subclasses of NoArgsCommand must provide a handle_noargs() method ' )