app/django/core/management/base.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 import os
       
     2 import sys
       
     3 from optparse import make_option, OptionParser
       
     4 
       
     5 import django
       
     6 from django.core.exceptions import ImproperlyConfigured
       
     7 from django.core.management.color import color_style
       
     8 
       
     9 class CommandError(Exception):
       
    10     pass
       
    11 
       
    12 def handle_default_options(options):
       
    13     """
       
    14     Include any default options that all commands should accept
       
    15     here so that ManagementUtility can handle them before searching
       
    16     for user commands.
       
    17     """
       
    18     if options.settings:
       
    19         os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
       
    20     if options.pythonpath:
       
    21         sys.path.insert(0, options.pythonpath)
       
    22 
       
    23 class BaseCommand(object):
       
    24     # Metadata about this command.
       
    25     option_list = (
       
    26         make_option('--settings',
       
    27             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.'),
       
    28         make_option('--pythonpath',
       
    29             help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".'),
       
    30         make_option('--traceback', action='store_true',
       
    31             help='Print traceback on exception'),
       
    32     )
       
    33     help = ''
       
    34     args = ''
       
    35 
       
    36     # Configuration shortcuts that alter various logic.
       
    37     can_import_settings = True
       
    38     requires_model_validation = True
       
    39     output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"
       
    40 
       
    41     def __init__(self):
       
    42         self.style = color_style()
       
    43 
       
    44     def get_version(self):
       
    45         """
       
    46         Returns the Django version, which should be correct for all built-in
       
    47         Django commands. User-supplied commands should override this method.
       
    48         """
       
    49         return django.get_version()
       
    50 
       
    51     def usage(self, subcommand):
       
    52         usage = '%%prog %s [options] %s' % (subcommand, self.args)
       
    53         if self.help:
       
    54             return '%s\n\n%s' % (usage, self.help)
       
    55         else:
       
    56             return usage
       
    57 
       
    58     def create_parser(self, prog_name, subcommand):
       
    59         return OptionParser(prog=prog_name,
       
    60                             usage=self.usage(subcommand),
       
    61                             version=self.get_version(),
       
    62                             option_list=self.option_list)
       
    63 
       
    64     def print_help(self, prog_name, subcommand):
       
    65         parser = self.create_parser(prog_name, subcommand)
       
    66         parser.print_help()
       
    67 
       
    68     def run_from_argv(self, argv):
       
    69         parser = self.create_parser(argv[0], argv[1])
       
    70         options, args = parser.parse_args(argv[2:])
       
    71         handle_default_options(options)
       
    72         self.execute(*args, **options.__dict__)
       
    73 
       
    74     def execute(self, *args, **options):
       
    75         # Switch to English, because django-admin.py creates database content
       
    76         # like permissions, and those shouldn't contain any translations.
       
    77         # But only do this if we can assume we have a working settings file,
       
    78         # because django.utils.translation requires settings.
       
    79         if self.can_import_settings:
       
    80             from django.utils import translation
       
    81             translation.activate('en-us')
       
    82 
       
    83         try:
       
    84             if self.requires_model_validation:
       
    85                 self.validate()
       
    86             output = self.handle(*args, **options)
       
    87             if output:
       
    88                 if self.output_transaction:
       
    89                     # This needs to be imported here, because it relies on settings.
       
    90                     from django.db import connection
       
    91                     if connection.ops.start_transaction_sql():
       
    92                         print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql())
       
    93                 print output
       
    94                 if self.output_transaction:
       
    95                     print self.style.SQL_KEYWORD("COMMIT;")
       
    96         except CommandError, e:
       
    97             sys.stderr.write(self.style.ERROR(str('Error: %s\n' % e)))
       
    98             sys.exit(1)
       
    99 
       
   100     def validate(self, app=None, display_num_errors=False):
       
   101         """
       
   102         Validates the given app, raising CommandError for any errors.
       
   103 
       
   104         If app is None, then this will validate all installed apps.
       
   105         """
       
   106         from django.core.management.validation import get_validation_errors
       
   107         try:
       
   108             from cStringIO import StringIO
       
   109         except ImportError:
       
   110             from StringIO import StringIO
       
   111         s = StringIO()
       
   112         num_errors = get_validation_errors(s, app)
       
   113         if num_errors:
       
   114             s.seek(0)
       
   115             error_text = s.read()
       
   116             raise CommandError("One or more models did not validate:\n%s" % error_text)
       
   117         if display_num_errors:
       
   118             print "%s error%s found" % (num_errors, num_errors != 1 and 's' or '')
       
   119 
       
   120     def handle(self, *args, **options):
       
   121         raise NotImplementedError()
       
   122 
       
   123 class AppCommand(BaseCommand):
       
   124     args = '<appname appname ...>'
       
   125 
       
   126     def handle(self, *app_labels, **options):
       
   127         from django.db import models
       
   128         if not app_labels:
       
   129             raise CommandError('Enter at least one appname.')
       
   130         try:
       
   131             app_list = [models.get_app(app_label) for app_label in app_labels]
       
   132         except (ImproperlyConfigured, ImportError), e:
       
   133             raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
       
   134         output = []
       
   135         for app in app_list:
       
   136             app_output = self.handle_app(app, **options)
       
   137             if app_output:
       
   138                 output.append(app_output)
       
   139         return '\n'.join(output)
       
   140 
       
   141     def handle_app(self, app, **options):
       
   142         raise NotImplementedError()
       
   143 
       
   144 class LabelCommand(BaseCommand):
       
   145     args = '<label label ...>'
       
   146     label = 'label'
       
   147 
       
   148     def handle(self, *labels, **options):
       
   149         if not labels:
       
   150             raise CommandError('Enter at least one %s.' % self.label)
       
   151 
       
   152         output = []
       
   153         for label in labels:
       
   154             label_output = self.handle_label(label, **options)
       
   155             if label_output:
       
   156                 output.append(label_output)
       
   157         return '\n'.join(output)
       
   158 
       
   159     def handle_label(self, label, **options):
       
   160         raise NotImplementedError()
       
   161 
       
   162 class NoArgsCommand(BaseCommand):
       
   163     args = ''
       
   164 
       
   165     def handle(self, *args, **options):
       
   166         if args:
       
   167             raise CommandError("Command doesn't accept any arguments")
       
   168         return self.handle_noargs(**options)
       
   169 
       
   170     def handle_noargs(self, **options):
       
   171         raise NotImplementedError()
       
   172 
       
   173 def copy_helper(style, app_or_project, name, directory, other_name=''):
       
   174     """
       
   175     Copies either a Django application layout template or a Django project
       
   176     layout template into the specified directory.
       
   177     """
       
   178     # style -- A color style object (see django.core.management.color).
       
   179     # app_or_project -- The string 'app' or 'project'.
       
   180     # name -- The name of the application or project.
       
   181     # directory -- The directory to which the layout template should be copied.
       
   182     # other_name -- When copying an application layout, this should be the name
       
   183     #               of the project.
       
   184     import re
       
   185     import shutil
       
   186     other = {'project': 'app', 'app': 'project'}[app_or_project]
       
   187     if not re.search(r'^\w+$', name): # If it's not a valid directory name.
       
   188         raise CommandError("%r is not a valid %s name. Please use only numbers, letters and underscores." % (name, app_or_project))
       
   189     top_dir = os.path.join(directory, name)
       
   190     try:
       
   191         os.mkdir(top_dir)
       
   192     except OSError, e:
       
   193         raise CommandError(e)
       
   194 
       
   195     # Determine where the app or project templates are. Use
       
   196     # django.__path__[0] because we don't know into which directory
       
   197     # django has been installed.
       
   198     template_dir = os.path.join(django.__path__[0], 'conf', '%s_template' % app_or_project)
       
   199 
       
   200     for d, subdirs, files in os.walk(template_dir):
       
   201         relative_dir = d[len(template_dir)+1:].replace('%s_name' % app_or_project, name)
       
   202         if relative_dir:
       
   203             os.mkdir(os.path.join(top_dir, relative_dir))
       
   204         for i, subdir in enumerate(subdirs):
       
   205             if subdir.startswith('.'):
       
   206                 del subdirs[i]
       
   207         for f in files:
       
   208             if f.endswith('.pyc'):
       
   209                 continue
       
   210             path_old = os.path.join(d, f)
       
   211             path_new = os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name))
       
   212             fp_old = open(path_old, 'r')
       
   213             fp_new = open(path_new, 'w')
       
   214             fp_new.write(fp_old.read().replace('{{ %s_name }}' % app_or_project, name).replace('{{ %s_name }}' % other, other_name))
       
   215             fp_old.close()
       
   216             fp_new.close()
       
   217             try:
       
   218                 shutil.copymode(path_old, path_new)
       
   219                 _make_writeable(path_new)
       
   220             except OSError:
       
   221                 sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))
       
   222 
       
   223 def _make_writeable(filename):
       
   224     "Makes sure that the file is writeable. Useful if our source is read-only."
       
   225     import stat
       
   226     if sys.platform.startswith('java'):
       
   227         # On Jython there is no os.access()
       
   228         return
       
   229     if not os.access(filename, os.W_OK):
       
   230         st = os.stat(filename)
       
   231         new_permissions = stat.S_IMODE(st.st_mode) | stat.S_IWUSR
       
   232         os.chmod(filename, new_permissions)