web/lib/django/core/management/commands/loaddata.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 import sys
       
     2 import os
       
     3 import gzip
       
     4 import zipfile
       
     5 from optparse import make_option
       
     6 
       
     7 from django.conf import settings
       
     8 from django.core import serializers
       
     9 from django.core.management.base import BaseCommand
       
    10 from django.core.management.color import no_style
       
    11 from django.db import connections, router, transaction, DEFAULT_DB_ALIAS
       
    12 from django.db.models import get_apps
       
    13 from django.utils.itercompat import product
       
    14 
       
    15 try:
       
    16     import bz2
       
    17     has_bz2 = True
       
    18 except ImportError:
       
    19     has_bz2 = False
       
    20 
       
    21 class Command(BaseCommand):
       
    22     help = 'Installs the named fixture(s) in the database.'
       
    23     args = "fixture [fixture ...]"
       
    24 
       
    25     option_list = BaseCommand.option_list + (
       
    26         make_option('--database', action='store', dest='database',
       
    27             default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load '
       
    28                 'fixtures into. Defaults to the "default" database.'),
       
    29     )
       
    30 
       
    31     def handle(self, *fixture_labels, **options):
       
    32         using = options.get('database', DEFAULT_DB_ALIAS)
       
    33 
       
    34         connection = connections[using]
       
    35         self.style = no_style()
       
    36 
       
    37         verbosity = int(options.get('verbosity', 1))
       
    38         show_traceback = options.get('traceback', False)
       
    39 
       
    40         # commit is a stealth option - it isn't really useful as
       
    41         # a command line option, but it can be useful when invoking
       
    42         # loaddata from within another script.
       
    43         # If commit=True, loaddata will use its own transaction;
       
    44         # if commit=False, the data load SQL will become part of
       
    45         # the transaction in place when loaddata was invoked.
       
    46         commit = options.get('commit', True)
       
    47 
       
    48         # Keep a count of the installed objects and fixtures
       
    49         fixture_count = 0
       
    50         object_count = 0
       
    51         models = set()
       
    52 
       
    53         humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path'
       
    54 
       
    55         # Get a cursor (even though we don't need one yet). This has
       
    56         # the side effect of initializing the test database (if
       
    57         # it isn't already initialized).
       
    58         cursor = connection.cursor()
       
    59 
       
    60         # Start transaction management. All fixtures are installed in a
       
    61         # single transaction to ensure that all references are resolved.
       
    62         if commit:
       
    63             transaction.commit_unless_managed(using=using)
       
    64             transaction.enter_transaction_management(using=using)
       
    65             transaction.managed(True, using=using)
       
    66 
       
    67         class SingleZipReader(zipfile.ZipFile):
       
    68             def __init__(self, *args, **kwargs):
       
    69                 zipfile.ZipFile.__init__(self, *args, **kwargs)
       
    70                 if settings.DEBUG:
       
    71                     assert len(self.namelist()) == 1, "Zip-compressed fixtures must contain only one file."
       
    72             def read(self):
       
    73                 return zipfile.ZipFile.read(self, self.namelist()[0])
       
    74 
       
    75         compression_types = {
       
    76             None:   file,
       
    77             'gz':   gzip.GzipFile,
       
    78             'zip':  SingleZipReader
       
    79         }
       
    80         if has_bz2:
       
    81             compression_types['bz2'] = bz2.BZ2File
       
    82 
       
    83         app_module_paths = []
       
    84         for app in get_apps():
       
    85             if hasattr(app, '__path__'):
       
    86                 # It's a 'models/' subpackage
       
    87                 for path in app.__path__:
       
    88                     app_module_paths.append(path)
       
    89             else:
       
    90                 # It's a models.py module
       
    91                 app_module_paths.append(app.__file__)
       
    92 
       
    93         app_fixtures = [os.path.join(os.path.dirname(path), 'fixtures') for path in app_module_paths]
       
    94         for fixture_label in fixture_labels:
       
    95             parts = fixture_label.split('.')
       
    96 
       
    97             if len(parts) > 1 and parts[-1] in compression_types:
       
    98                 compression_formats = [parts[-1]]
       
    99                 parts = parts[:-1]
       
   100             else:
       
   101                 compression_formats = compression_types.keys()
       
   102 
       
   103             if len(parts) == 1:
       
   104                 fixture_name = parts[0]
       
   105                 formats = serializers.get_public_serializer_formats()
       
   106             else:
       
   107                 fixture_name, format = '.'.join(parts[:-1]), parts[-1]
       
   108                 if format in serializers.get_public_serializer_formats():
       
   109                     formats = [format]
       
   110                 else:
       
   111                     formats = []
       
   112 
       
   113             if formats:
       
   114                 if verbosity > 1:
       
   115                     print "Loading '%s' fixtures..." % fixture_name
       
   116             else:
       
   117                 sys.stderr.write(
       
   118                     self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format." %
       
   119                         (fixture_name, format)))
       
   120                 transaction.rollback(using=using)
       
   121                 transaction.leave_transaction_management(using=using)
       
   122                 return
       
   123 
       
   124             if os.path.isabs(fixture_name):
       
   125                 fixture_dirs = [fixture_name]
       
   126             else:
       
   127                 fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + ['']
       
   128 
       
   129             for fixture_dir in fixture_dirs:
       
   130                 if verbosity > 1:
       
   131                     print "Checking %s for fixtures..." % humanize(fixture_dir)
       
   132 
       
   133                 label_found = False
       
   134                 for combo in product([using, None], formats, compression_formats):
       
   135                     database, format, compression_format = combo
       
   136                     file_name = '.'.join(
       
   137                         p for p in [
       
   138                             fixture_name, database, format, compression_format
       
   139                         ]
       
   140                         if p
       
   141                     )
       
   142 
       
   143                     if verbosity > 1:
       
   144                         print "Trying %s for %s fixture '%s'..." % \
       
   145                             (humanize(fixture_dir), file_name, fixture_name)
       
   146                     full_path = os.path.join(fixture_dir, file_name)
       
   147                     open_method = compression_types[compression_format]
       
   148                     try:
       
   149                         fixture = open_method(full_path, 'r')
       
   150                         if label_found:
       
   151                             fixture.close()
       
   152                             print self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting." %
       
   153                                 (fixture_name, humanize(fixture_dir)))
       
   154                             transaction.rollback(using=using)
       
   155                             transaction.leave_transaction_management(using=using)
       
   156                             return
       
   157                         else:
       
   158                             fixture_count += 1
       
   159                             objects_in_fixture = 0
       
   160                             if verbosity > 0:
       
   161                                 print "Installing %s fixture '%s' from %s." % \
       
   162                                     (format, fixture_name, humanize(fixture_dir))
       
   163                             try:
       
   164                                 objects = serializers.deserialize(format, fixture, using=using)
       
   165                                 for obj in objects:
       
   166                                     if router.allow_syncdb(using, obj.object.__class__):
       
   167                                         objects_in_fixture += 1
       
   168                                         models.add(obj.object.__class__)
       
   169                                         obj.save(using=using)
       
   170                                 object_count += objects_in_fixture
       
   171                                 label_found = True
       
   172                             except (SystemExit, KeyboardInterrupt):
       
   173                                 raise
       
   174                             except Exception:
       
   175                                 import traceback
       
   176                                 fixture.close()
       
   177                                 transaction.rollback(using=using)
       
   178                                 transaction.leave_transaction_management(using=using)
       
   179                                 if show_traceback:
       
   180                                     traceback.print_exc()
       
   181                                 else:
       
   182                                     sys.stderr.write(
       
   183                                         self.style.ERROR("Problem installing fixture '%s': %s\n" %
       
   184                                              (full_path, ''.join(traceback.format_exception(sys.exc_type,
       
   185                                                  sys.exc_value, sys.exc_traceback)))))
       
   186                                 return
       
   187                             fixture.close()
       
   188 
       
   189                             # If the fixture we loaded contains 0 objects, assume that an
       
   190                             # error was encountered during fixture loading.
       
   191                             if objects_in_fixture == 0:
       
   192                                 sys.stderr.write(
       
   193                                     self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)" %
       
   194                                         (fixture_name)))
       
   195                                 transaction.rollback(using=using)
       
   196                                 transaction.leave_transaction_management(using=using)
       
   197                                 return
       
   198 
       
   199                     except Exception, e:
       
   200                         if verbosity > 1:
       
   201                             print "No %s fixture '%s' in %s." % \
       
   202                                 (format, fixture_name, humanize(fixture_dir))
       
   203 
       
   204         # If we found even one object in a fixture, we need to reset the
       
   205         # database sequences.
       
   206         if object_count > 0:
       
   207             sequence_sql = connection.ops.sequence_reset_sql(self.style, models)
       
   208             if sequence_sql:
       
   209                 if verbosity > 1:
       
   210                     print "Resetting sequences"
       
   211                 for line in sequence_sql:
       
   212                     cursor.execute(line)
       
   213 
       
   214         if commit:
       
   215             transaction.commit(using=using)
       
   216             transaction.leave_transaction_management(using=using)
       
   217 
       
   218         if object_count == 0:
       
   219             if verbosity > 0:
       
   220                 print "No fixtures found."
       
   221         else:
       
   222             if verbosity > 0:
       
   223                 print "Installed %d object(s) from %d fixture(s)" % (object_count, fixture_count)
       
   224 
       
   225         # Close the DB connection. This is required as a workaround for an
       
   226         # edge case in MySQL: if the same connection is used to
       
   227         # create tables, load data, and query, the query can return
       
   228         # incorrect results. See Django #7572, MySQL #37735.
       
   229         if commit:
       
   230             connection.close()