From 0c167ae0ff3f23dcb00fddfb0d8b37b49060d3c4 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 23 Nov 2009 16:42:38 +0000 Subject: [PATCH] [soc2009/multidb] Modified the fixture loading, fixture dumping and synchronization process to be multi-db aware. Patch from Russell Keith-Magee. git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11763 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- TODO | 4 - .../management/commands/createcachetable.py | 6 +- django/core/management/commands/dbshell.py | 4 +- django/core/management/commands/dumpdata.py | 14 +- django/core/management/commands/flush.py | 6 +- django/core/management/commands/loaddata.py | 130 ++++++------ django/core/management/commands/reset.py | 66 +++---- django/core/management/commands/syncdb.py | 17 +- django/utils/itercompat.py | 15 ++ docs/ref/django-admin.txt | 186 +++++++++++------- .../fixtures/fixtures/fixture6.default.json | 10 + .../fixtures/fixture7.default.json.gz | Bin 0 -> 175 bytes .../fixtures/fixtures/fixture8.nosuchdb.json | 10 + tests/modeltests/fixtures/models.py | 37 ++++ 14 files changed, 317 insertions(+), 188 deletions(-) create mode 100644 tests/modeltests/fixtures/fixtures/fixture6.default.json create mode 100644 tests/modeltests/fixtures/fixtures/fixture7.default.json.gz create mode 100644 tests/modeltests/fixtures/fixtures/fixture8.nosuchdb.json diff --git a/TODO b/TODO index c2118db473..51aa6ad933 100644 --- a/TODO +++ b/TODO @@ -13,10 +13,6 @@ Required for v1.2 * Resolve the public facing UI issues around using multi-db * Should we take the opportunity to modify DB backends to use fully qualified paths? * Meta.using? Is is still required/desirable? - * syncdb - * Add --exclude/--include argument? (not sure this approach will work due to flush) - * Flush - which models are flushed? - * Fixture loading over multiple DBs * Testing infrastructure * Most tests don't need multidb. Some absolutely require it, but only to prove you can write to a different db. Second DB could be a SQLite temp file. Need to have diff --git a/django/core/management/commands/createcachetable.py b/django/core/management/commands/createcachetable.py index 2a87f27087..44e9a5baa6 100644 --- a/django/core/management/commands/createcachetable.py +++ b/django/core/management/commands/createcachetable.py @@ -10,9 +10,9 @@ class Command(LabelCommand): option_list = LabelCommand.option_list + ( make_option('--database', action='store', dest='database', - default=DEFAULT_DB_ALIAS, help='Nominates a specific database to ' - 'install the cache table to. Defaults to the "default" ' - 'database.'), + default=DEFAULT_DB_ALIAS, help='Nominates a database onto ' + 'which the cache table will be installed. ' + 'Defaults to the "default" database.'), ) requires_model_validation = False diff --git a/django/core/management/commands/dbshell.py b/django/core/management/commands/dbshell.py index 96443692e1..d42849459b 100644 --- a/django/core/management/commands/dbshell.py +++ b/django/core/management/commands/dbshell.py @@ -9,8 +9,8 @@ class Command(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--database', action='store', dest='database', - default=DEFAULT_DB_ALIAS, help='Nominates a database to open a ' - 'shell for. Defaults to the "default" database.'), + default=DEFAULT_DB_ALIAS, help='Nominates a database onto which to ' + 'open a shell. Defaults to the "default" database.'), ) requires_model_validation = False diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index 7e05b89160..d795d6f8b8 100644 --- a/django/core/management/commands/dumpdata.py +++ b/django/core/management/commands/dumpdata.py @@ -1,6 +1,7 @@ from django.core.exceptions import ImproperlyConfigured from django.core.management.base import BaseCommand, CommandError from django.core import serializers +from django.db import connections, DEFAULT_DB_ALIAS from django.utils.datastructures import SortedDict from optparse import make_option @@ -11,6 +12,9 @@ class Command(BaseCommand): help='Specifies the output serialization format for fixtures.'), make_option('--indent', default=None, dest='indent', type='int', help='Specifies the indent level to use when pretty-printing output'), + make_option('--database', action='store', dest='database', + default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load ' + 'fixtures into. Defaults to the "default" database.'), make_option('-e', '--exclude', dest='exclude',action='append', default=[], help='App to exclude (use multiple --exclude to exclude multiple apps).'), ) @@ -22,6 +26,8 @@ class Command(BaseCommand): format = options.get('format','json') indent = options.get('indent',None) + using = options['database'] + connection = connections[using] exclude = options.get('exclude',[]) show_traceback = options.get('traceback', False) @@ -67,14 +73,18 @@ class Command(BaseCommand): except KeyError: raise CommandError("Unknown serialization format: %s" % format) + # Get a list of synchronized tables + tables = connection.introspection.table_names() + objects = [] for app, model_list in app_list.items(): if model_list is None: model_list = get_models(app) for model in model_list: - if not model._meta.proxy: - objects.extend(model._default_manager.all()) + # Don't serialize proxy models, or models that haven't been synchronized + if not model._meta.proxy and model._meta.db_table in tables: + objects.extend(model._default_manager.using(using).all()) try: return serializers.serialize(format, objects, indent=indent) diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py index 6c6bbb5c11..12967690c0 100644 --- a/django/core/management/commands/flush.py +++ b/django/core/management/commands/flush.py @@ -1,7 +1,7 @@ from optparse import make_option from django.conf import settings -from django.db import connections, transaction, models +from django.db import connections, transaction, models, DEFAULT_DB_ALIAS from django.core.management import call_command from django.core.management.base import NoArgsCommand, CommandError from django.core.management.color import no_style @@ -15,8 +15,8 @@ class Command(NoArgsCommand): make_option('--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.'), make_option('--database', action='store', dest='database', - default=None, help='Nominates a database to flush. Defaults to ' - 'flushing all databases.'), + default=DEFAULT_DB_ALIAS, help='Nominates a database to flush. ' + 'Defaults to the "default" database.'), ) help = "Executes ``sqlflush`` on the current database." diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py index d2819d7fd8..6ca9f08c73 100644 --- a/django/core/management/commands/loaddata.py +++ b/django/core/management/commands/loaddata.py @@ -10,7 +10,7 @@ from django.core.management.base import BaseCommand from django.core.management.color import no_style from django.db import connections, transaction, DEFAULT_DB_ALIAS from django.db.models import get_apps - +from django.utils.itercompat import product try: set @@ -30,11 +30,15 @@ class Command(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--database', action='store', dest='database', default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load ' - 'fixtures into. By default uses the "default" database.'), + 'fixtures into. Defaults to the "default" database.'), + make_option('-e', '--exclude', dest='exclude',action='append', default=[], + help='App to exclude (use multiple --exclude to exclude multiple apps).'), ) def handle(self, *fixture_labels, **options): using = options['database'] + excluded_apps = options.get('exclude', []) + connection = connections[using] self.style = no_style() @@ -125,73 +129,75 @@ class Command(BaseCommand): print "Checking %s for fixtures..." % humanize(fixture_dir) label_found = False - for format in formats: - for compression_format in compression_formats: - if compression_format: - file_name = '.'.join([fixture_name, format, - compression_format]) - else: - file_name = '.'.join([fixture_name, format]) + for combo in product([using, None], formats, compression_formats): + database, format, compression_format = combo + file_name = '.'.join( + p for p in [ + fixture_name, database, format, compression_format + ] + if p + ) - if verbosity > 1: - print "Trying %s for %s fixture '%s'..." % \ - (humanize(fixture_dir), file_name, fixture_name) - full_path = os.path.join(fixture_dir, file_name) - open_method = compression_types[compression_format] - try: - fixture = open_method(full_path, 'r') - if label_found: - fixture.close() - print self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting." % - (fixture_name, humanize(fixture_dir))) - transaction.rollback(using=using) - transaction.leave_transaction_management(using=using) - return - else: - fixture_count += 1 - objects_in_fixture = 0 - if verbosity > 0: - print "Installing %s fixture '%s' from %s." % \ - (format, fixture_name, humanize(fixture_dir)) - try: - objects = serializers.deserialize(format, fixture) - for obj in objects: + if verbosity > 1: + print "Trying %s for %s fixture '%s'..." % \ + (humanize(fixture_dir), file_name, fixture_name) + full_path = os.path.join(fixture_dir, file_name) + open_method = compression_types[compression_format] + try: + fixture = open_method(full_path, 'r') + if label_found: + fixture.close() + print self.style.ERROR("Multiple fixtures named '%s' in %s. Aborting." % + (fixture_name, humanize(fixture_dir))) + transaction.rollback(using=using) + transaction.leave_transaction_management(using=using) + return + else: + fixture_count += 1 + objects_in_fixture = 0 + if verbosity > 0: + print "Installing %s fixture '%s' from %s." % \ + (format, fixture_name, humanize(fixture_dir)) + try: + objects = serializers.deserialize(format, fixture) + for obj in objects: + if obj.object._meta.app_label not in excluded_apps: objects_in_fixture += 1 models.add(obj.object.__class__) obj.save(using=using) - object_count += objects_in_fixture - label_found = True - except (SystemExit, KeyboardInterrupt): - raise - except Exception: - import traceback - fixture.close() - transaction.rollback(using=using) - transaction.leave_transaction_management(using=using) - if show_traceback: - traceback.print_exc() - else: - sys.stderr.write( - self.style.ERROR("Problem installing fixture '%s': %s\n" % - (full_path, ''.join(traceback.format_exception(sys.exc_type, - sys.exc_value, sys.exc_traceback))))) - return + object_count += objects_in_fixture + label_found = True + except (SystemExit, KeyboardInterrupt): + raise + except Exception: + import traceback fixture.close() - - # If the fixture we loaded contains 0 objects, assume that an - # error was encountered during fixture loading. - if objects_in_fixture == 0: + transaction.rollback(using=using) + transaction.leave_transaction_management(using=using) + if show_traceback: + traceback.print_exc() + else: sys.stderr.write( - self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)" % - (fixture_name))) - transaction.rollback(using=using) - transaction.leave_transaction_management(using=using) - return + self.style.ERROR("Problem installing fixture '%s': %s\n" % + (full_path, ''.join(traceback.format_exception(sys.exc_type, + sys.exc_value, sys.exc_traceback))))) + return + fixture.close() - except Exception, e: - if verbosity > 1: - print "No %s fixture '%s' in %s." % \ - (format, fixture_name, humanize(fixture_dir)) + # If the fixture we loaded contains 0 objects, assume that an + # error was encountered during fixture loading. + if objects_in_fixture == 0: + sys.stderr.write( + self.style.ERROR("No fixture data found for '%s'. (File format may be invalid.)" % + (fixture_name))) + transaction.rollback(using=using) + transaction.leave_transaction_management(using=using) + return + + except Exception, e: + if verbosity > 1: + print "No %s fixture '%s' in %s." % \ + (format, fixture_name, humanize(fixture_dir)) # If we found even one object in a fixture, we need to reset the # database sequences. diff --git a/django/core/management/commands/reset.py b/django/core/management/commands/reset.py index 4cb8720c8a..b46251b7b3 100644 --- a/django/core/management/commands/reset.py +++ b/django/core/management/commands/reset.py @@ -4,15 +4,15 @@ from django.conf import settings from django.core.management.base import AppCommand, CommandError from django.core.management.color import no_style from django.core.management.sql import sql_reset -from django.db import connections, transaction +from django.db import connections, transaction, DEFAULT_DB_ALIAS class Command(AppCommand): option_list = AppCommand.option_list + ( make_option('--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.'), make_option('--database', action='store', dest='database', - default='', help='Nominates a database to reset. Defaults to ' - 'reseting all databases.'), + default=DEFAULT_DB_ALIAS, help='Nominates a database to reset. ' + 'Defaults to the "default" database.'), ) help = "Executes ``sqlreset`` for the given app(s) in the current database." args = '[appname ...]' @@ -20,42 +20,38 @@ class Command(AppCommand): output_transaction = True def handle_app(self, app, **options): - if not options['database']: - dbs = connections.all() - else: - dbs = [options['database']] + using = options['database'] + connection = connections[using] app_name = app.__name__.split('.')[-2] self.style = no_style() - for connection in dbs: + sql_list = sql_reset(app, self.style, connection) - sql_list = sql_reset(app, self.style, connection) + if options.get('interactive'): + confirm = raw_input(""" +You have requested a database reset. +This will IRREVERSIBLY DESTROY any data for +the "%s" application in the database "%s". +Are you sure you want to do this? - if options.get('interactive'): - confirm = raw_input(""" - You have requested a database reset. - This will IRREVERSIBLY DESTROY any data for - the "%s" application in the database "%s". - Are you sure you want to do this? +Type 'yes' to continue, or 'no' to cancel: """ % (app_name, connection.settings_dict['DATABASE_NAME'])) + else: + confirm = 'yes' - Type 'yes' to continue, or 'no' to cancel: """ % (app_name, connection.settings_dict['DATABASE_NAME'])) - else: - confirm = 'yes' - - if confirm == 'yes': - try: - cursor = connection.cursor() - for sql in sql_list: - cursor.execute(sql) - except Exception, e: - transaction.rollback_unless_managed() - raise CommandError("""Error: %s couldn't be reset. Possible reasons: - * The database isn't running or isn't configured correctly. - * At least one of the database tables doesn't exist. - * The SQL was invalid. - Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run. - The full error: %s""" % (app_name, app_name, e)) - transaction.commit_unless_managed() - else: - print "Reset cancelled." + if confirm == 'yes': + try: + cursor = connection.cursor() + for sql in sql_list: + cursor.execute(sql) + except Exception, e: + transaction.rollback_unless_managed() + raise CommandError("""Error: %s couldn't be reset. Possible reasons: + * The database isn't running or isn't configured correctly. + * At least one of the database tables doesn't exist. + * The SQL was invalid. +Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run. +The full error: %s""" % (app_name, app_name, e)) + transaction.commit_unless_managed() + else: + print "Reset cancelled." diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py index b82753cbf6..85c1c1f4b7 100644 --- a/django/core/management/commands/syncdb.py +++ b/django/core/management/commands/syncdb.py @@ -14,8 +14,10 @@ class Command(NoArgsCommand): make_option('--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.'), make_option('--database', action='store', dest='database', - default=DEFAULT_DB_ALIAS, help='Nominates a database to sync. ' + default=DEFAULT_DB_ALIAS, help='Nominates a database to synchronize. ' 'Defaults to the "default" database.'), + make_option('-e', '--exclude', dest='exclude',action='append', default=[], + help='App to exclude (use multiple --exclude to exclude multiple apps).'), ) help = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." @@ -24,6 +26,7 @@ class Command(NoArgsCommand): verbosity = int(options.get('verbosity', 1)) interactive = options.get('interactive') show_traceback = options.get('traceback', False) + exclude = options.get('exclude', []) self.style = no_style() @@ -56,8 +59,11 @@ class Command(NoArgsCommand): created_models = set() pending_references = {} + excluded_apps = [models.get_app(app_label) for app_label in exclude] + app_list = [app for app in models.get_apps() if app not in excluded_apps] + # Create the tables for each model - for app in models.get_apps(): + for app in app_list: app_name = app.__name__.split('.')[-2] model_list = models.get_models(app, include_auto_created=True) for model in model_list: @@ -95,7 +101,7 @@ class Command(NoArgsCommand): # Install custom SQL for the app (but only if this # is a model we've just created) - for app in models.get_apps(): + for app in app_list: app_name = app.__name__.split('.')[-2] for model in models.get_models(app): if model in created_models: @@ -118,8 +124,9 @@ class Command(NoArgsCommand): else: if verbosity >= 2: print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name) + # Install SQL indicies for all newly created models - for app in models.get_apps(): + for app in app_list: app_name = app.__name__.split('.')[-2] for model in models.get_models(app): if model in created_models: @@ -138,4 +145,4 @@ class Command(NoArgsCommand): transaction.commit_unless_managed(using=db) from django.core.management import call_command - call_command('loaddata', 'initial_data', verbosity=verbosity, database=db) + call_command('loaddata', 'initial_data', verbosity=verbosity, exclude=exclude, database=db) diff --git a/django/utils/itercompat.py b/django/utils/itercompat.py index 96e6b038e8..639fc17e15 100644 --- a/django/utils/itercompat.py +++ b/django/utils/itercompat.py @@ -45,6 +45,19 @@ def groupby(iterable, keyfunc=None): l.append(item) yield lastkey, l +def product(*args, **kwds): + """ + Taken from http://docs.python.org/library/itertools.html#itertools.product + """ + # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy + # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 + pools = map(tuple, args) * kwds.get('repeat', 1) + result = [[]] + for pool in pools: + result = [x+[y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + # Not really in itertools, since it's a builtin in Python 2.4 and later, but it # does operate as an iterator. def reversed(data): @@ -57,6 +70,8 @@ else: tee = compat_tee if hasattr(itertools, 'groupby'): groupby = itertools.groupby +if hasattr(itertools, 'product'): + product = itertools.product def is_iterable(x): "A implementation independent way of checking for iterables" diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 9ff41faa4d..fa652b56ba 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -121,11 +121,10 @@ createcachetable Creates a cache table named ``tablename`` for use with the database cache backend. See :ref:`topics-cache` for more information. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to install the cachetable to. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database +onto which the cachetable will be installed. createsuperuser --------------- @@ -173,11 +172,10 @@ the program name (``psql``, ``mysql``, ``sqlite3``) will find the program in the right place. There's no way to specify the location of the program manually. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to open the shell for. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database +onto which to open a shell. diffsettings @@ -212,21 +210,6 @@ records to dump. If you're using a :ref:`custom manager ` as the default manager and it filters some of the available records, not all of the objects will be dumped. -.. django-admin-option:: --exclude - -.. versionadded:: 1.0 - -Exclude a specific application from the applications whose contents is -output. For example, to specifically exclude the `auth` application from -the output, you would call:: - - django-admin.py dumpdata --exclude=auth - -If you want to exclude multiple applications, use multiple ``--exclude`` -directives:: - - django-admin.py dumpdata --exclude=auth --exclude=contenttypes - .. django-admin-option:: --format By default, ``dumpdata`` will format its output in JSON, but you can use the @@ -239,6 +222,11 @@ By default, ``dumpdata`` will output all data on a single line. This isn't easy for humans to read, so you can use the ``--indent`` option to pretty-print the output with a number of indentation spaces. +.. versionadded:: 1.0 + +The :djadminopt:`--exclude` option may be provided to prevent specific +applications from being dumped. + .. versionadded:: 1.1 In addition to specifying application names, you can provide a list of @@ -247,6 +235,11 @@ name to ``dumpdata``, the dumped output will be restricted to that model, rather than the entire application. You can also mix application names and model names. +.. versionadded:: 1.2 + +The :djadminopt:`--database` option can be used to specify the database +onto which the data will be loaded. + flush ----- @@ -260,10 +253,10 @@ fixture will be re-installed. The :djadminopt:`--noinput` option may be provided to suppress all user prompts. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to flush. By default flushes all databases. +The :djadminopt:`--database` option may be used to specify the database +to flush. inspectdb @@ -309,11 +302,10 @@ needed. ``inspectdb`` works with PostgreSQL, MySQL and SQLite. Foreign-key detection only works in PostgreSQL and with certain types of MySQL tables. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to introspect. By default uses the ``'default'`` -alias. +The :djadminopt:`--database` option may be used to specify the +database to introspect. loaddata @@ -323,6 +315,11 @@ loaddata Searches for and loads the contents of the named fixture into the database. +.. versionadded:: 1.2 + +The :djadminopt:`--database` option can be used to specify the database +onto which the data will be loaded. + What's a "fixture"? ~~~~~~~~~~~~~~~~~~~ @@ -404,6 +401,37 @@ installation will be aborted, and any data installed in the call to references in your data files - MySQL doesn't provide a mechanism to defer checking of row constraints until a transaction is committed. +Database-specific fixtures +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are in a multi-database setup, you may have fixture data that +you want to load onto one database, but not onto another. In this +situation, you can add database identifier into . If your +:setting:`DATABASES` setting has a 'master' database defined, you can +define the fixture ``mydata.master.json`` or +``mydata.master.json.gz``. This fixture will only be loaded if you +have specified that you want to load data onto the ``master`` +database. + +Excluding applications from loading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 1.2 + +The :djadminopt:`--exclude` option may be provided to prevent specific +applications from being loaded. + +For example, if you wanted to exclude models from ``django.contrib.auth`` +from being loaded into your database, you would call:: + + django-admin.py loaddata mydata.json --exclude auth + +This will look for for a JSON fixture called ``mydata`` in all the +usual locations - including the ``fixtures`` directory of the +``django.contrib.auth`` application. However, any fixture object that +identifies itself as belonging to the ``auth`` application (e.g., +instance of ``auth.User``) would be ignored by loaddata. + makemessages ------------ @@ -465,10 +493,10 @@ Executes the equivalent of ``sqlreset`` for the given app name(s). The :djadminopt:`--noinput` option may be provided to suppress all user prompts. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to reset. By default resets all databases. +The :djadminopt:`--database` option can be used to specify the alias +of the database to reset. Executes the equivalent of ``sqlreset`` for the given app name(s). @@ -592,12 +620,10 @@ sql Prints the CREATE TABLE SQL statements for the given app name(s). ---database -~~~~~~~~~~ - -The alias for the database to print the SQL for. By default uses the -``'default'`` alias. +.. versionadded:: 1.2 +The :djadminopt:`--database` option can be used to specify the database for +which to print the SQL. sqlall ---------------------------- @@ -609,11 +635,10 @@ Prints the CREATE TABLE and initial-data SQL statements for the given app name(s Refer to the description of ``sqlcustom`` for an explanation of how to specify initial data. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to print the SQL for. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database for +which to print the SQL. sqlclear ------------------------------ @@ -622,11 +647,10 @@ sqlclear Prints the DROP TABLE SQL statements for the given app name(s). ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to print the SQL for. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database for +which to print the SQL. sqlcustom ------------------------------- @@ -649,11 +673,10 @@ table modifications, or insert any SQL functions into the database. Note that the order in which the SQL files are processed is undefined. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to print the SQL for. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database for +which to print the SQL. sqlflush -------- @@ -663,11 +686,10 @@ sqlflush Prints the SQL statements that would be executed for the :djadmin:`flush` command. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to print the SQL for. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database for +which to print the SQL. sqlindexes -------------------------------- @@ -676,11 +698,10 @@ sqlindexes Prints the CREATE INDEX SQL statements for the given app name(s). ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to print the SQL for. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database for +which to print the SQL. sqlreset ------------------------------ @@ -689,11 +710,10 @@ sqlreset Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given app name(s). ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to print the SQL for. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database for +which to print the SQL. sqlsequencereset -------------------------------------- @@ -708,11 +728,10 @@ number for automatically incremented fields. Use this command to generate SQL which will fix cases where a sequence is out of sync with its automatically incremented field data. ---database -~~~~~~~~~~ +.. versionadded:: 1.2 -The alias for the database to print the SQL for. By default uses the -``'default'`` alias. +The :djadminopt:`--database` option can be used to specify the database for +which to print the SQL. startapp ------------------ @@ -770,17 +789,16 @@ with an appropriate extension (e.g. ``json`` or ``xml``). See the documentation for ``loaddata`` for details on the specification of fixture data files. ---database -~~~~~~~~~~ - -The alias for the database install the tables for. By default uses the -``'default'`` alias. - --noinput ~~~~~~~~~ The :djadminopt:`--noinput` option may be provided to suppress all user prompts. +.. versionadded:: 1.2 + +The :djadminopt:`--database` option can be used to specify the database to +synchronize. + test ----------------------------- @@ -922,6 +940,30 @@ Common options The following options are not available on every commands, but they are common to a number of commands. +.. django-admin-option:: --database + +.. versionadded:: 1.2 + +Used to specify the database on which a command will operate. If not +specified, this option will default to an alias of ``default``. + +For example, to dump data from the database with the alias ``master``:: + + django-admin.py dumpdata --database=master + +.. django-admin-option:: --exclude + +Exclude a specific application from the applications whose contents is +output. For example, to specifically exclude the `auth` application from +the output of dumpdata, you would call:: + + django-admin.py dumpdata --exclude=auth + +If you want to exclude multiple applications, use multiple ``--exclude`` +directives:: + + django-admin.py dumpdata --exclude=auth --exclude=contenttypes + .. django-admin-option:: --locale Use the ``--locale`` or ``-l`` option to specify the locale to process. diff --git a/tests/modeltests/fixtures/fixtures/fixture6.default.json b/tests/modeltests/fixtures/fixtures/fixture6.default.json new file mode 100644 index 0000000000..9bb39e400f --- /dev/null +++ b/tests/modeltests/fixtures/fixtures/fixture6.default.json @@ -0,0 +1,10 @@ +[ + { + "pk": "6", + "model": "fixtures.article", + "fields": { + "headline": "Who needs more than one database?", + "pub_date": "2006-06-16 14:00:00" + } + } +] \ No newline at end of file diff --git a/tests/modeltests/fixtures/fixtures/fixture7.default.json.gz b/tests/modeltests/fixtures/fixtures/fixture7.default.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..80e4ba139f96c029cdb069b070f5b85998c5d395 GIT binary patch literal 175 zcmV;g08sxQiwFp}j|NKs17>M>bairNH!fslW?^+~bS`RhZ*BlhPC*L7Fc7@=70aGg zQjrS1_zVvsC3d$om^P(JpdkL;O$xTdFo)ThVIKtuK3NlRdSeZE#lvO|j@Tx*GfRjw z`;(r7X)W(VoncE}QrlMcd)8#l$f@L~d!7kM2YuTOuFu3*BZpi* dD^(qZWd-G>R!WHf^tV{``2!#v85__5002&SN+AFM literal 0 HcmV?d00001 diff --git a/tests/modeltests/fixtures/fixtures/fixture8.nosuchdb.json b/tests/modeltests/fixtures/fixtures/fixture8.nosuchdb.json new file mode 100644 index 0000000000..3da326bce1 --- /dev/null +++ b/tests/modeltests/fixtures/fixtures/fixture8.nosuchdb.json @@ -0,0 +1,10 @@ +[ + { + "pk": "8", + "model": "fixtures.article", + "fields": { + "headline": "There is no spoon.", + "pub_date": "2006-06-16 14:00:00" + } + } +] \ No newline at end of file diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py index 661a81e5ab..c685d1ef0d 100644 --- a/tests/modeltests/fixtures/models.py +++ b/tests/modeltests/fixtures/models.py @@ -159,6 +159,43 @@ Multiple fixtures named 'fixture2' in '...fixtures'. Aborting. >>> management.call_command('loaddata', 'fixture5', verbosity=0) # doctest: +ELLIPSIS Multiple fixtures named 'fixture5' in '...fixtures'. Aborting. +>>> management.call_command('flush', verbosity=0, interactive=False) + +# Load fixtures 6 and 7. These will load using the 'default' database identifier implicitly +>>> management.call_command('loaddata', 'fixture6', verbosity=0) +>>> management.call_command('loaddata', 'fixture7', verbosity=0) +>>> Article.objects.all() +[, , ] + +>>> management.call_command('flush', verbosity=0, interactive=False) + +# Load fixtures 6 and 7. These will load using the 'default' database identifier explicitly +>>> management.call_command('loaddata', 'fixture6', verbosity=0, using='default') +>>> management.call_command('loaddata', 'fixture7', verbosity=0, using='default') +>>> Article.objects.all() +[, , ] + +>>> management.call_command('flush', verbosity=0, interactive=False) + +# Try to load fixture 8. This won't load because the database identifier doesn't match +>>> management.call_command('loaddata', 'fixture8', verbosity=0) +>>> Article.objects.all() +[] + +>>> management.call_command('loaddata', 'fixture8', verbosity=0, using='default') +>>> Article.objects.all() +[] + +>>> management.call_command('flush', verbosity=0, interactive=False) + +# Try to load fixture 1, but this time, exclude the 'fixtures' app. +>>> management.call_command('loaddata', 'fixture1', verbosity=0, exclude='fixtures') +>>> Article.objects.all() +[] + +>>> Category.objects.all() +[] + """ from django.test import TestCase