2009-02-28 13:35:22 +08:00
from django . core . exceptions import ImproperlyConfigured
2007-08-16 14:06:55 +08:00
from django . core . management . base import BaseCommand , CommandError
2007-12-17 14:53:15 +08:00
from django . core import serializers
2011-09-11 06:46:44 +08:00
from django . db import router , DEFAULT_DB_ALIAS
2009-03-04 12:53:15 +08:00
from django . utils . datastructures import SortedDict
2007-08-16 14:06:55 +08:00
2007-09-10 05:57:59 +08:00
from optparse import make_option
2007-08-16 14:06:55 +08:00
class Command ( BaseCommand ) :
2007-09-10 10:07:57 +08:00
option_list = BaseCommand . option_list + (
2007-09-10 05:57:59 +08:00
make_option ( ' --format ' , default = ' json ' , dest = ' format ' ,
2007-12-17 19:09:56 +08:00
help = ' Specifies the output serialization format for fixtures. ' ) ,
2007-09-10 05:57:59 +08:00
make_option ( ' --indent ' , default = None , dest = ' indent ' , type = ' int ' ,
help = ' Specifies the indent level to use when pretty-printing output ' ) ,
2009-12-22 23:18:51 +08:00
make_option ( ' --database ' , action = ' store ' , dest = ' database ' ,
2011-07-29 17:40:02 +08:00
default = DEFAULT_DB_ALIAS , help = ' Nominates a specific database to dump '
' fixtures from. Defaults to the " default " database. ' ) ,
2009-02-28 13:35:22 +08:00
make_option ( ' -e ' , ' --exclude ' , dest = ' exclude ' , action = ' append ' , default = [ ] ,
2010-08-07 00:48:07 +08:00
help = ' An appname or appname.ModelName to exclude (use multiple --exclude to exclude multiple apps/models). ' ) ,
2009-12-14 20:39:20 +08:00
make_option ( ' -n ' , ' --natural ' , action = ' store_true ' , dest = ' use_natural_keys ' , default = False ,
help = ' Use natural keys if they are available. ' ) ,
2010-08-30 19:58:26 +08:00
make_option ( ' -a ' , ' --all ' , action = ' store_true ' , dest = ' use_base_manager ' , default = False ,
help = " Use Django ' s base manager to dump all models stored in the database, including those that would otherwise be filtered or modified by a custom manager. " ) ,
2007-09-10 05:57:59 +08:00
)
2010-08-05 03:08:14 +08:00
help = ( " Output the contents of the database as a fixture of the given "
2010-08-30 19:58:26 +08:00
" format (using each model ' s default manager unless --all is "
" specified). " )
2010-03-31 19:30:42 +08:00
args = ' [appname appname.ModelName ...] '
2007-08-16 14:06:55 +08:00
def handle ( self , * app_labels , * * options ) :
2011-09-11 06:46:44 +08:00
from django . db . models import get_app , get_apps , get_model
2007-08-16 14:06:55 +08:00
2011-10-23 11:43:43 +08:00
format = options . get ( ' format ' )
indent = options . get ( ' indent ' )
using = options . get ( ' database ' )
excludes = options . get ( ' exclude ' )
show_traceback = options . get ( ' traceback ' )
use_natural_keys = options . get ( ' use_natural_keys ' )
use_base_manager = options . get ( ' use_base_manager ' )
2008-06-11 22:01:35 +08:00
2010-08-07 00:48:07 +08:00
excluded_apps = set ( )
excluded_models = set ( )
for exclude in excludes :
if ' . ' in exclude :
app_label , model_name = exclude . split ( ' . ' , 1 )
model_obj = get_model ( app_label , model_name )
if not model_obj :
raise CommandError ( ' Unknown model in excludes: %s ' % exclude )
excluded_models . add ( model_obj )
else :
try :
app_obj = get_app ( exclude )
excluded_apps . add ( app_obj )
except ImproperlyConfigured :
raise CommandError ( ' Unknown app in excludes: %s ' % exclude )
2007-08-16 14:06:55 +08:00
if len ( app_labels ) == 0 :
2009-12-22 23:18:51 +08:00
app_list = SortedDict ( ( app , None ) for app in get_apps ( ) if app not in excluded_apps )
2007-08-16 14:06:55 +08:00
else :
2009-03-04 12:53:15 +08:00
app_list = SortedDict ( )
2009-02-28 13:35:22 +08:00
for label in app_labels :
try :
app_label , model_label = label . split ( ' . ' )
try :
app = get_app ( app_label )
except ImproperlyConfigured :
raise CommandError ( " Unknown application: %s " % app_label )
2010-08-07 00:48:07 +08:00
if app in excluded_apps :
continue
2009-02-28 13:35:22 +08:00
model = get_model ( app_label , model_label )
if model is None :
raise CommandError ( " Unknown model: %s . %s " % ( app_label , model_label ) )
if app in app_list . keys ( ) :
if app_list [ app ] and model not in app_list [ app ] :
app_list [ app ] . append ( model )
else :
app_list [ app ] = [ model ]
except ValueError :
# This is just an app - no model qualifier
app_label = label
try :
app = get_app ( app_label )
except ImproperlyConfigured :
raise CommandError ( " Unknown application: %s " % app_label )
2010-08-07 00:48:07 +08:00
if app in excluded_apps :
continue
2009-02-28 13:35:22 +08:00
app_list [ app ] = None
2007-08-16 14:06:55 +08:00
# Check that the serialization format exists; this is a shortcut to
# avoid collating all the objects and _then_ failing.
2007-12-17 19:09:56 +08:00
if format not in serializers . get_public_serializer_formats ( ) :
2007-12-17 14:53:15 +08:00
raise CommandError ( " Unknown serialization format: %s " % format )
2007-08-16 14:06:55 +08:00
try :
serializers . get_serializer ( format )
except KeyError :
raise CommandError ( " Unknown serialization format: %s " % format )
2012-05-26 17:43:37 +08:00
def get_objects ( ) :
# Collate the objects to be serialized.
for model in sort_dependencies ( app_list . items ( ) ) :
if model in excluded_models :
continue
if not model . _meta . proxy and router . allow_syncdb ( using , model ) :
if use_base_manager :
objects = model . _base_manager
else :
objects = model . _default_manager
for obj in objects . using ( using ) . \
order_by ( model . _meta . pk . name ) . iterator ( ) :
yield obj
2009-02-28 13:35:22 +08:00
2007-08-16 14:06:55 +08:00
try :
2012-05-26 17:43:37 +08:00
self . stdout . ending = None
serializers . serialize ( format , get_objects ( ) , indent = indent ,
use_natural_keys = use_natural_keys , stream = self . stdout )
2012-04-29 00:09:37 +08:00
except Exception as e :
2007-12-17 19:09:32 +08:00
if show_traceback :
raise
2007-08-16 14:06:55 +08:00
raise CommandError ( " Unable to serialize database: %s " % e )
2009-12-14 20:39:20 +08:00
def sort_dependencies ( app_list ) :
""" Sort a list of app,modellist pairs into a single list of models.
The single list of models is sorted so that any model with a natural key
is serialized before a normal model , and any model with a natural key
dependency has it ' s dependencies serialized first.
"""
from django . db . models import get_model , get_models
# Process the list of models, and get the list of dependencies
model_dependencies = [ ]
models = set ( )
for app , model_list in app_list :
if model_list is None :
model_list = get_models ( app )
for model in model_list :
models . add ( model )
# Add any explicitly defined dependencies
if hasattr ( model , ' natural_key ' ) :
deps = getattr ( model . natural_key , ' dependencies ' , [ ] )
if deps :
deps = [ get_model ( * d . split ( ' . ' ) ) for d in deps ]
else :
deps = [ ]
# Now add a dependency for any FK or M2M relation with
# a model that defines a natural key
for field in model . _meta . fields :
if hasattr ( field . rel , ' to ' ) :
rel_model = field . rel . to
if hasattr ( rel_model , ' natural_key ' ) :
deps . append ( rel_model )
for field in model . _meta . many_to_many :
rel_model = field . rel . to
if hasattr ( rel_model , ' natural_key ' ) :
deps . append ( rel_model )
model_dependencies . append ( ( model , deps ) )
model_dependencies . reverse ( )
# Now sort the models to ensure that dependencies are met. This
# is done by repeatedly iterating over the input list of models.
# If all the dependencies of a given model are in the final list,
# that model is promoted to the end of the final list. This process
# continues until the input list is empty, or we do a full iteration
# over the input models without promoting a model to the final list.
# If we do a full iteration without a promotion, that means there are
# circular dependencies in the list.
model_list = [ ]
while model_dependencies :
skipped = [ ]
changed = False
while model_dependencies :
model , deps = model_dependencies . pop ( )
2009-12-22 23:18:51 +08:00
2009-12-17 07:47:30 +08:00
# If all of the models in the dependency list are either already
# on the final model list, or not on the original serialization list,
# then we've found another model with all it's dependencies satisfied.
found = True
for candidate in ( ( d not in models or d in model_list ) for d in deps ) :
if not candidate :
found = False
if found :
2009-12-14 20:39:20 +08:00
model_list . append ( model )
changed = True
else :
skipped . append ( ( model , deps ) )
if not changed :
raise CommandError ( " Can ' t resolve dependencies for %s in serialized app list. " %
' , ' . join ( ' %s . %s ' % ( model . _meta . app_label , model . _meta . object_name )
for model , deps in sorted ( skipped , key = lambda obj : obj [ 0 ] . __name__ ) )
)
model_dependencies = skipped
2010-08-05 03:08:14 +08:00
return model_list