Django

Code

Changeset 5898

Show
Ignore:
Timestamp:
08/16/07 01:06:55 (9 months ago)
Author:
adrian
Message:

Major refactoring of django.core.management -- it's now a package rather than a 1730-line single module. All django-admin/manage.py commands are now stored in separate modules. This is backwards-incompatible for people who used django.core.management functions directly

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/core/management/__init__.py

    r5884 r5898  
    1 # Django management-related functions, including "CREATE TABLE" generation and 
    2 # development-server initialization. 
    3  
    41import django 
    5 from django.core.exceptions import ImproperlyConfigured 
    62from optparse import OptionParser 
    7 from django.utils import termcolors 
    8 import os, re, shutil, sys, textwrap 
    9  
    10 try: 
    11     set 
    12 except NameError: 
    13     from sets import Set as set   # Python 2.3 fallback 
     3import os 
     4import sys 
     5import textwrap 
    146 
    157# For backwards compatibility: get_version() used to be in this module. 
    168get_version = django.get_version 
    179 
    18 MODULE_TEMPLATE = '''    {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%} 
    19     <tr> 
    20         <th>{%% if perms.%(app)s.%(changeperm)s %%}<a href="%(app)s/%(mod)s/">{%% endif %%}%(name)s{%% if perms.%(app)s.%(changeperm)s %%}</a>{%% endif %%}</th> 
    21         <td class="x50">{%% if perms.%(app)s.%(addperm)s %%}<a href="%(app)s/%(mod)s/add/" class="addlink">{%% endif %%}Add{%% if perms.%(app)s.%(addperm)s %%}</a>{%% endif %%}</td> 
    22         <td class="x75">{%% if perms.%(app)s.%(changeperm)s %%}<a href="%(app)s/%(mod)s/" class="changelink">{%% endif %%}Change{%% if perms.%(app)s.%(changeperm)s %%}</a>{%% endif %%}</td> 
    23     </tr> 
    24     {%% endif %%}''' 
     10def load_command_class(name): 
     11    """ 
     12    Given a command name, returns the Command class instance. Raises 
     13    ImportError if it doesn't exist. 
     14    """ 
     15    # Let the ImportError propogate. 
     16    return getattr(__import__('django.core.management.commands.%s' % name, {}, {}, ['Command']), 'Command')() 
    2517 
    26 APP_ARGS = '[appname ...]' 
     18def call_command(name, *args, **options): 
     19    """ 
     20    Calls the given command, with the given options and args/kwargs. 
     21     
     22    This is the primary API you should use for calling specific commands. 
     23     
     24    Some examples: 
     25        call_command('syncdb') 
     26        call_command('shell', plain=True) 
     27        call_command('sqlall', 'myapp') 
     28    """ 
     29    klass = load_command_class(name) 
     30    return klass.execute(*args, **options) 
    2731 
    28 # Use django.__path__[0] because we don't know into which directory django 
    29 # has been installed. 
    30 PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template') 
     32class ManagementUtility(object): 
     33    """ 
     34    Encapsulates the logic of the django-admin.py and manage.py utilities. 
    3135 
    32 INVALID_PROJECT_NAMES = ('django', 'site', 'test') 
     36    A ManagementUtility has a number of commands, which can be manipulated 
     37    by editing the self.commands dictionary. 
     38    """ 
     39    def __init__(self): 
     40        self.commands = self.default_commands() 
    3341 
    34 # Set up the terminal color scheme. 
    35 class dummy: pass 
    36 style = dummy() 
    37 style.ERROR = termcolors.make_style(fg='red', opts=('bold',)) 
    38 style.ERROR_OUTPUT = termcolors.make_style(fg='red', opts=('bold',)) 
    39 style.NOTICE = termcolors.make_style(fg='red') 
    40 style.SQL_FIELD = termcolors.make_style(fg='green', opts=('bold',)) 
    41 style.SQL_COLTYPE = termcolors.make_style(fg='green') 
    42 style.SQL_KEYWORD = termcolors.make_style(fg='yellow') 
    43 style.SQL_TABLE = termcolors.make_style(opts=('bold',)) 
    44 del dummy 
     42    def default_commands(self): 
     43        """ 
     44        Returns a dictionary of instances of all available Command classes. 
    4545 
    46 def disable_termcolors(): 
    47     class dummy: 
    48         def __getattr__(self, attr): 
    49             return lambda x: x 
    50     global style 
    51     style = dummy() 
     46        This works by looking for and loading all Python modules in the 
     47        django.core.management.commands package. 
    5248 
    53 # Disable terminal coloring on Windows, Pocket PC, or if somebody's piping the output. 
    54 if sys.platform == 'win32' or sys.platform == 'Pocket PC' or not sys.stdout.isatty(): 
    55     disable_termcolors() 
     49        The dictionary is in the format {name: command_instance}. 
     50        """ 
     51        command_dir = os.path.join(__path__[0], 'commands') 
     52        names = [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')] 
     53        return dict([(name, load_command_class(name)) for name in names]) 
    5654 
    57 def _is_valid_dir_name(s): 
    58     return bool(re.search(r'^\w+$', s)) 
     55    def usage(self): 
     56        """ 
     57        Returns a usage string, for use with optparse. 
    5958 
    60 def _get_installed_models(table_list): 
    61     "Gets a set of all models that are installed, given a list of existing tables" 
    62     from django.db import backend, models 
    63     all_models = [] 
    64     for app in models.get_apps(): 
    65         for model in models.get_models(app): 
    66             all_models.append(model) 
    67     if backend.uses_case_insensitive_names: 
    68         converter = lambda x: x.upper() 
    69     else: 
    70         converter = lambda x: x 
    71     return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)]) 
     59        The string doesn't include the options (e.g., "--verbose"), because 
     60        optparse puts those in automatically. 
     61        """ 
     62        usage = ["%prog command [options]\nactions:"] 
     63        commands = self.commands.items() 
     64        commands.sort() 
     65        for name, cmd in commands: 
     66            usage.append('  %s %s' % (name, cmd.args)) 
     67            usage.extend(textwrap.wrap(cmd.help, initial_indent='    ', subsequent_indent='    ')) 
     68            usage.append('') 
     69        return '\n'.join(usage[:-1]) # Cut off the last list element, an empty space. 
    7270 
    73 def _get_table_list(): 
    74     "Gets a list of all db tables that are physically installed." 
    75     from django.db import connection, get_introspection_module 
    76     cursor = connection.cursor() 
    77     return get_introspection_module().get_table_list(cursor) 
     71    def execute(self, argv=None): 
     72        """ 
     73        Parses the given argv from the command line, determines which command 
     74        to run and runs the command. 
     75        """ 
     76        if argv is None: 
     77            argv = sys.argv 
    7878 
    79 def _get_sequence_list(): 
    80     "Returns a list of information about all DB sequences for all models in all apps" 
    81     from django.db import models 
     79        # Create the parser object and parse the command-line args. 
     80        # TODO: Ideally each Command class would register its own options for 
     81        # add_option(), but we'd need to figure out how to allow for multiple 
     82        # Commands using the same options. The optparse library gets in the way 
     83        # by checking for conflicts: 
     84        # http://docs.python.org/lib/optparse-conflicts-between-options.html 
     85        parser = OptionParser(usage=self.usage(), version=get_version()) 
     86        parser.add_option('--settings', 
     87            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.') 
     88        parser.add_option('--pythonpath', 
     89            help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".') 
     90        parser.add_option('--plain', action='store_true', dest='plain', 
     91            help='When using "shell": Tells Django to use plain Python, not IPython.') 
     92        parser.add_option('--noinput', action='store_false', dest='interactive', default=True, 
     93            help='Tells Django to NOT prompt the user for input of any kind.') 
     94        parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True, 
     95            help='When using "runserver": Tells Django to NOT use the auto-reloader.') 
     96        parser.add_option('--format', default='json', dest='format', 
     97            help='Specifies the output serialization format for fixtures') 
     98        parser.add_option('--indent', default=None, dest='indent', 
     99            type='int', help='Specifies the indent level to use when pretty-printing output') 
     100        parser.add_option('--verbosity', action='store', dest='verbosity', default='1', 
     101            type='choice', choices=['0', '1', '2'], 
     102            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output') 
     103        parser.add_option('--adminmedia', dest='admin_media_path', default='', 
     104            help='When using "runserver": Specifies the directory from which to serve admin media.') 
     105        options, args = parser.parse_args(argv[1:]) 
    82106 
    83     apps = models.get_apps() 
    84     sequence_list = [] 
     107        # If the 'settings' or 'pythonpath' options were submitted, activate those. 
     108        if options.settings: 
     109            os.environ['DJANGO_SETTINGS_MODULE'] = options.settings 
     110        if options.pythonpath: 
     111            sys.path.insert(0, options.pythonpath) 
    85112 
    86     for app in apps: 
    87         for model in models.get_models(app): 
    88             for f in model._meta.fields: 
    89                 if isinstance(f, models.AutoField): 
    90                     sequence_list.append({'table':model._meta.db_table,'column':f.column,}) 
    91                     break # Only one AutoField is allowed per model, so don't bother continuing. 
     113        # Run the appropriate command. 
     114        try: 
     115            command_name = args[0] 
     116        except IndexError: 
     117            sys.stderr.write("Type '%s --help' for usage.\n" % os.path.basename(argv[0])) 
     118            sys.exit(1) 
     119        try: 
     120            command = self.commands[command_name] 
     121        except KeyError: 
     122            sys.stderr.write("Unknown command: %r\nType '%s --help' for usage.\n" % (command_name, os.path.basename(argv[0]))) 
     123            sys.exit(1) 
     124        command.execute(*args[1:], **options.__dict__) 
    92125 
    93             for f in model._meta.many_to_many: 
    94                 sequence_list.append({'table':f.m2m_db_table(),'column':None,}) 
     126class ProjectManagementUtility(ManagementUtility): 
     127    """ 
     128    A ManagementUtility that is specific to a particular Django project. 
     129    As such, its commands are slightly different than those of its parent 
     130    class. 
    95131 
    96     return sequence_list 
     132    In practice, this class represents manage.py, whereas ManagementUtility 
     133    represents django-admin.py. 
     134    """ 
     135    def __init__(self, project_directory): 
     136        super(ProjectManagementUtility, self).__init__() 
    97137 
    98 def get_sql_create(app): 
    99     "Returns a list of the CREATE TABLE SQL statements for the given app." 
    100     from django.db import models 
    101     from django.conf import settings 
     138        # Remove the "startproject" command from self.commands, because 
     139        # that's a django-admin.py command, not a manage.py command. 
     140        del self.commands['startproject'] 
    102141 
    103     if settings.DATABASE_ENGINE == 'dummy': 
    104         # This must be the "dummy" database backend, which means the user 
    105         # hasn't set DATABASE_ENGINE. 
    106         sys.stderr.write(style.ERROR("Error: Django doesn't know which syntax to use for your SQL statements,\n" + 
    107             "because you haven't specified the DATABASE_ENGINE setting.\n" + 
    108             "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.\n")) 
    109         sys.exit(1) 
    110  
    111     # Get installed models, so we generate REFERENCES right. 
    112     # We trim models from the current app so that the sqlreset command does not 
    113     # generate invalid SQL (leaving models out of known_models is harmless, so 
    114     # we can be conservative). 
    115     app_models = models.get_models(app) 
    116     final_output = [] 
    117     known_models = set([model for model in _get_installed_models(_get_table_list()) if model not in app_models]) 
    118     pending_references = {} 
    119  
    120     for model in app_models: 
    121         output, references = _get_sql_model_create(model, known_models) 
    122         final_output.extend(output) 
    123         for refto, refs in references.items(): 
    124             pending_references.setdefault(refto,[]).extend(refs) 
    125         final_output.extend(_get_sql_for_pending_references(model, pending_references)) 
    126         # Keep track of the fact that we've created the table for this model. 
    127         known_models.add(model) 
    128  
    129     # Create the many-to-many join tables. 
    130     for model in app_models: 
    131         final_output.extend(_get_many_to_many_sql_for_model(model)) 
    132  
    133     # Handle references to tables that are from other apps 
    134     # but don't exist physically 
    135     not_installed_models = set(pending_references.keys()) 
    136     if not_installed_models: 
    137         alter_sql = [] 
    138         for model in not_installed_models: 
    139             alter_sql.extend(['-- ' + sql for sql in 
    140                 _get_sql_for_pending_references(model, pending_references)]) 
    141         if alter_sql: 
    142             final_output.append('-- The following references should be added but depend on non-existent tables:') 
    143             final_output.extend(alter_sql) 
    144  
    145     return final_output 
    146 get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given app name(s)." 
    147 get_sql_create.args = APP_ARGS 
    148  
    149 def _get_sql_model_create(model, known_models=set()): 
    150     """ 
    151     Get the SQL required to create a single model. 
    152  
    153     Returns list_of_sql, pending_references_dict 
    154     """ 
    155     from django.db import backend, models 
    156  
    157     opts = model._meta 
    158     final_output = [] 
    159     table_output = [] 
    160     pending_references = {} 
    161     for f in opts.fields: 
    162         col_type = f.db_type() 
    163         tablespace = f.db_tablespace or opts.db_tablespace 
    164         if col_type is None: 
    165             # Skip ManyToManyFields, because they're not represented as 
    166             # database columns in this table. 
    167             continue 
    168         # Make the definition (e.g. 'foo VARCHAR(30)') for this field. 
    169         field_output = [style.SQL_FIELD(backend.quote_name(f.column)), 
    170             style.SQL_COLTYPE(col_type)] 
    171         field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 
    172         if f.unique and (not f.primary_key or backend.allows_unique_and_pk): 
    173             field_output.append(style.SQL_KEYWORD('UNIQUE')) 
    174         if f.primary_key: 
    175             field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 
    176         if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys: 
    177             # We must specify the index tablespace inline, because we 
    178             # won't be generating a CREATE INDEX statement for this field. 
    179             field_output.append(backend.get_tablespace_sql(tablespace, inline=True)) 
    180         if f.rel: 
    181             if f.rel.to in known_models: 
    182                 field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ 
    183                     style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \ 
    184                     style.SQL_FIELD(backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' + 
    185                     backend.get_deferrable_sql() 
    186                 ) 
    187             else: 
    188                 # We haven't yet created the table to which this field 
    189                 # is related, so save it for later. 
    190                 pr = pending_references.setdefault(f.rel.to, []).append((model, f)) 
    191         table_output.append(' '.join(field_output)) 
    192     if opts.order_with_respect_to: 
    193         table_output.append(style.SQL_FIELD(backend.quote_name('_order')) + ' ' + \ 
    194             style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \ 
    195             style.SQL_KEYWORD('NULL')) 
    196     for field_constraints in opts.unique_together: 
    197         table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ 
    198             ", ".join([backend.quote_name(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints])) 
    199  
    200     full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(backend.quote_name(opts.db_table)) + ' ('] 
    201     for i, line in enumerate(table_output): # Combine and add commas. 
    202         full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or '')) 
    203     full_statement.append(')') 
    204     if opts.db_tablespace and backend.supports_tablespaces: 
    205         full_statement.append(backend.get_tablespace_sql(opts.db_tablespace)) 
    206     full_statement.append(';') 
    207     final_output.append('\n'.join(full_statement)) 
    208  
    209     if opts.has_auto_field and hasattr(backend, 'get_autoinc_sql'): 
    210         # Add any extra SQL needed to support auto-incrementing primary keys 
    211         autoinc_sql = backend.get_autoinc_sql(opts.db_table) 
    212         if autoinc_sql: 
    213             for stmt in autoinc_sql: 
    214                 final_output.append(stmt) 
    215  
    216     return final_output, pending_references 
    217  
    218 def _get_sql_for_pending_references(model, pending_references): 
    219     """ 
    220     Get any ALTER TABLE statements to add constraints after the fact. 
    221     """ 
    222     from django.db import backend 
    223     from django.db.backends.util import truncate_name 
    224  
    225     final_output = [] 
    226     if backend.supports_constraints: 
    227         opts = model._meta 
    228         if model in pending_references: 
    229             for rel_class, f in pending_references[model]: 
    230                 rel_opts = rel_class._meta 
    231                 r_table = rel_opts.db_table 
    232                 r_col = f.column 
    233                 table = opts.db_table 
    234                 col = opts.get_field(f.rel.field_name).column 
    235                 # For MySQL, r_name must be unique in the first 64 characters. 
    236                 # So we are careful with character usage here. 
    237                 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 
    238                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 
    239                     (backend.quote_name(r_table), truncate_name(r_name, backend.get_max_name_length()), 
    240                     backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col), 
    241                     backend.get_deferrable_sql())) 
    242             del pending_references[model] 
    243     return final_output 
    244  
    245 def _get_many_to_many_sql_for_model(model): 
    246     from django.db import backend, models 
    247     from django.contrib.contenttypes import generic 
    248  
    249     opts = model._meta 
    250     final_output = [] 
    251     for f in opts.many_to_many: 
    252         if not isinstance(f.rel, generic.GenericRel): 
    253             tablespace = f.db_tablespace or opts.db_tablespace 
    254             if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys: 
    255                 tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace, inline=True) 
    256             else: 
    257                 tablespace_sql = '' 
    258             table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ 
    259                 style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' ('] 
    260             table_output.append('    %s %s %s%s,' % \ 
    261                 (style.SQL_FIELD(backend.quote_name('id')), 
    262                 style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()), 
    263                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), 
    264                 tablespace_sql)) 
    265             table_output.append('    %s %s %s %s (%s)%s,' % \ 
    266                 (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 
    267                 style.SQL_COLTYPE(models.ForeignKey(model).db_type()), 
    268                 style.SQL_KEYWORD('NOT NULL REFERENCES'), 
    269                 style.SQL_TABLE(backend.quote_name(opts.db_table)), 
    270                 style.SQL_FIELD(backend.quote_name(opts.pk.column)), 
    271                 backend.get_deferrable_sql())) 
    272             table_output.append('    %s %s %s %s (%s)%s,' % \ 
    273                 (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), 
    274                 style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()), 
    275                 style.SQL_KEYWORD('NOT NULL REFERENCES'), 
    276                 style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)), 
    277                 style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)), 
    278                 backend.get_deferrable_sql())) 
    279             table_output.append('    %s (%s, %s)%s' % \ 
    280                 (style.SQL_KEYWORD('UNIQUE'), 
    281                 style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 
    282                 style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), 
    283                 tablespace_sql)) 
    284             table_output.append(')') 
    285             if opts.db_tablespace and backend.supports_tablespaces: 
    286                 # f.db_tablespace is only for indices, so ignore its value here. 
    287                 table_output.append(backend.get_tablespace_sql(opts.db_tablespace)) 
    288             table_output.append(';') 
    289             final_output.append('\n'.join(table_output)) 
    290  
    291             # Add any extra SQL needed to support auto-incrementing PKs 
    292             autoinc_sql = backend.get_autoinc_sql(f.m2m_db_table()) 
    293             if autoinc_sql: 
    294                 for stmt in autoinc_sql: 
    295                     final_output.append(stmt) 
    296  
    297     return final_output 
    298  
    299 def get_sql_delete(app): 
    300     "Returns a list of the DROP TABLE SQL statements for the given app." 
    301     from django.db import backend, connection, models, get_introspection_module 
    302     from django.db.backends.util import truncate_name 
    303     introspection = get_introspection_module() 
    304  
    305     # This should work even if a connection isn't available 
    306     try: 
    307         cursor = connection.cursor() 
    308     except: 
    309         cursor = None 
    310  
    311     # Figure out which tables already exist 
    312     if cursor: 
    313         table_names = introspection.get_table_list(cursor) 
    314     else: 
    315         table_names = [] 
    316     if backend.uses_case_insensitive_names: 
    317         table_name_converter = str.upper 
    318     else: 
    319         table_name_converter = lambda x: x 
    320  
    321     output = [] 
    322  
    323     # Output DROP TABLE statements for standard application tables. 
    324     to_delete = set() 
    325  
    326     references_to_delete = {} 
    327     app_models = models.get_models(app) 
    328     for model in app_models: 
    329         if cursor and table_name_converter(model._meta.db_table) in table_names: 
    330             # The table exists, so it needs to be dropped 
    331             opts = model._meta 
    332             for f in opts.fields: 
    333                 if f.rel and f.rel.to not in to_delete: 
    334                     references_to_delete.setdefault(f.rel.to, []).append( (model, f) ) 
    335  
    336             to_delete.add(model) 
    337  
    338     for model in app_models: 
    339         if cursor and table_name_converter(model._meta.db_table) in table_names: 
    340             # Drop the table now 
    341             output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 
    342                 style.SQL_TABLE(backend.quote_name(model._meta.db_table)))) 
    343             if backend.supports_constraints and model in references_to_delete: 
    344                 for rel_class, f in references_to_delete[model]: 
    345                     table = rel_class._meta.db_table 
    346                     col = f.column 
    347                     r_table = model._meta.db_table 
    348                     r_col = model._meta.get_field(f.rel.field_name).column 
    349                     r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) 
    350                     output.append('%s %s %s %s;' % \ 
    351                         (style.SQL_KEYWORD('ALTER TABLE'), 
    352                         style.SQL_TABLE(backend.quote_name(table)), 
    353                         style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()), 
    354                         style.SQL_FIELD(truncate_name(r_name, backend.get_max_name_length())))) 
    355                 del references_to_delete[model] 
    356             if model._meta.has_auto_field and hasattr(backend, 'get_drop_sequence'): 
    357                 output.append(backend.get_drop_sequence(model._meta.db_table)) 
    358  
    359     # Output DROP TABLE statements for many-to-many tables. 
    360     for model in app_models: 
    361         opts = model._meta 
    362         for f in opts.many_to_many: 
    363             if cursor and table_name_converter(f.m2m_db_table()) in table_names: 
    364                 output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), 
    365                     style.SQL_TABLE(backend.quote_name(f.m2m_db_table())))) 
    366                 if hasattr(backend, 'get_drop_sequence'): 
    367                     output.append(backend.get_drop_sequence("%s_%s" % (model._meta.db_table, f.column))) 
    368  
    369  
    370     app_label = app_models[0]._meta.app_label 
    371  
    372     # Close database connection explicitly, in case this output is being piped 
    373     # directly into a database client, to avoid locking issues. 
    374     if cursor: 
    375         cursor.close() 
    376         connection.close() 
    377  
    378     return output[::-1] # Reverse it, to deal with table dependencies. 
    379 get_sql_delete.help_doc = "Prints the DROP TABLE SQL statements for the given app name(s)." 
    380 get_sql_delete.args = APP_ARGS 
    381  
    382 def get_sql_reset(app): 
    383     "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module." 
    384     return get_sql_delete(app) + get_sql_all(app) 
    385 get_sql_reset.help_doc = "Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given app name(s)." 
    386 get_sql_reset.args = APP_ARGS 
    387  
    388 def get_sql_flush(): 
    389     "Returns a list of the SQL statements used to flush the database" 
    390     from django.db import backend 
    391     statements = backend.get_sql_flush(style, _get_table_list(), _get_sequence_list()) 
    392     return statements 
    393 get_sql_flush.help_doc = "Returns a list of the SQL statements required to return all tables in the database to the state they were in just after they were installed." 
    394 get_sql_flush.args = '' 
    395  
    396 def get_custom_sql_for_model(model): 
    397     from django.db import models 
    398     from django.conf import settings 
    399  
    400     opts = model._meta 
    401     app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql')) 
    402     output = [] 
    403  
    404     # Some backends can't execute more than one SQL statement at a time, 
    405     # so split into separate statements. 
    406     statements = re.compile(r";[ \t]*$", re.M) 
    407  
    408     # Find custom SQL, if it's available. 
    409     sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), settings.DATABASE_ENGINE)), 
    410                  os.path.join(app_dir, "%s.sql" % opts.object_name.lower())] 
    411     for sql_file in sql_files: 
    412         if os.path.exists(sql_file): 
    413             fp = open(sql_file, 'U') 
    414             for statement in statements.split(fp.read().decode(settings.FILE_CHARSET)): 
    415                 # Remove any comments from the file 
    416                 statement = re.sub(ur"--.*[\n\Z]", "", statement) 
    417                 if statement.strip(): 
    418                     output.append(statement + u";") 
    419             fp.close() 
    420  
    421     return output 
    422  
    423 def get_custom_sql(app): 
    424     "Returns a list of the custom table modifying SQL statements for the given app." 
    425     from django.db.models import get_models 
    426     output = [] 
    427  
    428     app_models = get_models(app) 
    429     app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql')) 
    430  
    431     for model in app_models: 
    432         output.extend(get_custom_sql_for_model(model)) 
    433  
    434     return output 
    435 get_custom_sql.help_doc = "Prints the custom table modifying SQL statements for the given app name(s)." 
    436 get_custom_sql.args = APP_ARGS 
    437  
    438 def get_sql_initial_data(apps): 
    439     "Returns a list of the initial INSERT SQL statements for the given app." 
    440     return style.ERROR("This action has been renamed. Try './manage.py sqlcustom %s'." % ' '.join(apps and apps or ['app1', 'app2'])) 
    441 get_sql_initial_data.help_doc = "RENAMED: see 'sqlcustom'" 
    442 get_sql_initial_data.args = '' 
    443  
    444 def get_sql_sequence_reset(app): 
    445     "Returns a list of the SQL statements to reset sequences for the given app." 
    446     from django.db import backend, models 
    447     return backend.get_sql_sequence_reset(style, models.get_models(app)) 
    448 get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting sequences for the given app name(s)." 
    449 get_sql_sequence_reset.args = APP_ARGS 
    450  
    451 def get_sql_indexes(app): 
    452     "Returns a list of the CREATE INDEX SQL statements for all models in the given app." 
    453     from django.db import models 
    454     output = [] 
    455     for model in models.get_models(app): 
    456         output.extend(get_sql_indexes_for_model(model)) 
    457     return output 
    458 get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given model module name(s)." 
    459 get_sql_indexes.args = APP_ARGS 
    460  
    461 def get_sql_indexes_for_model(model): 
    462     "Returns the CREATE INDEX SQL statements for a single model" 
    463     from django.db import backend 
    464     output = [] 
    465  
    466     for f in model._meta.fields: 
    467         if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys): 
    468             unique = f.unique and 'UNIQUE ' or '' 
    469             tablespace = f.db_tablespace or model._meta.db_tablespace 
    470             if tablespace and backend.supports_tablespaces: 
    471                 tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace) 
    472             else: 
    473                 tablespace_sql = '' 
    474             output.append( 
    475                 style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ 
    476                 style.SQL_TABLE(backend.quote_name('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \ 
    477                 style.SQL_KEYWORD('ON') + ' ' + \ 
    478                 style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \ 
    479                 "(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \ 
    480                 "%s;" % tablespace_sql 
    481             ) 
    482     return output 
    483  
    484 def get_sql_all(app): 
    485     "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module." 
    486     return get_sql_create(app) + get_custom_sql(app) + get_sql_indexes(app) 
    487 get_sql_all.help_doc = "Prints the CREATE TABLE, initial-data and CREATE INDEX SQL statements for the given model module name(s)." 
    488 get_sql_all.args = APP_ARGS 
    489  
    490 def _emit_post_sync_signal(created_models, verbosity, interactive): 
    491     from django.db import models 
    492     from django.dispatch import dispatcher 
    493     # Emit the post_sync signal for every application. 
    494     for app in models.get_apps(): 
    495         app_name = app.__name__.split('.')[-2] 
    496         if verbosity >= 2: 
    497             print "Running post-sync handlers for application", app_name 
    498         dispatcher.send(signal=models.signals.post_syncdb, sender=app, 
    499             app=app, created_models=created_models, 
    500             verbosity=verbosity, interactive=interactive) 
    501  
    502 def syncdb(verbosity=1, interactive=True): 
    503     "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 
    504     from django.db import backend, connection, transaction, models 
    505     from django.conf import settings 
    506  
    507     disable_termcolors() 
    508  
    509     # First, try validating the models. 
    510     _check_for_validation_errors() 
    511  
    512     # Import the 'management' module within each installed app, to register 
    513     # dispatcher events. 
    514     for app_name in settings.INSTALLED_APPS: 
    515         try: 
    516             __import__(app_name + '.management', {}, {}, ['']) 
    517         except ImportError: 
    518             pass 
    519  
    520     cursor = connection.cursor() 
    521  
    522     # Get a list of all existing database tables, 
    523     # so we know what needs to be added. 
    524     table_list = _get_table_list() 
    525     if backend.uses_case_insensitive_names: 
    526         table_name_converter = str.upper 
    527     else: 
    528         table_name_converter = lambda x: x 
    529  
    530     # Get a list of already installed *models* so that references work right. 
    531     seen_models = _get_installed_models(table_list) 
    532     created_models = set() 
    533     pending_references = {} 
    534  
    535     # Create the tables for each model 
    536     for app in models.get_apps(): 
    537         app_name = app.__name__.split('.')[-2] 
    538         model_list = models.get_models(app) 
    539         for model in model_list: 
    540             # Create the model's database table, if it doesn't already exist. 
    541             if verbosity >= 2: 
    542                 print "Processing %s.%s model" % (app_name, model._meta.object_name) 
    543             if table_name_converter(model._meta.db_table) in table_list: 
    544                 continue 
    545             sql, references = _get_sql_model_create(model, seen_models) 
    546             seen_models.add(model) 
    547             created_models.add(model) 
    548             for refto, refs in references.items(): 
    549                 pending_references.setdefault(refto, []).extend(refs) 
    550             sql.extend(_get_sql_for_pending_references(model, pending_references)) 
    551             if verbosity >= 1: 
    552                 print "Creating table %s" % model._meta.db_table 
    553             for statement in sql: 
    554                 cursor.execute(statement) 
    555             table_list.append(table_name_converter(model._meta.db_table)) 
    556  
    557     # Create the m2m tables. This must be done after all tables have been created 
    558     # to ensure that all referred tables will exist. 
    559     for app in models.get_apps(): 
    560         app_name = app.__name__.split('.')[-2] 
    561         model_list = models.get_models(app) 
    562         for model in model_list: 
    563             if model in created_models: 
    564                 sql = _get_many_to_many_sql_for_model(model) 
    565                 if sql: 
    566                     if verbosity >= 2: 
    567                         print "Creating many-to-many tables for %s.%s model" % (app_name, model._meta.object_name) 
    568                     for statement in sql: 
    569                         cursor.execute(statement) 
    570  
    571     transaction.commit_unless_managed() 
    572  
    573     # Send the post_syncdb signal, so individual apps can do whatever they need 
    574     # to do at this point. 
    575     _emit_post_sync_signal(created_models, verbosity, interactive) 
    576  
    577     # Install custom SQL for the app (but only if this 
    578     # is a model we've just created) 
    579     for app in models.get_apps(): 
    580         app_name = app.__name__.split('.')[-2] 
    581         for model in models.get_models(app): 
    582             if model in created_models: 
    583                 custom_sql = get_custom_sql_for_model(model) 
    584                 if custom_sql: 
    585                     if verbosity >= 1: 
    586                         print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name) 
    587                     try: 
    588                         for sql in custom_sql: 
    589                             cursor.execute(sql) 
    590                     except Exception, e: 
    591                         sys.stderr.write("Failed to install custom SQL for %s.%s model: %s" % \ 
    592                                             (app_name, model._meta.object_name, e)) 
    593                         transaction.rollback_unless_managed() 
    594                     else: 
    595                         transaction.commit_unless_managed() 
    596  
    597     # Install SQL indicies for all newly created models 
    598     for app in models.get_apps(): 
    599         app_name = app.__name__.split('.')[-2] 
    600         for model in models.get_models(app): 
    601             if model in created_models: 
    602                 index_sql = get_sql_indexes_for_model(model) 
    603                 if index_sql: 
    604                     if verbosity >= 1: 
    605                         print "Installing index for %s.%s model" % (app_name, model._meta.object_name) 
    606                     try: 
    607                         for sql in index_sql: 
    608                             cursor.execute(sql) 
    609                     except Exception, e: 
    610                         sys.stderr.write("Failed to install index for %s.%s model: %s" % \ 
    611                                             (app_name, model._meta.object_name, e)) 
    612                         transaction.rollback_unless_managed() 
    613                     else: 
    614                         transaction.commit_unless_managed() 
    615  
    616     # Install the 'initialdata' fixture, using format discovery 
    617     load_data(['initial_data'], verbosity=verbosity) 
    618 syncdb.help_doc = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 
    619 syncdb.args = '[--verbosity] [--noinput]' 
    620  
    621 def get_admin_index(app): 
    622     "Returns admin-index template snippet (in list form) for the given app." 
    623     from django.utils.text import capfirst 
    624     from django.db.models import get_models 
    625     output = [] 
    626     app_models = get_models(app) 
    627     app_label = app_models[0]._meta.app_label 
    628     output.append('{%% if perms.%s %%}' % app_label) 
    629     output.append('<div class="module"><h2>%s</h2><table>' % app_label.title()) 
    630     for model in app_models: 
    631         if model._meta.admin: 
    632             output.append(MODULE_TEMPLATE % { 
    633                 'app': app_label, 
    634                 'mod': model._meta.module_name, 
    635                 'name': capfirst(model._meta.verbose_name_plural), 
    636                 'addperm': model._meta.get_add_permission(), 
    637                 'changeperm': model._meta.get_change_permission(), 
    638             }) 
    639     output.append('</table></div>') 
    640     output.append('{% endif %}') 
    641     return output 
    642 get_admin_index.help_doc = "Prints the admin-index template snippet for the given app name(s)." 
    643 get_admin_index.args = APP_ARGS 
    644  
    645 def _module_to_dict(module, omittable=lambda k: k.startswith('_')): 
    646     "Converts a module namespace to a Python dictionary. Used by get_settings_diff." 
    647     return dict([(k, repr(v)) for k, v in module.__dict__.items() if not omittable(k)]) 
    648  
    649 def diffsettings(): 
    650     """ 
    651     Displays differences between the current settings.py and Django's 
    652     default settings. Settings that don't appear in the defaults are 
    653     followed by "###". 
    654     """ 
    655     # Inspired by Postfix's "postconf -n". 
    656     from django.conf import settings, global_settings 
    657  
    658     user_settings = _module_to_dict(settings._target) 
    659     default_settings = _module_to_dict(global_settings) 
    660  
    661     output = [] 
    662     keys = user_settings.keys() 
    663     keys.sort() 
    664     for key in keys: 
    665         if key not in default_settings: 
    666             output.append("%s = %s  ###" % (key, user_settings[key])) 
    667         elif user_settings[key] != default_settings[key]: 
    668             output.append("%s = %s" % (key, user_settings[key])) 
    669     print '\n'.join(output) 
    670 diffsettings.args = "" 
    671  
    672 def reset(app, interactive=True): 
    673     "Executes the equivalent of 'get_sql_reset' in the current database." 
    674     from django.db import connection, transaction 
    675     from django.conf import settings 
    676     app_name = app.__name__.split('.')[-2] 
    677  
    678     disable_termcolors() 
    679  
    680     # First, try validating the models. 
    681     _check_for_validation_errors(app) 
    682     sql_list = get_sql_reset(app) 
    683  
    684     if interactive: 
    685         confirm = raw_input(""" 
    686 You have requested a database reset. 
    687 This will IRREVERSIBLY DESTROY any data for 
    688 the "%s" application in the database "%s". 
    689 Are you sure you want to do this? 
    690  
    691 Type 'yes' to continue, or 'no' to cancel: """ % (app_name, settings.DATABASE_NAME)) 
    692     else: 
    693         confirm = 'yes' 
    694  
    695     if confirm == 'yes': 
    696         try: 
    697             cursor = connection.cursor() 
    698             for sql in sql_list: 
    699                 cursor.execute(sql) 
    700         except Exception, e: 
    701             sys.stderr.write(style.ERROR("""Error: %s couldn't be reset. Possible reasons: 
    702   * The database isn't running or isn't configured correctly. 
    703   * At least one of the database tables doesn't exist. 
    704   * The SQL was invalid. 
    705 Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run. 
    706 The full error: """ % (app_name, app_name)) + style.ERROR_OUTPUT(str(e)) + '\n') 
    707             transaction.rollback_unless_managed() 
    708             sys.exit(1) 
    709         transaction.commit_unless_managed() 
    710     else: 
    711         print "Reset cancelled." 
    712 reset.help_doc = "Executes ``sqlreset`` for the given app(s) in the current database." 
    713 reset.args = '[--noinput]' + APP_ARGS 
    714  
    715 def flush(verbosity=1, interactive=True): 
    716     "Returns all tables in the database to the same state they were in immediately after syncdb." 
    717     from django.conf import settings 
    718     from django.db import connection, transaction, models 
    719     from django.dispatch import dispatcher 
    720  
    721     disable_termcolors() 
    722  
    723     # First, try validating the models. 
    724     _check_for_validation_errors() 
    725  
    726     # Import the 'management' module within each installed app, to register 
    727     # dispatcher events. 
    728     for app_name in settings.INSTALLED_APPS: 
    729         try: 
    730             __import__(app_name + '.management', {}, {}, ['']) 
    731         except ImportError: 
    732             pass 
    733  
    734     sql_list = get_sql_flush() 
    735  
    736     if interactive: 
    737         confirm = raw_input(""" 
    738 You have requested a flush of the database. 
    739 This will IRREVERSIBLY DESTROY all data currently in the database, 
    740 and return each table to the state it was in after syncdb. 
    741 Are you sure you want to do this? 
    742  
    743 Type 'yes' to continue, or 'no' to cancel: """) 
    744     else: 
    745         confirm = 'yes' 
    746  
    747     if confirm == 'yes': 
    748         try: 
    749             cursor = connection.cursor() 
    750             for sql in sql_list: 
    751                 cursor.execute(s