Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

magic-removal: Refactored a bunch of django.core.management, includin…

…g fixes to syncdb and permissions.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2460 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 7e11865b844a872f211a61a15b0984df77bc508e 1 parent c6ab599
@jacobian jacobian authored
Showing with 151 additions and 176 deletions.
  1. +151 −176 django/core/management.py
View
327 django/core/management.py
@@ -65,10 +65,10 @@ def get_version():
if VERSION[3]:
v += ' (%s)' % VERSION[3]
return v
-
-def sql_for_table(model):
- from django.db import backend, get_creation_module, models
-
+
+def get_sql_create(app):
+ "Returns a list of the CREATE TABLE SQL statements for the given app."
+ from django.db import get_creation_module, models
data_types = get_creation_module().DATA_TYPES
if not data_types:
@@ -79,10 +79,41 @@ def sql_for_table(model):
"Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.\n")
sys.exit(1)
- opts = model._meta
+ final_output = []
+ models_output = set()
+ pending_references = {}
+
+ app_models = models.get_models(app)
+
+ for klass in app_models:
+ output, references = _get_sql_model_create(klass, models_output)
+ final_output.extend(output)
+ pending_references.update(references)
+ final_output.extend(_get_sql_for_pending_references(klass, pending_references))
+ # Keep track of the fact that we've created the table for this model.
+ models_output.add(klass)
+
+ # Create the many-to-many join tables.
+ for klass in app_models:
+ final_output.extend(_get_many_to_many_sql_for_model(klass))
+
+ return final_output
+get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given app name(s)."
+get_sql_create.args = APP_ARGS
- field_metadata = []
- references = []
+def _get_sql_model_create(klass, models_already_seen=set()):
+ """
+ Get the SQL required to create a single model.
+
+ Returns list_of_sql, pending_references_dict
+ """
+ from django.db import backend, get_creation_module, models
+ data_types = get_creation_module().DATA_TYPES
+
+ opts = klass._meta
+ final_output = []
+ table_output = []
+ pending_references = {}
for f in opts.fields:
if isinstance(f, models.ForeignKey):
rel_field = f.rel.get_related_field()
@@ -100,121 +131,78 @@ def sql_for_table(model):
if f.primary_key:
field_output.append('PRIMARY KEY')
if f.rel:
- references.append((f.rel.to, model, f))
- field_metadata.append(field_output)
+ if f.rel.to in models_already_seen:
+ field_output.append('REFERENCES %s (%s)' % \
+ (backend.quote_name(f.rel.to._meta.db_table),
+ backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)))
+ else:
+ # We haven't yet created the table to which this field
+ # is related, so save it for later.
+ pr = pending_references.setdefault(f.rel.to, []).append((klass, f))
+ table_output.append(' '.join(field_output))
if opts.order_with_respect_to:
- field_metadata.append((backend.quote_name('_order'), data_types['IntegerField'], 'NULL'))
-
- table_metadata = []
+ table_output.append('%s %s NULL' % (backend.quote_name('_order'), data_types['IntegerField']))
for field_constraints in opts.unique_together:
- table_metadata.append(['UNIQUE (%s)' % \
- ", ".join([backend.quote_name(opts.get_field(f).column) for f in field_constraints])])
- return field_metadata, table_metadata, references
+ table_output.append('UNIQUE (%s)' % \
+ ", ".join([backend.quote_name(opts.get_field(f).column) for f in field_constraints]))
-def get_sql_create(app):
- "Returns a list of the CREATE TABLE SQL statements for the given app."
- from django.db import backend, get_creation_module, models
+ full_statement = ['CREATE TABLE %s (' % backend.quote_name(opts.db_table)]
+ for i, line in enumerate(table_output): # Combine and add commas.
+ full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
+ full_statement.append(');')
+ final_output.append('\n'.join(full_statement))
+
+ return final_output, pending_references
+def _get_sql_for_pending_references(klass, pending_references):
+ """
+ Get any ALTER TABLE statements to add constraints after the fact.
+ """
+ from django.db import backend, get_creation_module
data_types = get_creation_module().DATA_TYPES
-
- if not data_types:
- # This must be the "dummy" database backend, which means the user
- # hasn't set DATABASE_ENGINE.
- sys.stderr.write("Error: 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'.\n")
- sys.exit(1)
-
+
final_output = []
- models_output = set()
- pending_references = {}
-
- app_models = models.get_models(app)
-
- for klass in app_models:
+ if backend.supports_constraints:
opts = klass._meta
- table_output = []
- for f in opts.fields:
- if isinstance(f, models.ForeignKey):
- rel_field = f.rel.get_related_field()
- data_type = get_rel_data_type(rel_field)
- else:
- rel_field = f
- data_type = f.get_internal_type()
- col_type = data_types[data_type]
- if col_type is not None:
- # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
- field_output = [backend.quote_name(f.column), col_type % rel_field.__dict__]
- field_output.append('%sNULL' % (not f.null and 'NOT ' or ''))
- if f.unique:
- field_output.append('UNIQUE')
- if f.primary_key:
- field_output.append('PRIMARY KEY')
- if f.rel:
- if f.rel.to in models_output:
- field_output.append('REFERENCES %s (%s)' % \
- (backend.quote_name(f.rel.to._meta.db_table),
- backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)))
- else:
- # We haven't yet created the table to which this field
- # is related, so save it for later.
- pr = pending_references.setdefault(f.rel.to, []).append((klass, f))
- table_output.append(' '.join(field_output))
- if opts.order_with_respect_to:
- table_output.append('%s %s NULL' % (backend.quote_name('_order'), data_types['IntegerField']))
- for field_constraints in opts.unique_together:
- table_output.append('UNIQUE (%s)' % \
- ", ".join([backend.quote_name(opts.get_field(f).column) for f in field_constraints]))
-
- full_statement = ['CREATE TABLE %s (' % backend.quote_name(opts.db_table)]
- for i, line in enumerate(table_output): # Combine and add commas.
- full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
- full_statement.append(');')
- final_output.append('\n'.join(full_statement))
-
- # Take care of any ALTER TABLE statements to add constraints
- # after the fact.
- if backend.supports_constraints:
- if klass in pending_references:
- for rel_class, f in pending_references[klass]:
- rel_opts = rel_class._meta
- r_table = rel_opts.db_table
- r_col = f.column
- table = opts.db_table
- col = opts.get_field(f.rel.field_name).column
- final_output.append('ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
- (backend.quote_name(r_table),
- backend.quote_name("%s_referencing_%s_%s" % (r_col, table, col)),
- backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
- del pending_references[klass]
+ if klass in pending_references:
+ for rel_class, f in pending_references[klass]:
+ rel_opts = rel_class._meta
+ r_table = rel_opts.db_table
+ r_col = f.column
+ table = opts.db_table
+ col = opts.get_field(f.rel.field_name).column
+ final_output.append('ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
+ (backend.quote_name(r_table),
+ backend.quote_name("%s_referencing_%s_%s" % (r_col, table, col)),
+ backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
+ del pending_references[klass]
+ return final_output
- # Keep track of the fact that we've created the table for this model.
- models_output.add(klass)
+def _get_many_to_many_sql_for_model(klass):
+ from django.db import backend, get_creation_module
+ data_types = get_creation_module().DATA_TYPES
- # Create the many-to-many join tables.
- for klass in app_models:
- opts = klass._meta
- for f in opts.many_to_many:
- table_output = ['CREATE TABLE %s (' % backend.quote_name(f.m2m_db_table())]
- table_output.append(' %s %s NOT NULL PRIMARY KEY,' % (backend.quote_name('id'), data_types['AutoField']))
- table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
- (backend.quote_name(f.m2m_column_name()),
- data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__,
- backend.quote_name(opts.db_table),
- backend.quote_name(opts.pk.column)))
- table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
- (backend.quote_name(f.m2m_reverse_name()),
- data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__,
- backend.quote_name(f.rel.to._meta.db_table),
- backend.quote_name(f.rel.to._meta.pk.column)))
- table_output.append(' UNIQUE (%s, %s)' % \
- (backend.quote_name(f.m2m_column_name()),
- backend.quote_name(f.m2m_reverse_name())))
- table_output.append(');')
- final_output.append('\n'.join(table_output))
+ opts = klass._meta
+ final_output = []
+ for f in opts.many_to_many:
+ table_output = ['CREATE TABLE %s (' % backend.quote_name(f.m2m_db_table())]
+ table_output.append(' %s %s NOT NULL PRIMARY KEY,' % (backend.quote_name('id'), data_types['AutoField']))
+ table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
+ (backend.quote_name(f.m2m_column_name()),
+ data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__,
+ backend.quote_name(opts.db_table),
+ backend.quote_name(opts.pk.column)))
+ table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
+ (backend.quote_name(f.m2m_reverse_name()),
+ data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__,
+ backend.quote_name(f.rel.to._meta.db_table),
+ backend.quote_name(f.rel.to._meta.pk.column)))
+ table_output.append(' UNIQUE (%s, %s)' % \
+ (backend.quote_name(f.m2m_column_name()),
+ backend.quote_name(f.m2m_reverse_name())))
+ table_output.append(');')
+ final_output.append('\n'.join(table_output))
return final_output
-get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given app name(s)."
-get_sql_create.args = APP_ARGS
def get_sql_delete(app):
"Returns a list of the DROP TABLE SQL statements for the given app."
@@ -400,19 +388,30 @@ def get_sql_all(app):
# TODO: syncdb() should include initial SQL data
# TODO: Put "example.com" site in initial SQL data for sites app
-# TODO: Check for model validation errors before executing SQL
def syncdb():
"Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
- from django.db import backend, connection, transaction, models, get_creation_module, get_introspection_module
+ from django.db import connection, transaction, models, get_creation_module, get_introspection_module
+
+ # Check that there are no validation errors before continuing
+ _check_for_validation_errors()
+
introspection_module = get_introspection_module()
data_types = get_creation_module().DATA_TYPES
cursor = connection.cursor()
+
# Get a list of all existing database tables,
# so we know what needs to be added.
table_list = introspection_module.get_table_list(cursor)
-
- pending_references = []
+
+ # Get a list of already installed *models* so that references work right.
+ all_models = []
+ for app in models.get_apps():
+ for model in models.get_models(app):
+ all_models.append(model)
+ seen_models = set([m for m in all_models if m._meta.db_table in table_list])
+ created_models = set()
+ pending_references = {}
for app in models.get_apps():
model_list = models.get_models(app)
@@ -420,55 +419,22 @@ def syncdb():
# Create the model's database table, if it doesn't already exist.
if model._meta.db_table in table_list:
continue
- field_metadata, table_metadata, references = sql_for_table(model)
- pending_references.extend(references)
- sql = "CREATE TABLE %s (\n %s\n)" % \
- (backend.quote_name(model._meta.db_table),
- ",\n ".join([' '.join(i) for i in field_metadata + table_metadata]))
+ sql, references = _get_sql_model_create(model, seen_models)
+ seen_models.add(model)
+ created_models.add(model)
+ pending_references.update(references)
+ sql.extend(_get_sql_for_pending_references(model, pending_references))
+ sql = "\n".join(sql)
print "Creating table %s" % model._meta.db_table
cursor.execute(sql)
for model in model_list:
- # Create the many-to-many join table, if it doesn't already exist.
- for f in model._meta.many_to_many:
- if f.m2m_db_table() in table_list:
- continue
- # This table doesn't exist yet and needs to be created.
- table_output = ['CREATE TABLE %s (' % backend.quote_name(f.m2m_db_table())]
- table_output.append(' %s %s NOT NULL PRIMARY KEY,' % (backend.quote_name('id'), data_types['AutoField']))
- table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
- (backend.quote_name(f.m2m_column_name()),
- data_types[get_rel_data_type(model._meta.pk)] % model._meta.pk.__dict__,
- backend.quote_name(model._meta.db_table),
- backend.quote_name(model._meta.pk.column)))
- table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
- (backend.quote_name(f.m2m_reverse_name()),
- data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__,
- backend.quote_name(f.rel.to._meta.db_table),
- backend.quote_name(f.rel.to._meta.pk.column)))
- table_output.append(' UNIQUE (%s, %s)' % \
- (backend.quote_name(f.m2m_column_name()),
- backend.quote_name(f.m2m_reverse_name())))
- table_output.append(')')
- sql = '\n'.join(table_output)
- print "Creating table %s" % f.m2m_db_table()
- cursor.execute(sql)
-
- # Create the pending references.
- # Take care of any ALTER TABLE statements to add constraints
- # after the fact.
- if backend.supports_constraints:
- for model, rel_class, field in pending_references:
- rel_opts = rel_class._meta
- r_table = rel_opts.db_table
- r_col = field.column
- table = model._meta.db_table
- col = model._meta.get_field(field.rel.field_name).column
- sql = 'ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
- (backend.quote_name(r_table),
- backend.quote_name("%s_referencing_%s_%s" % (r_col, table, col)),
- backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col))
- cursor.execute(sql)
+ if model in created_models:
+ sql = _get_many_to_many_sql_for_model(model)
+ if sql:
+ sql = '\n'.join(sql).strip()
+ print "Creating many-to-many tables for %s model" % model.__name__
+ cursor.execute(sql)
transaction.commit_unless_managed()
syncdb.args = ''
@@ -500,18 +466,13 @@ def get_admin_index(app):
def install(app):
"Executes the equivalent of 'get_sql_all' in the current database."
from django.db import connection, transaction
- from cStringIO import StringIO
+
app_name = app.__name__[app.__name__.rindex('.')+1:]
app_label = app_name.split('.')[-1]
# First, try validating the models.
- s = StringIO()
- num_errors = get_validation_errors(s, app)
- if num_errors:
- sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
- s.seek(0)
- sys.stderr.write(s.read())
- sys.exit(1)
+ _check_for_validation_errors(app)
+
sql_list = get_sql_all(app)
try:
@@ -578,20 +539,20 @@ def reset(app):
def installperms(app):
"Installs any permissions for the given app, if needed."
+ from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Permission
- from django.contrib.contenttypes.models import Package
from django.db.models import get_models
num_added = 0
app_models = get_models(app)
app_label = app_models[0]._meta.app_label
- package = Package.objects.get(pk=app_label)
for klass in app_models:
opts = klass._meta
+ ctype = ContentType.objects.get_for_model(klass)
for codename, name in _get_all_permissions(opts):
try:
- Permission.objects.get(name=name, codename=codename, package__label__exact=package.label)
+ Permission.objects.get(name=name, codename=codename, content_type__pk=ctype.id)
except Permission.DoesNotExist:
- p = Permission(name=name, package=package, codename=codename)
+ p = Permission(name=name, codename=codename, content_type=ctype)
p.save()
print "Added permission '%r'." % p
num_added += 1
@@ -996,6 +957,20 @@ def validate(outfile=sys.stdout):
outfile.write("Skipping validation because things aren't configured properly.")
validate.args = ''
+def _check_for_validation_errors(app=None):
+ """Check that an app has no validation errors, and exit with errors if it does."""
+ try:
+ from cStringIO import StringIO
+ except ImportError:
+ from StringIO import StringIO
+ s = StringIO()
+ num_errors = get_validation_errors(s, app)
+ if num_errors:
+ sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app)
+ s.seek(0)
+ sys.stderr.write(s.read())
+ sys.exit(1)
+
def runserver(addr, port):
"Starts a lightweight Web server for development."
from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
@@ -1046,7 +1021,7 @@ def createcachetable(tablename):
table_output = []
index_output = []
for f in fields:
- field_output = [backend.quote_name(f.column), data_types[f.get_internal_type()] % f.__dict__]
+ field_output = [backend.quote_name(f.name), data_types[f.get_internal_type()] % f.__dict__]
field_output.append("%sNULL" % (not f.null and "NOT " or ""))
if f.unique:
field_output.append("UNIQUE")
@@ -1055,8 +1030,8 @@ def createcachetable(tablename):
if f.db_index:
unique = f.unique and "UNIQUE " or ""
index_output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
- (unique, tablename, f.column, backend.quote_name(tablename),
- backend.quote_name(f.column)))
+ (unique, tablename, f.name, backend.quote_name(tablename),
+ backend.quote_name(f.name)))
table_output.append(" ".join(field_output))
full_statement = ["CREATE TABLE %s (" % backend.quote_name(tablename)]
for i, line in enumerate(table_output):
Please sign in to comment.
Something went wrong with that request. Please try again.