web/lib/django/core/management/commands/dumpdata.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 from django.core.exceptions import ImproperlyConfigured
     1 from django.core.exceptions import ImproperlyConfigured
     2 from django.core.management.base import BaseCommand, CommandError
     2 from django.core.management.base import BaseCommand, CommandError
     3 from django.core import serializers
     3 from django.core import serializers
       
     4 from django.db import connections, router, DEFAULT_DB_ALIAS
     4 from django.utils.datastructures import SortedDict
     5 from django.utils.datastructures import SortedDict
     5 
     6 
     6 from optparse import make_option
     7 from optparse import make_option
     7 
     8 
     8 class Command(BaseCommand):
     9 class Command(BaseCommand):
     9     option_list = BaseCommand.option_list + (
    10     option_list = BaseCommand.option_list + (
    10         make_option('--format', default='json', dest='format',
    11         make_option('--format', default='json', dest='format',
    11             help='Specifies the output serialization format for fixtures.'),
    12             help='Specifies the output serialization format for fixtures.'),
    12         make_option('--indent', default=None, dest='indent', type='int',
    13         make_option('--indent', default=None, dest='indent', type='int',
    13             help='Specifies the indent level to use when pretty-printing output'),
    14             help='Specifies the indent level to use when pretty-printing output'),
       
    15         make_option('--database', action='store', dest='database',
       
    16             default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load '
       
    17                 'fixtures into. Defaults to the "default" database.'),
    14         make_option('-e', '--exclude', dest='exclude',action='append', default=[],
    18         make_option('-e', '--exclude', dest='exclude',action='append', default=[],
    15             help='App to exclude (use multiple --exclude to exclude multiple apps).'),
    19             help='App to exclude (use multiple --exclude to exclude multiple apps).'),
       
    20         make_option('-n', '--natural', action='store_true', dest='use_natural_keys', default=False,
       
    21             help='Use natural keys if they are available.'),
    16     )
    22     )
    17     help = 'Output the contents of the database as a fixture of the given format.'
    23     help = 'Output the contents of the database as a fixture of the given format.'
    18     args = '[appname ...]'
    24     args = '[appname appname.ModelName ...]'
    19 
    25 
    20     def handle(self, *app_labels, **options):
    26     def handle(self, *app_labels, **options):
    21         from django.db.models import get_app, get_apps, get_models, get_model
    27         from django.db.models import get_app, get_apps, get_models, get_model
    22 
    28 
    23         format = options.get('format','json')
    29         format = options.get('format','json')
    24         indent = options.get('indent',None)
    30         indent = options.get('indent',None)
       
    31         using = options.get('database', DEFAULT_DB_ALIAS)
       
    32         connection = connections[using]
    25         exclude = options.get('exclude',[])
    33         exclude = options.get('exclude',[])
    26         show_traceback = options.get('traceback', False)
    34         show_traceback = options.get('traceback', False)
       
    35         use_natural_keys = options.get('use_natural_keys', False)
    27 
    36 
    28         excluded_apps = [get_app(app_label) for app_label in exclude]
    37         excluded_apps = set(get_app(app_label) for app_label in exclude)
    29 
    38 
    30         if len(app_labels) == 0:
    39         if len(app_labels) == 0:
    31             app_list = SortedDict([(app, None) for app in get_apps() if app not in excluded_apps])
    40             app_list = SortedDict((app, None) for app in get_apps() if app not in excluded_apps)
    32         else:
    41         else:
    33             app_list = SortedDict()
    42             app_list = SortedDict()
    34             for label in app_labels:
    43             for label in app_labels:
    35                 try:
    44                 try:
    36                     app_label, model_label = label.split('.')
    45                     app_label, model_label = label.split('.')
    65         try:
    74         try:
    66             serializers.get_serializer(format)
    75             serializers.get_serializer(format)
    67         except KeyError:
    76         except KeyError:
    68             raise CommandError("Unknown serialization format: %s" % format)
    77             raise CommandError("Unknown serialization format: %s" % format)
    69 
    78 
       
    79         # Now collate the objects to be serialized.
    70         objects = []
    80         objects = []
    71         for app, model_list in app_list.items():
    81         for model in sort_dependencies(app_list.items()):
    72             if model_list is None:
    82             if not model._meta.proxy and router.allow_syncdb(using, model):
    73                 model_list = get_models(app)
    83                 objects.extend(model._default_manager.using(using).all())
    74 
       
    75             for model in model_list:
       
    76                 if not model._meta.proxy:
       
    77                     objects.extend(model._default_manager.all())
       
    78 
    84 
    79         try:
    85         try:
    80             return serializers.serialize(format, objects, indent=indent)
    86             return serializers.serialize(format, objects, indent=indent,
       
    87                         use_natural_keys=use_natural_keys)
    81         except Exception, e:
    88         except Exception, e:
    82             if show_traceback:
    89             if show_traceback:
    83                 raise
    90                 raise
    84             raise CommandError("Unable to serialize database: %s" % e)
    91             raise CommandError("Unable to serialize database: %s" % e)
       
    92 
       
    93 def sort_dependencies(app_list):
       
    94     """Sort a list of app,modellist pairs into a single list of models.
       
    95 
       
    96     The single list of models is sorted so that any model with a natural key
       
    97     is serialized before a normal model, and any model with a natural key
       
    98     dependency has it's dependencies serialized first.
       
    99     """
       
   100     from django.db.models import get_model, get_models
       
   101     # Process the list of models, and get the list of dependencies
       
   102     model_dependencies = []
       
   103     models = set()
       
   104     for app, model_list in app_list:
       
   105         if model_list is None:
       
   106             model_list = get_models(app)
       
   107 
       
   108         for model in model_list:
       
   109             models.add(model)
       
   110             # Add any explicitly defined dependencies
       
   111             if hasattr(model, 'natural_key'):
       
   112                 deps = getattr(model.natural_key, 'dependencies', [])
       
   113                 if deps:
       
   114                     deps = [get_model(*d.split('.')) for d in deps]
       
   115             else:
       
   116                 deps = []
       
   117 
       
   118             # Now add a dependency for any FK or M2M relation with
       
   119             # a model that defines a natural key
       
   120             for field in model._meta.fields:
       
   121                 if hasattr(field.rel, 'to'):
       
   122                     rel_model = field.rel.to
       
   123                     if hasattr(rel_model, 'natural_key'):
       
   124                         deps.append(rel_model)
       
   125             for field in model._meta.many_to_many:
       
   126                 rel_model = field.rel.to
       
   127                 if hasattr(rel_model, 'natural_key'):
       
   128                     deps.append(rel_model)
       
   129             model_dependencies.append((model, deps))
       
   130 
       
   131     model_dependencies.reverse()
       
   132     # Now sort the models to ensure that dependencies are met. This
       
   133     # is done by repeatedly iterating over the input list of models.
       
   134     # If all the dependencies of a given model are in the final list,
       
   135     # that model is promoted to the end of the final list. This process
       
   136     # continues until the input list is empty, or we do a full iteration
       
   137     # over the input models without promoting a model to the final list.
       
   138     # If we do a full iteration without a promotion, that means there are
       
   139     # circular dependencies in the list.
       
   140     model_list = []
       
   141     while model_dependencies:
       
   142         skipped = []
       
   143         changed = False
       
   144         while model_dependencies:
       
   145             model, deps = model_dependencies.pop()
       
   146 
       
   147             # If all of the models in the dependency list are either already
       
   148             # on the final model list, or not on the original serialization list,
       
   149             # then we've found another model with all it's dependencies satisfied.
       
   150             found = True
       
   151             for candidate in ((d not in models or d in model_list) for d in deps):
       
   152                 if not candidate:
       
   153                     found = False
       
   154             if found:
       
   155                 model_list.append(model)
       
   156                 changed = True
       
   157             else:
       
   158                 skipped.append((model, deps))
       
   159         if not changed:
       
   160             raise CommandError("Can't resolve dependencies for %s in serialized app list." %
       
   161                 ', '.join('%s.%s' % (model._meta.app_label, model._meta.object_name)
       
   162                 for model, deps in sorted(skipped, key=lambda obj: obj[0].__name__))
       
   163             )
       
   164         model_dependencies = skipped
       
   165 
       
   166     return model_list