1
0
mirror of https://github.com/django/django.git synced 2025-11-07 07:15:35 +00:00

Fixed #1142 -- Added multiple database support.

This monster of a patch is the result of Alex Gaynor's 2009 Google Summer of Code project.
Congratulations to Alex for a job well done.

Big thanks also go to:
 * Justin Bronn for keeping GIS in line with the changes,
 * Karen Tracey and Jani Tiainen for their help testing Oracle support
 * Brett Hoerner, Jon Loyens, and Craig Kimmerer for their feedback.
 * Malcolm Treddinick for his guidance during the GSoC submission process.
 * Simon Willison for driving the original design process
 * Cal Henderson for complaining about ponies he wanted.

... and everyone else too numerous to mention that helped to bring this feature into fruition.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11952 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee
2009-12-22 15:18:51 +00:00
parent 7ef212af14
commit ff60c5f9de
231 changed files with 7860 additions and 5668 deletions

View File

@@ -1,14 +1,25 @@
from optparse import make_option
from django.core.management.base import LabelCommand
from django.db import connections, transaction, models, DEFAULT_DB_ALIAS
class Command(LabelCommand):
help = "Creates the table needed to use the SQL cache backend."
args = "<tablename>"
label = 'tablename'
option_list = LabelCommand.option_list + (
make_option('--database', action='store', dest='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
def handle_label(self, tablename, **options):
from django.db import connection, transaction, models
alias = options.get('database', DEFAULT_DB_ALIAS)
connection = connections[alias]
fields = (
# "key" is a reserved word in MySQL, so use "cache_key" instead.
models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
@@ -19,7 +30,7 @@ class Command(LabelCommand):
index_output = []
qn = connection.ops.quote_name
for f in fields:
field_output = [qn(f.name), f.db_type()]
field_output = [qn(f.name), f.db_type(connection=connection)]
field_output.append("%sNULL" % (not f.null and "NOT " or ""))
if f.primary_key:
field_output.append("PRIMARY KEY")
@@ -39,4 +50,4 @@ class Command(LabelCommand):
curs.execute("\n".join(full_statement))
for statement in index_output:
curs.execute(statement)
transaction.commit_unless_managed()
transaction.commit_unless_managed(using=alias)

View File

@@ -1,12 +1,22 @@
from django.core.management.base import NoArgsCommand, CommandError
from optparse import make_option
class Command(NoArgsCommand):
help = "Runs the command-line client for the current DATABASE_ENGINE."
from django.core.management.base import BaseCommand, CommandError
from django.db import connections, DEFAULT_DB_ALIAS
class Command(BaseCommand):
help = ("Runs the command-line client for specified database, or the "
"default database if none is provided.")
option_list = BaseCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database onto which to '
'open a shell. Defaults to the "default" database.'),
)
requires_model_validation = False
def handle_noargs(self, **options):
from django.db import connection
def handle(self, **options):
connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
try:
connection.client.runshell()
except OSError:

View File

@@ -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).'),
make_option('-n', '--natural', action='store_true', dest='use_natural_keys', default=False,
@@ -24,14 +28,16 @@ class Command(BaseCommand):
format = options.get('format','json')
indent = options.get('indent',None)
using = options.get('database', DEFAULT_DB_ALIAS)
connection = connections[using]
exclude = options.get('exclude',[])
show_traceback = options.get('traceback', False)
use_natural_keys = options.get('use_natural_keys', False)
excluded_apps = [get_app(app_label) for app_label in exclude]
excluded_apps = set(get_app(app_label) for app_label in exclude)
if len(app_labels) == 0:
app_list = SortedDict([(app, None) for app in get_apps() if app not in excluded_apps])
app_list = SortedDict((app, None) for app in get_apps() if app not in excluded_apps)
else:
app_list = SortedDict()
for label in app_labels:
@@ -74,7 +80,7 @@ class Command(BaseCommand):
objects = []
for model in sort_dependencies(app_list.items()):
if not model._meta.proxy:
objects.extend(model._default_manager.all())
objects.extend(model._default_manager.using(using).all())
try:
return serializers.serialize(format, objects, indent=indent,
@@ -137,7 +143,7 @@ def sort_dependencies(app_list):
changed = False
while model_dependencies:
model, deps = model_dependencies.pop()
# 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.

View File

@@ -1,20 +1,28 @@
from optparse import make_option
from django.conf import settings
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
from django.core.management.sql import sql_flush, emit_post_sync_signal
from django.utils.importlib import import_module
from optparse import make_option
class Command(NoArgsCommand):
option_list = NoArgsCommand.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=DEFAULT_DB_ALIAS, help='Nominates a database to flush. '
'Defaults to the "default" database.'),
)
help = "Executes ``sqlflush`` on the current database."
def handle_noargs(self, **options):
from django.conf import settings
from django.db import connection, transaction, models
from django.core.management.sql import sql_flush, emit_post_sync_signal
db = options.get('database', DEFAULT_DB_ALIAS)
connection = connections[db]
verbosity = int(options.get('verbosity', 1))
interactive = options.get('interactive')
@@ -28,7 +36,7 @@ class Command(NoArgsCommand):
except ImportError:
pass
sql_list = sql_flush(self.style, only_django=True)
sql_list = sql_flush(self.style, connection, only_django=True)
if interactive:
confirm = raw_input("""You have requested a flush of the database.
@@ -36,7 +44,7 @@ This will IRREVERSIBLY DESTROY all data currently in the %r database,
and return each table to the state it was in after syncdb.
Are you sure you want to do this?
Type 'yes' to continue, or 'no' to cancel: """ % settings.DATABASE_NAME)
Type 'yes' to continue, or 'no' to cancel: """ % connection.settings_dict['NAME'])
else:
confirm = 'yes'
@@ -46,23 +54,24 @@ Are you sure you want to do this?
for sql in sql_list:
cursor.execute(sql)
except Exception, e:
transaction.rollback_unless_managed()
transaction.rollback_unless_managed(using=db)
raise CommandError("""Database %s couldn't be flushed. Possible reasons:
* The database isn't running or isn't configured correctly.
* At least one of the expected database tables doesn't exist.
* The SQL was invalid.
Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.
The full error: %s""" % (settings.DATABASE_NAME, e))
transaction.commit_unless_managed()
* The database isn't running or isn't configured correctly.
* At least one of the expected database tables doesn't exist.
* The SQL was invalid.
Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.
The full error: %s""" % (connection.settings_dict['NAME'], e))
transaction.commit_unless_managed(using=db)
# Emit the post sync signal. This allows individual
# applications to respond as if the database had been
# sync'd from scratch.
emit_post_sync_signal(models.get_models(), verbosity, interactive)
emit_post_sync_signal(models.get_models(), verbosity, interactive, db)
# Reinstall the initial_data fixture.
from django.core.management import call_command
call_command('loaddata', 'initial_data', **options)
kwargs = options.copy()
kwargs['database'] = db
call_command('loaddata', 'initial_data', **kwargs)
else:
print "Flush cancelled."

View File

@@ -1,20 +1,29 @@
import keyword
from optparse import make_option
from django.core.management.base import NoArgsCommand, CommandError
from django.db import connections, DEFAULT_DB_ALIAS
class Command(NoArgsCommand):
help = "Introspects the database tables in the given database and outputs a Django model module."
option_list = NoArgsCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to '
'introspect. Defaults to using the "default" database.'),
)
requires_model_validation = False
def handle_noargs(self, **options):
try:
for line in self.handle_inspection():
for line in self.handle_inspection(options):
print line
except NotImplementedError:
raise CommandError("Database inspection isn't supported for the currently selected database backend.")
def handle_inspection(self):
from django.db import connection
import keyword
def handle_inspection(self, options):
connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '')

View File

@@ -4,8 +4,13 @@ import gzip
import zipfile
from optparse import make_option
from django.conf import settings
from django.core import serializers
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
@@ -22,12 +27,19 @@ class Command(BaseCommand):
help = 'Installs the named fixture(s) in the database.'
args = "fixture [fixture ...]"
def handle(self, *fixture_labels, **options):
from django.db.models import get_apps
from django.core import serializers
from django.db import connection, transaction
from django.conf import settings
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. 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.get('database', DEFAULT_DB_ALIAS)
excluded_apps = options.get('exclude', [])
connection = connections[using]
self.style = no_style()
verbosity = int(options.get('verbosity', 1))
@@ -56,9 +68,9 @@ class Command(BaseCommand):
# Start transaction management. All fixtures are installed in a
# single transaction to ensure that all references are resolved.
if commit:
transaction.commit_unless_managed()
transaction.enter_transaction_management()
transaction.managed(True)
transaction.commit_unless_managed(using=using)
transaction.enter_transaction_management(using=using)
transaction.managed(True, using=using)
class SingleZipReader(zipfile.ZipFile):
def __init__(self, *args, **kwargs):
@@ -113,8 +125,8 @@ class Command(BaseCommand):
sys.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s is not a known serialization format." %
(fixture_name, format)))
transaction.rollback()
transaction.leave_transaction_management()
transaction.rollback(using=using)
transaction.leave_transaction_management(using=using)
return
if os.path.isabs(fixture_name):
@@ -127,73 +139,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])
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()
transaction.leave_transaction_management()
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:
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, using=using)
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()
object_count += objects_in_fixture
label_found = True
except (SystemExit, KeyboardInterrupt):
raise
except Exception:
import traceback
fixture.close()
transaction.rollback()
transaction.leave_transaction_management()
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
obj.save(using=using)
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()
transaction.leave_transaction_management()
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.
@@ -206,8 +220,8 @@ class Command(BaseCommand):
cursor.execute(line)
if commit:
transaction.commit()
transaction.leave_transaction_management()
transaction.commit(using=using)
transaction.leave_transaction_management(using=using)
if object_count == 0:
if verbosity > 1:

View File

@@ -1,11 +1,18 @@
from optparse import make_option
from django.conf import settings
from django.core.management.base import AppCommand, CommandError
from django.core.management.color import no_style
from optparse import make_option
from django.core.management.sql import sql_reset
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=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 ...]'
@@ -13,15 +20,13 @@ class Command(AppCommand):
output_transaction = True
def handle_app(self, app, **options):
from django.db import connection, transaction
from django.conf import settings
from django.core.management.sql import sql_reset
using = options.get('database', DEFAULT_DB_ALIAS)
connection = connections[using]
app_name = app.__name__.split('.')[-2]
self.style = no_style()
sql_list = sql_reset(app, self.style)
sql_list = sql_reset(app, self.style, connection)
if options.get('interactive'):
confirm = raw_input("""
@@ -30,7 +35,7 @@ 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, settings.DATABASE_NAME))
Type 'yes' to continue, or 'no' to cancel: """ % (app_name, connection.settings_dict['NAME']))
else:
confirm = 'yes'

View File

@@ -1,10 +1,19 @@
from optparse import make_option
from django.core.management.base import AppCommand
from django.core.management.sql import sql_create
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the CREATE TABLE SQL statements for the given app name(s)."
option_list = AppCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
'SQL for. Defaults to the "default" database.'),
)
output_transaction = True
def handle_app(self, app, **options):
from django.core.management.sql import sql_create
return u'\n'.join(sql_create(app, self.style)).encode('utf-8')
return u'\n'.join(sql_create(app, self.style, connections[options.get('database', DEFAULT_DB_ALIAS)])).encode('utf-8')

View File

@@ -1,10 +1,19 @@
from optparse import make_option
from django.core.management.base import AppCommand
from django.core.management.sql import sql_all
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s)."
option_list = AppCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
'SQL for. Defaults to the "default" database.'),
)
output_transaction = True
def handle_app(self, app, **options):
from django.core.management.sql import sql_all
return u'\n'.join(sql_all(app, self.style)).encode('utf-8')
return u'\n'.join(sql_all(app, self.style, connections[options.get('database', DEFAULT_DB_ALIAS)])).encode('utf-8')

View File

@@ -1,10 +1,19 @@
from optparse import make_option
from django.core.management.base import AppCommand
from django.core.management.sql import sql_delete
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the DROP TABLE SQL statements for the given app name(s)."
option_list = AppCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
'SQL for. Defaults to the "default" database.'),
)
output_transaction = True
def handle_app(self, app, **options):
from django.core.management.sql import sql_delete
return u'\n'.join(sql_delete(app, self.style)).encode('utf-8')
return u'\n'.join(sql_delete(app, self.style, connections[options.get('database', DEFAULT_DB_ALIAS)])).encode('utf-8')

View File

@@ -1,10 +1,19 @@
from optparse import make_option
from django.core.management.base import AppCommand
from django.core.management.sql import sql_custom
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the custom table modifying SQL statements for the given app name(s)."
option_list = AppCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
'SQL for. Defaults to the "default" database.'),
)
output_transaction = True
def handle_app(self, app, **options):
from django.core.management.sql import sql_custom
return u'\n'.join(sql_custom(app, self.style)).encode('utf-8')
return u'\n'.join(sql_custom(app, self.style, connections[options.get('database', DEFAULT_DB_ALIAS)])).encode('utf-8')

View File

@@ -1,10 +1,19 @@
from optparse import make_option
from django.core.management.base import NoArgsCommand
from django.core.management.sql import sql_flush
from django.db import connections, DEFAULT_DB_ALIAS
class Command(NoArgsCommand):
help = "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."
option_list = NoArgsCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
'SQL for. Defaults to the "default" database.'),
)
output_transaction = True
def handle_noargs(self, **options):
from django.core.management.sql import sql_flush
return u'\n'.join(sql_flush(self.style, only_django=True)).encode('utf-8')
return u'\n'.join(sql_flush(self.style, connections[options.get('database', DEFAULT_DB_ALIAS)], only_django=True)).encode('utf-8')

View File

@@ -1,10 +1,20 @@
from optparse import make_option
from django.core.management.base import AppCommand
from django.core.management.sql import sql_indexes
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the CREATE INDEX SQL statements for the given model module name(s)."
option_list = AppCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
'SQL for. Defaults to the "default" database.'),
)
output_transaction = True
def handle_app(self, app, **options):
from django.core.management.sql import sql_indexes
return u'\n'.join(sql_indexes(app, self.style)).encode('utf-8')
return u'\n'.join(sql_indexes(app, self.style, connections[options.get('database', DEFAULT_DB_ALIAS)])).encode('utf-8')

View File

@@ -1,10 +1,20 @@
from optparse import make_option
from django.core.management.base import AppCommand
from django.core.management.sql import sql_reset
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given app name(s)."
option_list = AppCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
'SQL for. Defaults to the "default" database.'),
)
output_transaction = True
def handle_app(self, app, **options):
from django.core.management.sql import sql_reset
return u'\n'.join(sql_reset(app, self.style)).encode('utf-8')
return u'\n'.join(sql_reset(app, self.style, connections[options.get('database', DEFAULT_DB_ALIAS)])).encode('utf-8')

View File

@@ -1,9 +1,20 @@
from optparse import make_option
from django.core.management.base import AppCommand
from django.db import connections, models, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = 'Prints the SQL statements for resetting sequences for the given app name(s).'
option_list = AppCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Nominates a database to print the '
'SQL for. Defaults to the "default" database.'),
)
output_transaction = True
def handle_app(self, app, **options):
from django.db import connection, models
connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
return u'\n'.join(connection.ops.sequence_reset_sql(self.style, models.get_models(app))).encode('utf-8')

View File

@@ -1,29 +1,32 @@
from django.core.management.base import NoArgsCommand
from django.core.management.color import no_style
from django.utils.importlib import import_module
from optparse import make_option
import sys
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
from django.conf import settings
from django.core.management.base import NoArgsCommand
from django.core.management.color import no_style
from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal
from django.db import connections, transaction, models, DEFAULT_DB_ALIAS
from django.utils.importlib import import_module
class Command(NoArgsCommand):
option_list = NoArgsCommand.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=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."
def handle_noargs(self, **options):
from django.db import connection, transaction, models
from django.conf import settings
from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal
verbosity = int(options.get('verbosity', 1))
interactive = options.get('interactive')
show_traceback = options.get('traceback', False)
exclude = options.get('exclude', [])
self.style = no_style()
@@ -46,6 +49,8 @@ class Command(NoArgsCommand):
if not msg.startswith('No module named') or 'management' not in msg:
raise
db = options.get('database', DEFAULT_DB_ALIAS)
connection = connections[db]
cursor = connection.cursor()
# Get a list of already installed *models* so that references work right.
@@ -54,8 +59,11 @@ class Command(NoArgsCommand):
created_models = set()
pending_references = {}
excluded_apps = set(models.get_app(app_label) for app_label in exclude)
included_apps = set(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 included_apps:
app_name = app.__name__.split('.')[-2]
model_list = models.get_models(app, include_auto_created=True)
for model in model_list:
@@ -82,22 +90,22 @@ class Command(NoArgsCommand):
tables.append(connection.introspection.table_name_converter(model._meta.db_table))
transaction.commit_unless_managed()
transaction.commit_unless_managed(using=db)
# Send the post_syncdb signal, so individual apps can do whatever they need
# to do at this point.
emit_post_sync_signal(created_models, verbosity, interactive)
emit_post_sync_signal(created_models, verbosity, interactive, db)
# The connection may have been closed by a syncdb handler.
cursor = connection.cursor()
# 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 included_apps:
app_name = app.__name__.split('.')[-2]
for model in models.get_models(app):
if model in created_models:
custom_sql = custom_sql_for_model(model, self.style)
custom_sql = custom_sql_for_model(model, self.style, connection)
if custom_sql:
if verbosity >= 1:
print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name)
@@ -110,14 +118,15 @@ class Command(NoArgsCommand):
if show_traceback:
import traceback
traceback.print_exc()
transaction.rollback_unless_managed()
transaction.rollback_unless_managed(using=db)
else:
transaction.commit_unless_managed()
transaction.commit_unless_managed(using=db)
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 included_apps:
app_name = app.__name__.split('.')[-2]
for model in models.get_models(app):
if model in created_models:
@@ -131,10 +140,9 @@ class Command(NoArgsCommand):
except Exception, e:
sys.stderr.write("Failed to install index for %s.%s model: %s\n" % \
(app_name, model._meta.object_name, e))
transaction.rollback_unless_managed()
transaction.rollback_unless_managed(using=db)
else:
transaction.commit_unless_managed()
transaction.commit_unless_managed(using=db)
# Install the 'initial_data' fixture, using format discovery
from django.core.management import call_command
call_command('loaddata', 'initial_data', verbosity=verbosity)
call_command('loaddata', 'initial_data', verbosity=verbosity, exclude=exclude, database=db)

View File

@@ -1,23 +1,29 @@
from django.core.management.base import CommandError
import os
import re
from django.conf import settings
from django.contrib.contenttypes import generic
from django.core.management.base import CommandError
from django.dispatch import dispatcher
from django.db import models
from django.db.models import get_models
from django.db.backends.util import truncate_name
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
def sql_create(app, style):
def sql_create(app, style, connection):
"Returns a list of the CREATE TABLE SQL statements for the given app."
from django.db import connection, models
from django.conf import settings
if settings.DATABASE_ENGINE == 'dummy':
if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy':
# This must be the "dummy" database backend, which means the user
# hasn't set DATABASE_ENGINE.
# hasn't set ENGINE for the databse.
raise CommandError("Django doesn't know which syntax to use for your SQL statements,\n" +
"because you haven't specified the DATABASE_ENGINE setting.\n" +
"Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.")
"because you haven't specified the ENGINE setting for the database.\n" +
"Edit your settings file and change DATBASES['default']['ENGINE'] to something like\n" +
"'django.db.backends.postgresql' or 'django.db.backends.mysql'")
# Get installed models, so we generate REFERENCES right.
# We trim models from the current app so that the sqlreset command does not
@@ -54,11 +60,8 @@ def sql_create(app, style):
return final_output
def sql_delete(app, style):
def sql_delete(app, style, connection):
"Returns a list of the DROP TABLE SQL statements for the given app."
from django.db import connection, models
from django.db.backends.util import truncate_name
from django.contrib.contenttypes import generic
# This should work even if a connection isn't available
try:
@@ -101,18 +104,17 @@ def sql_delete(app, style):
return output[::-1] # Reverse it, to deal with table dependencies.
def sql_reset(app, style):
def sql_reset(app, style, connection):
"Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
return sql_delete(app, style) + sql_all(app, style)
return sql_delete(app, style, connection) + sql_all(app, style, connection)
def sql_flush(style, only_django=False):
def sql_flush(style, connection, only_django=False):
"""
Returns a list of the SQL statements used to flush the database.
If only_django is True, then only table names that have associated Django
models and are in INSTALLED_APPS will be included.
"""
from django.db import connection
if only_django:
tables = connection.introspection.django_table_names(only_existing=True)
else:
@@ -120,35 +122,30 @@ def sql_flush(style, only_django=False):
statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list())
return statements
def sql_custom(app, style):
def sql_custom(app, style, connection):
"Returns a list of the custom table modifying SQL statements for the given app."
from django.db.models import get_models
output = []
app_models = get_models(app)
app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
for model in app_models:
output.extend(custom_sql_for_model(model, style))
output.extend(custom_sql_for_model(model, style, connection))
return output
def sql_indexes(app, style):
def sql_indexes(app, style, connection):
"Returns a list of the CREATE INDEX SQL statements for all models in the given app."
from django.db import connection, models
output = []
for model in models.get_models(app):
output.extend(connection.creation.sql_indexes_for_model(model, style))
return output
def sql_all(app, style):
def sql_all(app, style, connection):
"Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
return sql_create(app, style) + sql_custom(app, style) + sql_indexes(app, style)
def custom_sql_for_model(model, style):
from django.db import models
from django.conf import settings
return sql_create(app, style, connection) + sql_custom(app, style, connection) + sql_indexes(app, style, connection)
def custom_sql_for_model(model, style, connection):
opts = model._meta
app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
output = []
@@ -166,7 +163,8 @@ def custom_sql_for_model(model, style):
statements = re.compile(r";[ \t]*$", re.M)
# Find custom SQL, if it's available.
sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), settings.DATABASE_ENGINE)),
backend_name = connection.settings_dict['ENGINE'].split('.')[-1]
sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), backend_name)),
os.path.join(app_dir, "%s.sql" % opts.object_name.lower())]
for sql_file in sql_files:
if os.path.exists(sql_file):
@@ -181,9 +179,7 @@ def custom_sql_for_model(model, style):
return output
def emit_post_sync_signal(created_models, verbosity, interactive):
from django.db import models
from django.dispatch import dispatcher
def emit_post_sync_signal(created_models, verbosity, interactive, db):
# Emit the post_sync signal for every application.
for app in models.get_apps():
app_name = app.__name__.split('.')[-2]
@@ -191,4 +187,4 @@ def emit_post_sync_signal(created_models, verbosity, interactive):
print "Running post-sync handlers for application", app_name
models.signals.post_syncdb.send(sender=app, app=app,
created_models=created_models, verbosity=verbosity,
interactive=interactive)
interactive=interactive, db=db)

View File

@@ -36,14 +36,14 @@ except ImportError:
_serializers = {}
def register_serializer(format, serializer_module, serializers=None):
""""Register a new serializer.
""""Register a new serializer.
``serializer_module`` should be the fully qualified module name
for the serializer.
If ``serializers`` is provided, the registration will be added
to the provided dictionary.
If ``serializers`` is not provided, the registration will be made
directly into the global register of serializers. Adding serializers
directly is not a thread-safe operation.
@@ -53,7 +53,7 @@ def register_serializer(format, serializer_module, serializers=None):
_serializers[format] = module
else:
serializers[format] = module
def unregister_serializer(format):
"Unregister a given serializer. This is not a thread-safe operation."
del _serializers[format]
@@ -87,7 +87,7 @@ def serialize(format, queryset, **options):
s.serialize(queryset, **options)
return s.getvalue()
def deserialize(format, stream_or_string):
def deserialize(format, stream_or_string, **options):
"""
Deserialize a stream or a string. Returns an iterator that yields ``(obj,
m2m_relation_dict)``, where ``obj`` is a instantiated -- but *unsaved* --
@@ -95,7 +95,7 @@ def deserialize(format, stream_or_string):
list_of_related_objects}``.
"""
d = get_deserializer(format)
return d(stream_or_string)
return d(stream_or_string, **options)
def _load_serializers():
"""

View File

@@ -153,15 +153,16 @@ class DeserializedObject(object):
self.m2m_data = m2m_data
def __repr__(self):
return "<DeserializedObject: %s>" % smart_str(self.object)
return "<DeserializedObject: %s.%s(pk=%s)>" % (
self.object._meta.app_label, self.object._meta.object_name, self.object.pk)
def save(self, save_m2m=True):
def save(self, save_m2m=True, using=None):
# Call save on the Model baseclass directly. This bypasses any
# model-defined save. The save is also forced to be raw.
# This ensures that the data that is deserialized is literally
# what came from the file, not post-processed by pre_save/save
# methods.
models.Model.save_base(self.object, raw=True)
models.Model.save_base(self.object, using=using, raw=True)
if self.m2m_data and save_m2m:
for accessor_name, object_list in self.m2m_data.items():
setattr(self.object, accessor_name, object_list)

View File

@@ -39,7 +39,7 @@ def Deserializer(stream_or_string, **options):
stream = StringIO(stream_or_string)
else:
stream = stream_or_string
for obj in PythonDeserializer(simplejson.load(stream)):
for obj in PythonDeserializer(simplejson.load(stream), **options):
yield obj
class DjangoJSONEncoder(simplejson.JSONEncoder):

View File

@@ -6,7 +6,7 @@ other serializers.
from django.conf import settings
from django.core.serializers import base
from django.db import models
from django.db import models, DEFAULT_DB_ALIAS
from django.utils.encoding import smart_unicode, is_protected_type
class Serializer(base.Serializer):
@@ -77,6 +77,7 @@ def Deserializer(object_list, **options):
It's expected that you pass the Python objects themselves (instead of a
stream or a string) to the constructor
"""
db = options.pop('using', DEFAULT_DB_ALIAS)
models.get_apps()
for d in object_list:
# Look up the model and starting build a dict of data for it.
@@ -96,7 +97,7 @@ def Deserializer(object_list, **options):
if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
def m2m_convert(value):
if hasattr(value, '__iter__'):
return field.rel.to._default_manager.get_by_natural_key(*value).pk
return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk
else:
return smart_unicode(field.rel.to._meta.pk.to_python(value))
else:
@@ -108,7 +109,7 @@ def Deserializer(object_list, **options):
if field_value is not None:
if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
if hasattr(field_value, '__iter__'):
obj = field.rel.to._default_manager.get_by_natural_key(*field_value)
obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value)
value = getattr(obj, field.rel.field_name)
else:
value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)

View File

@@ -5,13 +5,9 @@ Requires PyYaml (http://pyyaml.org/), but that's checked for in __init__.
"""
from StringIO import StringIO
import decimal
import yaml
try:
import decimal
except ImportError:
from django.utils import _decimal as decimal # Python 2.3 fallback
from django.db import models
from django.core.serializers.python import Serializer as PythonSerializer
from django.core.serializers.python import Deserializer as PythonDeserializer
@@ -58,6 +54,6 @@ def Deserializer(stream_or_string, **options):
stream = StringIO(stream_or_string)
else:
stream = stream_or_string
for obj in PythonDeserializer(yaml.load(stream)):
for obj in PythonDeserializer(yaml.load(stream), **options):
yield obj

View File

@@ -4,7 +4,7 @@ XML serializer.
from django.conf import settings
from django.core.serializers import base
from django.db import models
from django.db import models, DEFAULT_DB_ALIAS
from django.utils.xmlutils import SimplerXMLGenerator
from django.utils.encoding import smart_unicode
from xml.dom import pulldom
@@ -149,6 +149,7 @@ class Deserializer(base.Deserializer):
def __init__(self, stream_or_string, **options):
super(Deserializer, self).__init__(stream_or_string, **options)
self.event_stream = pulldom.parse(self.stream)
self.db = options.pop('using', DEFAULT_DB_ALIAS)
def next(self):
for event, node in self.event_stream:
@@ -218,7 +219,7 @@ class Deserializer(base.Deserializer):
if keys:
# If there are 'natural' subelements, it must be a natural key
field_value = [getInnerText(k).strip() for k in keys]
obj = field.rel.to._default_manager.get_by_natural_key(*field_value)
obj = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value)
obj_pk = getattr(obj, field.rel.field_name)
else:
# Otherwise, treat like a normal PK
@@ -239,7 +240,7 @@ class Deserializer(base.Deserializer):
if keys:
# If there are 'natural' subelements, it must be a natural key
field_value = [getInnerText(k).strip() for k in keys]
obj_pk = field.rel.to._default_manager.get_by_natural_key(*field_value).pk
obj_pk = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value).pk
else:
# Otherwise, treat like a normal PK value.
obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk'))