summaryrefslogtreecommitdiff
path: root/django/core/management.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/core/management.py')
-rw-r--r--django/core/management.py1279
1 files changed, 727 insertions, 552 deletions
diff --git a/django/core/management.py b/django/core/management.py
index 15f570c57b..3838c2abee 100644
--- a/django/core/management.py
+++ b/django/core/management.py
@@ -2,8 +2,10 @@
# development-server initialization.
import django
-import os, re, sys, textwrap
+from django.core.exceptions import ImproperlyConfigured
+import os, re, shutil, sys, textwrap
from optparse import OptionParser
+from django.utils import termcolors
# For Python 2.3
if not hasattr(__builtins__, 'set'):
@@ -17,7 +19,7 @@ MODULE_TEMPLATE = ''' {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(cha
</tr>
{%% endif %%}'''
-APP_ARGS = '[modelmodule ...]'
+APP_ARGS = '[appname ...]'
# Use django.__path__[0] because we don't know which directory django into
# which has been installed.
@@ -25,38 +27,46 @@ PROJECT_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf', '%s_template')
INVALID_PROJECT_NAMES = ('django', 'test')
-def _get_packages_insert(app_label):
- from django.core.db import db
- return "INSERT INTO %s (%s, %s) VALUES ('%s', '%s');" % \
- (db.quote_name('packages'), db.quote_name('label'), db.quote_name('name'),
- app_label, app_label)
+# Set up the terminal color scheme.
+class dummy: pass
+style = dummy()
+style.ERROR = termcolors.make_style(fg='red', opts=('bold',))
+style.ERROR_OUTPUT = termcolors.make_style(fg='red', opts=('bold',))
+style.SQL_FIELD = termcolors.make_style(fg='green', opts=('bold',))
+style.SQL_COLTYPE = termcolors.make_style(fg='green')
+style.SQL_KEYWORD = termcolors.make_style(fg='yellow')
+style.SQL_TABLE = termcolors.make_style(opts=('bold',))
+del dummy
-def _get_permission_codename(action, opts):
- return '%s_%s' % (action, opts.object_name.lower())
+def disable_termcolors():
+ class dummy:
+ def __getattr__(self, attr):
+ return lambda x: x
+ global style
+ style = dummy()
-def _get_all_permissions(opts):
- "Returns (codename, name) for all permissions in the given opts."
- perms = []
- if opts.admin:
- for action in ('add', 'change', 'delete'):
- perms.append((_get_permission_codename(action, opts), 'Can %s %s' % (action, opts.verbose_name)))
- return perms + list(opts.permissions)
-
-def _get_permission_insert(name, codename, opts):
- from django.core.db import db
- return "INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s');" % \
- (db.quote_name('auth_permissions'), db.quote_name('name'), db.quote_name('package'),
- db.quote_name('codename'), name.replace("'", "''"), opts.app_label, codename)
-
-def _get_contenttype_insert(opts):
- from django.core.db import db
- return "INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s');" % \
- (db.quote_name('content_types'), db.quote_name('name'), db.quote_name('package'),
- db.quote_name('python_module_name'), opts.verbose_name, opts.app_label, opts.module_name)
+# Disable terminal coloring if somebody's piping the output.
+if (not sys.stdout.isatty()) or (sys.platform == 'win32'):
+ disable_termcolors()
def _is_valid_dir_name(s):
return bool(re.search(r'^\w+$', s))
+def _get_installed_models(table_list):
+ "Gets a set of all models that are installed, given a list of existing tables"
+ from django.db import models
+ all_models = []
+ for app in models.get_apps():
+ for model in models.get_models(app):
+ all_models.append(model)
+ return set([m for m in all_models if m._meta.db_table in table_list])
+
+def _get_table_list():
+ "Gets a list of all db tables that are physically installed."
+ from django.db import connection, get_introspection_module
+ cursor = connection.cursor()
+ return get_introspection_module().get_table_list(cursor)
+
# If the foreign key points to an AutoField, a PositiveIntegerField or a
# PositiveSmallIntegerField, the foreign key should be an IntegerField, not the
# referred field type. Otherwise, the foreign key should be the same type of
@@ -67,304 +77,429 @@ def get_version():
"Returns the version as a human-format string."
from django import VERSION
v = '.'.join([str(i) for i in VERSION[:-1]])
- if VERSION[3]:
- v += ' (%s)' % VERSION[3]
+ if VERSION[-1]:
+ v += ' (%s)' % VERSION[-1]
return v
-def get_sql_create(mod):
- "Returns a list of the CREATE TABLE SQL statements for the given module."
- from django.core import db, meta
+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:
+ # This must be the "dummy" database backend, which means the user
+ # hasn't set DATABASE_ENGINE.
+ sys.stderr.write(style.ERROR("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)
+
+ # Get installed models, so we generate REFERENCES right
+ installed_models = _get_installed_models(_get_table_list())
+
final_output = []
- for klass in mod._MODELS:
- opts = klass._meta
- table_output = []
- for f in opts.fields:
- if isinstance(f, meta.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 = db.DATA_TYPES[data_type]
- if col_type is not None:
- field_output = [db.db.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:
- field_output.append('REFERENCES %s (%s)' % \
- (db.db.quote_name(f.rel.to.db_table),
- db.db.quote_name(f.rel.to.get_field(f.rel.field_name).column)))
- table_output.append(' '.join(field_output))
- if opts.order_with_respect_to:
- table_output.append('%s %s NULL' % (db.db.quote_name('_order'), db.DATA_TYPES['IntegerField']))
- for field_constraints in opts.unique_together:
- table_output.append('UNIQUE (%s)' % \
- ", ".join([db.db.quote_name(opts.get_field(f).column) for f in field_constraints]))
+ models_output = set(installed_models)
+ pending_references = {}
- full_statement = ['CREATE TABLE %s (' % db.db.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))
+ 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))
+
+ # Handle references to tables that are from other apps
+ # but don't exist physically
+ not_installed_models = set(pending_references.keys())
+ if not_installed_models:
+ final_output.append('-- The following references should be added but depend on non-existant tables:')
+ for klass in not_installed_models:
+ final_output.extend(['-- ' + sql for sql in
+ _get_sql_for_pending_references(klass, pending_references)])
- for klass in mod._MODELS:
- opts = klass._meta
- for f in opts.many_to_many:
- table_output = ['CREATE TABLE %s (' % db.db.quote_name(f.get_m2m_db_table(opts))]
- table_output.append(' %s %s NOT NULL PRIMARY KEY,' % (db.db.quote_name('id'), db.DATA_TYPES['AutoField']))
- table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
- (db.db.quote_name(opts.object_name.lower() + '_id'),
- db.DATA_TYPES[get_rel_data_type(opts.pk)] % opts.pk.__dict__,
- db.db.quote_name(opts.db_table),
- db.db.quote_name(opts.pk.column)))
- table_output.append(' %s %s NOT NULL REFERENCES %s (%s),' % \
- (db.db.quote_name(f.rel.to.object_name.lower() + '_id'),
- db.DATA_TYPES[get_rel_data_type(f.rel.to.pk)] % f.rel.to.pk.__dict__,
- db.db.quote_name(f.rel.to.db_table),
- db.db.quote_name(f.rel.to.pk.column)))
- table_output.append(' UNIQUE (%s, %s)' % \
- (db.db.quote_name(opts.object_name.lower() + '_id'),
- db.db.quote_name(f.rel.to.object_name.lower() + '_id')))
- 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 model module name(s)."
+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(mod):
- "Returns a list of the DROP TABLE SQL statements for the given module."
- from django.core import db
+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()
+ 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 = [style.SQL_FIELD(backend.quote_name(f.column)),
+ style.SQL_COLTYPE(col_type % rel_field.__dict__)]
+ field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
+ if f.unique:
+ field_output.append(style.SQL_KEYWORD('UNIQUE'))
+ if f.primary_key:
+ field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
+ if f.rel:
+ if f.rel.to in models_already_seen:
+ field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
+ style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \
+ style.SQL_FIELD(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(style.SQL_FIELD(backend.quote_name('_order')) + ' ' + \
+ style.SQL_COLTYPE(data_types['IntegerField']) + ' ' + \
+ style.SQL_KEYWORD('NULL'))
+ for field_constraints in opts.unique_together:
+ table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
+ ", ".join([backend.quote_name(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
+
+ full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(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
+
+ final_output = []
+ if backend.supports_constraints:
+ opts = klass._meta
+ 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(style.SQL_KEYWORD('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
+
+def _get_many_to_many_sql_for_model(klass):
+ from django.db import backend, get_creation_module
+ data_types = get_creation_module().DATA_TYPES
+
+ opts = klass._meta
+ final_output = []
+ for f in opts.many_to_many:
+ table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
+ style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' (']
+ table_output.append(' %s %s %s,' % \
+ (style.SQL_FIELD(backend.quote_name('id')),
+ style.SQL_COLTYPE(data_types['AutoField']),
+ style.SQL_KEYWORD('NOT NULL PRIMARY KEY')))
+ table_output.append(' %s %s %s %s (%s),' % \
+ (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
+ style.SQL_COLTYPE(data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__),
+ style.SQL_KEYWORD('NOT NULL REFERENCES'),
+ style.SQL_TABLE(backend.quote_name(opts.db_table)),
+ style.SQL_FIELD(backend.quote_name(opts.pk.column))))
+ table_output.append(' %s %s %s %s (%s),' % \
+ (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())),
+ style.SQL_COLTYPE(data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__),
+ style.SQL_KEYWORD('NOT NULL REFERENCES'),
+ style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)),
+ style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column))))
+ table_output.append(' %s (%s, %s)' % \
+ (style.SQL_KEYWORD('UNIQUE'),
+ style.SQL_FIELD(backend.quote_name(f.m2m_column_name())),
+ style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name()))))
+ table_output.append(');')
+ final_output.append('\n'.join(table_output))
+ return final_output
+
+def get_sql_delete(app):
+ "Returns a list of the DROP TABLE SQL statements for the given app."
+ from django.db import backend, connection, models, get_introspection_module
+ introspection = get_introspection_module()
+
+ # This should work even if a connecton isn't available
try:
- cursor = db.db.cursor()
+ cursor = connection.cursor()
except:
cursor = None
- # Determine whether the admin log table exists. It only exists if the
- # person has installed the admin app.
- try:
- if cursor is not None:
- # Check whether the table exists.
- cursor.execute("SELECT 1 FROM %s LIMIT 1" % db.db.quote_name('django_admin_log'))
- except:
- # The table doesn't exist, so it doesn't need to be dropped.
- db.db.rollback()
- admin_log_exists = False
+ # Figure out which tables already exist
+ if cursor:
+ table_names = introspection.get_table_list(cursor)
else:
- admin_log_exists = True
+ table_names = []
output = []
# Output DROP TABLE statements for standard application tables.
- for klass in mod._MODELS:
- try:
- if cursor is not None:
- # Check whether the table exists.
- cursor.execute("SELECT 1 FROM %s LIMIT 1" % db.db.quote_name(klass._meta.db_table))
- except:
- # The table doesn't exist, so it doesn't need to be dropped.
- db.db.rollback()
- else:
- output.append("DROP TABLE %s;" % db.db.quote_name(klass._meta.db_table))
+ to_delete = set()
+
+ references_to_delete = {}
+ app_models = models.get_models(app)
+ for klass in app_models:
+ if cursor and klass._meta.db_table in table_names:
+ # The table exists, so it needs to be dropped
+ opts = klass._meta
+ for f in opts.fields:
+ if f.rel and f.rel.to not in to_delete:
+ references_to_delete.setdefault(f.rel.to, []).append( (klass, f) )
+
+ to_delete.add(klass)
+
+ for klass in app_models:
+ if cursor and klass._meta.db_table in table_names:
+ # Drop the table now
+ output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
+ style.SQL_TABLE(backend.quote_name(klass._meta.db_table))))
+ if backend.supports_constraints and references_to_delete.has_key(klass):
+ for rel_class, f in references_to_delete[klass]:
+ table = rel_class._meta.db_table
+ col = f.column
+ r_table = klass._meta.db_table
+ r_col = klass._meta.get_field(f.rel.field_name).column
+ output.append('%s %s %s %s;' % \
+ (style.SQL_KEYWORD('ALTER TABLE'),
+ style.SQL_TABLE(backend.quote_name(table)),
+ style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()),
+ style.SQL_FIELD(backend.quote_name("%s_referencing_%s_%s" % (col, r_table, r_col)))))
+ del references_to_delete[klass]
# Output DROP TABLE statements for many-to-many tables.
- for klass in mod._MODELS:
+ for klass in app_models:
opts = klass._meta
for f in opts.many_to_many:
- try:
- if cursor is not None:
- cursor.execute("SELECT 1 FROM %s LIMIT 1" % db.db.quote_name(f.get_m2m_db_table(opts)))
- except:
- db.db.rollback()
- else:
- output.append("DROP TABLE %s;" % db.db.quote_name(f.get_m2m_db_table(opts)))
+ if cursor and f.m2m_db_table() in table_names:
+ output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
+ style.SQL_TABLE(backend.quote_name(f.m2m_db_table()))))
- app_label = mod._MODELS[0]._meta.app_label
-
- # Delete from packages, auth_permissions, content_types.
- output.append("DELETE FROM %s WHERE %s = '%s';" % \
- (db.db.quote_name('packages'), db.db.quote_name('label'), app_label))
- output.append("DELETE FROM %s WHERE %s = '%s';" % \
- (db.db.quote_name('auth_permissions'), db.db.quote_name('package'), app_label))
- output.append("DELETE FROM %s WHERE %s = '%s';" % \
- (db.db.quote_name('content_types'), db.db.quote_name('package'), app_label))
-
- # Delete from the admin log.
- if cursor is not None:
- cursor.execute("SELECT %s FROM %s WHERE %s = %%s" % \
- (db.db.quote_name('id'), db.db.quote_name('content_types'),
- db.db.quote_name('package')), [app_label])
- if admin_log_exists:
- for row in cursor.fetchall():
- output.append("DELETE FROM %s WHERE %s = %s;" % \
- (db.db.quote_name('django_admin_log'), db.db.quote_name('content_type_id'), row[0]))
+ app_label = app_models[0]._meta.app_label
# Close database connection explicitly, in case this output is being piped
# directly into a database client, to avoid locking issues.
- if cursor is not None:
+ if cursor:
cursor.close()
- db.db.close()
+ connection.close()
return output[::-1] # Reverse it, to deal with table dependencies.
-get_sql_delete.help_doc = "Prints the DROP TABLE SQL statements for the given model module name(s)."
+get_sql_delete.help_doc = "Prints the DROP TABLE SQL statements for the given app name(s)."
get_sql_delete.args = APP_ARGS
-def get_sql_reset(mod):
+def get_sql_reset(app):
"Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
- return get_sql_delete(mod) + get_sql_all(mod)
-get_sql_reset.help_doc = "Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given model module name(s)."
+ return get_sql_delete(app) + get_sql_all(app)
+get_sql_reset.help_doc = "Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given app name(s)."
get_sql_reset.args = APP_ARGS
-def get_sql_initial_data(mod):
- "Returns a list of the initial INSERT SQL statements for the given module."
- from django.core import db
+def get_sql_initial_data_for_model(model):
+ from django.db import models
+ from django.conf import settings
+
+ opts = model._meta
+ app_dir = os.path.normpath(os.path.join(os.path.dirname(models.get_app(model._meta.app_label).__file__), 'sql'))
output = []
- app_label = mod._MODELS[0]._meta.app_label
- output.append(_get_packages_insert(app_label))
- app_dir = os.path.normpath(os.path.join(os.path.dirname(mod.__file__), '..', 'sql'))
- for klass in mod._MODELS:
- opts = klass._meta
- # Add custom SQL, if it's available.
- sql_files = [os.path.join(app_dir, opts.module_name + '.' + db.DATABASE_ENGINE + '.sql'),
- os.path.join(app_dir, opts.module_name + '.sql')]
- for sql_file in sql_files:
- if os.path.exists(sql_file):
- fp = open(sql_file)
- output.append(fp.read())
- fp.close()
+ # Find custom SQL, if it's available.
+ sql_files = [os.path.join(app_dir, "%s.%s.sql" % (opts.object_name.lower(), settings.DATABASE_ENGINE)),
+ os.path.join(app_dir, "%s.sql" % opts.object_name.lower())]
+ for sql_file in sql_files:
+ if os.path.exists(sql_file):
+ fp = open(sql_file)
+ output.append(fp.read())
+ fp.close()
- # Content types.
- output.append(_get_contenttype_insert(opts))
- # Permissions.
- for codename, name in _get_all_permissions(opts):
- output.append(_get_permission_insert(name, codename, opts))
return output
-get_sql_initial_data.help_doc = "Prints the initial INSERT SQL statements for the given model module name(s)."
+
+def get_sql_initial_data(app):
+ "Returns a list of the initial INSERT 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 klass in app_models:
+ output.extend(get_sql_initial_data_for_model(klass))
+
+ return output
+get_sql_initial_data.help_doc = "Prints the initial INSERT SQL statements for the given app name(s)."
get_sql_initial_data.args = APP_ARGS
-def get_sql_sequence_reset(mod):
- "Returns a list of the SQL statements to reset PostgreSQL sequences for the given module."
- from django.core import db, meta
+def get_sql_sequence_reset(app):
+ "Returns a list of the SQL statements to reset PostgreSQL sequences for the given app."
+ from django.db import backend, models
output = []
- for klass in mod._MODELS:
+ for klass in models.get_models(app):
for f in klass._meta.fields:
- if isinstance(f, meta.AutoField):
- output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % \
- (klass._meta.db_table, f.column, db.db.quote_name(f.column),
- db.db.quote_name(klass._meta.db_table)))
+ if isinstance(f, models.AutoField):
+ output.append("%s setval('%s', (%s max(%s) %s %s));" % \
+ (style.SQL_KEYWORD('SELECT'),
+ style.SQL_FIELD('%s_%s_seq' % (klass._meta.db_table, f.column)),
+ style.SQL_KEYWORD('SELECT'),
+ style.SQL_FIELD(backend.quote_name(f.column)),
+ style.SQL_KEYWORD('FROM'),
+ style.SQL_TABLE(backend.quote_name(klass._meta.db_table))))
+ break # Only one AutoField is allowed per model, so don't bother continuing.
for f in klass._meta.many_to_many:
- output.append("SELECT setval('%s_id_seq', (SELECT max(%s) FROM %s));" % \
- (f.get_m2m_db_table(klass._meta), db.db.quote_name('id'), f.get_m2m_db_table(klass._meta)))
+ output.append("%s setval('%s', (%s max(%s) %s %s));" % \
+ (style.SQL_KEYWORD('SELECT'),
+ style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()),
+ style.SQL_KEYWORD('SELECT'),
+ style.SQL_FIELD(backend.quote_name('id')),
+ style.SQL_KEYWORD('FROM'),
+ style.SQL_TABLE(f.m2m_db_table())))
return output
-get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given model module name(s)."
+get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given app name(s)."
get_sql_sequence_reset.args = APP_ARGS
-def get_sql_indexes(mod):
- "Returns a list of the CREATE INDEX SQL statements for the given module."
- from django.core.db import db
+def get_sql_indexes(app):
+ "Returns a list of the CREATE INDEX SQL statements for the given app."
+ from django.db import backend, models
output = []
- for klass in mod._MODELS:
+
+ for klass in models.get_models(app):
for f in klass._meta.fields:
if f.db_index:
- unique = f.unique and "UNIQUE " or ""
- output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
- (unique, klass._meta.db_table, f.column,
- db.quote_name(klass._meta.db_table), db.quote_name(f.column)))
+ unique = f.unique and 'UNIQUE ' or ''
+ output.append(
+ style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \
+ style.SQL_TABLE('%s_%s' % (klass._meta.db_table, f.column)) + ' ' + \
+ style.SQL_KEYWORD('ON') + ' ' + \
+ style.SQL_TABLE(backend.quote_name(klass._meta.db_table)) + ' ' + \
+ "(%s);" % style.SQL_FIELD(backend.quote_name(f.column))
+ )
return output
get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given model module name(s)."
get_sql_indexes.args = APP_ARGS
-def get_sql_all(mod):
- "Returns a list of CREATE TABLE SQL and initial-data insert for the given module."
- return get_sql_create(mod) + get_sql_initial_data(mod)
-get_sql_all.help_doc = "Prints the CREATE TABLE and initial-data SQL statements for the given model module name(s)."
+def get_sql_all(app):
+ "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
+ return get_sql_create(app) + get_sql_initial_data(app) + get_sql_indexes(app)
+get_sql_all.help_doc = "Prints the CREATE TABLE, initial-data and CREATE INDEX SQL statements for the given model module name(s)."
get_sql_all.args = APP_ARGS
-def has_no_records(cursor):
- "Returns True if the cursor, having executed a query, returned no records."
- # This is necessary due to an inconsistency in the DB-API spec.
- # cursor.rowcount can be -1 (undetermined), according to
- # http://www.python.org/peps/pep-0249.html
- if cursor.rowcount < 0:
- return cursor.fetchone() is None
- return cursor.rowcount < 1
-
-def database_check(mod):
- "Checks that everything is properly installed in the database for the given module."
- from django.core import db
- cursor = db.db.cursor()
- app_label = mod._MODELS[0]._meta.app_label
+def syncdb():
+ "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
+ from django.db import connection, transaction, models, get_creation_module
+ from django.db.models import signals
+ from django.conf import settings
+ from django.dispatch import dispatcher
- # Check that the package exists in the database.
- cursor.execute("SELECT 1 FROM %s WHERE %s = %%s" % \
- (db.db.quote_name('packages'), db.db.quote_name('label')), [app_label])
- if has_no_records(cursor):
-# sys.stderr.write("The '%s' package isn't installed.\n" % app_label)
- print _get_packages_insert(app_label)
+ disable_termcolors()
- # Check that the permissions and content types are in the database.
- perms_seen = {}
- contenttypes_seen = {}
- for klass in mod._MODELS:
- opts = klass._meta
- perms = _get_all_permissions(opts)
- perms_seen.update(dict(perms))
- contenttypes_seen[opts.module_name] = 1
- for codename, name in perms:
- cursor.execute("SELECT 1 FROM %s WHERE %s = %%s AND %s = %%s" % \
- (db.db.quote_name('auth_permissions'), db.db.quote_name('package'),
- db.db.quote_name('codename')), (app_label, codename))
- if has_no_records(cursor):
-# sys.stderr.write("The '%s.%s' permission doesn't exist.\n" % (app_label, codename))
- print _get_permission_insert(name, codename, opts)
- cursor.execute("SELECT 1 FROM %s WHERE %s = %%s AND %s = %%s" % \
- (db.db.quote_name('content_types'), db.db.quote_name('package'),
- db.db.quote_name('python_module_name')), (app_label, opts.module_name))
- if has_no_records(cursor):
-# sys.stderr.write("The '%s.%s' content type doesn't exist.\n" % (app_label, opts.module_name))
- print _get_contenttype_insert(opts)
+ # First, try validating the models.
+ _check_for_validation_errors()
- # Check that there aren't any *extra* permissions in the DB that the model
- # doesn't know about.
- cursor.execute("SELECT %s FROM %s WHERE %s = %%s" % \
- (db.db.quote_name('codename'), db.db.quote_name('auth_permissions'),
- db.db.quote_name('package')), (app_label,))
- for row in cursor.fetchall():
+ # Import the 'management' module within each installed app, to register
+ # dispatcher events.
+ for app_name in settings.INSTALLED_APPS:
try:
- perms_seen[row[0]]
- except KeyError:
-# sys.stderr.write("A permission called '%s.%s' was found in the database but not in the model.\n" % (app_label, row[0]))
- print "DELETE FROM %s WHERE %s='%s' AND %s = '%s';" % \
- (db.db.quote_name('auth_permissions'), db.db.quote_name('package'),
- app_label, db.db.quote_name('codename'), row[0])
+ __import__(app_name + '.management', '', '', [''])
+ except ImportError:
+ pass
- # Check that there aren't any *extra* content types in the DB that the
- # model doesn't know about.
- cursor.execute("SELECT %s FROM %s WHERE %s = %%s" % \
- (db.db.quote_name('python_module_name'), db.db.quote_name('content_types'),
- db.db.quote_name('package')), (app_label,))
- for row in cursor.fetchall():
- try:
- contenttypes_seen[row[0]]
- except KeyError:
-# sys.stderr.write("A content type called '%s.%s' was found in the database but not in the model.\n" % (app_label, row[0]))
- print "DELETE FROM %s WHERE %s='%s' AND %s = '%s';" % \
- (db.db.quote_name('content_types'), db.db.quote_name('package'),
- app_label, db.db.quote_name('python_module_name'), row[0])
-database_check.help_doc = "Checks that everything is installed in the database for the given model module name(s) and prints SQL statements if needed."
-database_check.args = APP_ARGS
+ 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 = _get_table_list()
+
+ # Get a list of already installed *models* so that references work right.
+ seen_models = _get_installed_models(table_list)
+ created_models = set()
+ pending_references = {}
+
+ for app in models.get_apps():
+ model_list = models.get_models(app)
+ for model in model_list:
+ # Create the model's database table, if it doesn't already exist.
+ if model._meta.db_table in table_list:
+ continue
+ 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))
+ print "Creating table %s" % model._meta.db_table
+ for statement in sql:
+ cursor.execute(statement)
+
+ for model in model_list:
+ if model in created_models:
+ sql = _get_many_to_many_sql_for_model(model)
+ if sql:
+ print "Creating many-to-many tables for %s model" % model.__name__
+ for statement in sql:
+ cursor.execute(statement)
+
+ transaction.commit_unless_managed()
+
+ # Send the post_syncdb signal, so individual apps can do whatever they need
+ # to do at this point.
+ for app in models.get_apps():
+ dispatcher.send(signal=signals.post_syncdb, sender=app,
+ app=app, created_models=created_models)
+
+ # Install initial data for the app (but only if this is a model we've
+ # just created)
+ for model in models.get_models(app):
+ if model in created_models:
+ initial_sql = get_sql_initial_data_for_model(model)
+ if initial_sql:
+ print "Installing initial data for %s model" % model._meta.object_name
+ try:
+ for sql in initial_sql:
+ cursor.execute(sql)
+ except Exception, e:
+ sys.stderr.write("Failed to install initial SQL data for %s model: %s" % \
+ (model._meta.object_name, e))
+ transaction.rollback_unless_managed()
+ else:
+ transaction.commit_unless_managed()
+
+syncdb.args = ''
-def get_admin_index(mod):
- "Returns admin-index template snippet (in list form) for the given module."
+def get_admin_index(app):
+ "Returns admin-index template snippet (in list form) for the given app."
from django.utils.text import capfirst
+ from django.db.models import get_models
output = []
- app_label = mod._MODELS[0]._meta.app_label
+ app_models = get_models(app)
+ app_label = app_models[0]._meta.app_label
output.append('{%% if perms.%s %%}' % app_label)
output.append('<div class="module"><h2>%s</h2><table>' % app_label.title())
- for klass in mod._MODELS:
+ for klass in app_models:
if klass._meta.admin:
output.append(MODULE_TEMPLATE % {
'app': app_label,
@@ -376,97 +511,114 @@ def get_admin_index(mod):
output.append('</table></div>')
output.append('{% endif %}')
return output
-get_admin_index.help_doc = "Prints the admin-index template snippet for the given model module name(s)."
+get_admin_index.help_doc = "Prints the admin-index template snippet for the given app name(s)."
get_admin_index.args = APP_ARGS
-def init():
- "Initializes the database with auth and core."
- try:
- from django.core import db, meta
- auth = meta.get_app('auth')
- core = meta.get_app('core')
- cursor = db.db.cursor()
- for sql in get_sql_create(core) + get_sql_create(auth) + get_sql_initial_data(core) + get_sql_initial_data(auth):
- cursor.execute(sql)
- cursor.execute("INSERT INTO %s (%s, %s) VALUES ('example.com', 'Example site')" % \
- (db.db.quote_name(core.Site._meta.db_table), db.db.quote_name('domain'),
- db.db.quote_name('name')))
- except Exception, e:
- sys.stderr.write("Error: The database couldn't be initialized.\n%s\n" % e)
- try:
- db.db.rollback()
- except UnboundLocalError:
- pass
- sys.exit(1)
- else:
- db.db.commit()
-init.args = ''
+def _module_to_dict(module, omittable=lambda k: k.startswith('_')):
+ "Converts a module namespace to a Python dictionary. Used by get_settings_diff."
+ return dict([(k, repr(v)) for k, v in module.__dict__.items() if not omittable(k)])
+
+def diffsettings():
+ """
+ Displays differences between the current settings.py and Django's
+ default settings. Settings that don't appear in the defaults are
+ followed by "###".
+ """
+ # Inspired by Postfix's "postconf -n".
+ from django.conf import settings, global_settings
+
+ user_settings = _module_to_dict(settings)
+ default_settings = _module_to_dict(global_settings)
-def install(mod):
+ output = []
+ keys = user_settings.keys()
+ keys.sort()
+ for key in keys:
+ if key not in default_settings:
+ output.append("%s = %s ###" % (key, user_settings[key]))
+ elif user_settings[key] != default_settings[key]:
+ output.append("%s = %s" % (key, user_settings[key]))
+ print '\n'.join(output)
+diffsettings.args = ""
+
+def install(app):
"Executes the equivalent of 'get_sql_all' in the current database."
- from django.core import db
- from cStringIO import StringIO
- mod_name = mod.__name__[mod.__name__.rindex('.')+1:]
+ from django.db import connection, transaction
+
+ app_name = app.__name__.split('.')[-2]
+
+ disable_termcolors()
# First, try validating the models.
- s = StringIO()
- num_errors = get_validation_errors(s)
- if num_errors:
- sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % mod_name)
- s.seek(0)
- sys.stderr.write(s.read())
- sys.exit(1)
- sql_list = get_sql_all(mod)
+ _check_for_validation_errors(app)
+
+ sql_list = get_sql_all(app)
try:
- cursor = db.db.cursor()
+ cursor = connection.cursor()
for sql in sql_list:
cursor.execute(sql)
except Exception, e:
- sys.stderr.write("""Error: %s couldn't be installed. Possible reasons:
+ sys.stderr.write(style.ERROR("""Error: %s couldn't be installed. Possible reasons:
* The database isn't running or isn't configured correctly.
* At least one of the database tables already exists.
* The SQL was invalid.
Hint: Look at the output of 'django-admin.py sqlall %s'. That's the SQL this command wasn't able to run.
-The full error: %s\n""" % \
- (mod_name, mod_name, e))
- db.db.rollback()
+The full error: """ % (app_name, app_name)) + style.ERROR_OUTPUT(str(e)) + '\n')
+ transaction.rollback_unless_managed()
sys.exit(1)
- db.db.commit()
-install.help_doc = "Executes ``sqlall`` for the given model module name(s) in the current database."
+ transaction.commit_unless_managed()
+install.help_doc = "Executes ``sqlall`` for the given app(s) in the current database."
install.args = APP_ARGS
-def installperms(mod):
- "Installs any permissions for the given model, if needed."
- from django.models.auth import permissions
- from django.models.core import packages
- num_added = 0
- package = packages.get_object(pk=mod._MODELS[0]._meta.app_label)
- for klass in mod._MODELS:
- opts = klass._meta
- for codename, name in _get_all_permissions(opts):
- try:
- permissions.get_object(name__exact=name, codename__exact=codename, package__label__exact=package.label)
- except permissions.PermissionDoesNotExist:
- p = permissions.Permission(name=name, package=package, codename=codename)
- p.save()
- print "Added permission '%r'." % p
- num_added += 1
- if not num_added:
- print "No permissions were added, because all necessary permissions were already installed."
-installperms.help_doc = "Installs any permissions for the given model module name(s), if needed."
-installperms.args = APP_ARGS
+def reset(app):
+ "Executes the equivalent of 'get_sql_reset' in the current database."
+ from django.db import connection, transaction
+ from cStringIO import StringIO
+ app_name = app.__name__.split('.')[-2]
+
+ disable_termcolors()
+
+ # First, try validating the models.
+ _check_for_validation_errors(app)
+ sql_list = get_sql_reset(app)
+
+ confirm = raw_input("""
+You have requested a database reset.
+This will IRREVERSIBLY DESTROY any data in your database.
+Are you sure you want to do this?
+
+Type 'yes' to continue, or 'no' to cancel: """)
+ if confirm == 'yes':
+ try:
+ cursor = connection.cursor()
+ for sql in sql_list:
+ cursor.execute(sql)
+ except Exception, e:
+ sys.stderr.write(style.ERROR("""Error: %s couldn't be installed. Possible reasons:
+ * The database isn't running or isn't configured correctly.
+ * At least one of the database tables already exists.
+ * 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: """ % (app_name, app_name)) + style.ERROR_OUTPUT(str(e)) + '\n')
+ transaction.rollback_unless_managed()
+ sys.exit(1)
+ transaction.commit_unless_managed()
+ else:
+ print "Reset cancelled."
+reset.help_doc = "Executes ``sqlreset`` for the given app(s) in the current database."
+reset.args = APP_ARGS
def _start_helper(app_or_project, name, directory, other_name=''):
other = {'project': 'app', 'app': 'project'}[app_or_project]
if not _is_valid_dir_name(name):
- sys.stderr.write("Error: %r is not a valid %s name. Please use only numbers, letters and underscores.\n" % (name, app_or_project))
+ sys.stderr.write(style.ERROR("Error: %r is not a valid %s name. Please use only numbers, letters and underscores.\n" % (name, app_or_project)))
sys.exit(1)
top_dir = os.path.join(directory, name)
try:
os.mkdir(top_dir)
except OSError, e:
- sys.stderr.write("Error: %s\n" % e)
+ sys.stderr.write(style.ERROR("Error: %s\n" % e))
sys.exit(1)
template_dir = PROJECT_TEMPLATE_DIR % app_or_project
for d, subdirs, files in os.walk(template_dir):
@@ -479,17 +631,20 @@ def _start_helper(app_or_project, name, directory, other_name=''):
for f in files:
if f.endswith('.pyc'):
continue
- fp_old = open(os.path.join(d, f), 'r')
- fp_new = open(os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name)), 'w')
+ path_old = os.path.join(d, f)
+ path_new = os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name))
+ fp_old = open(path_old, 'r')
+ fp_new = open(path_new, 'w')
fp_new.write(fp_old.read().replace('{{ %s_name }}' % app_or_project, name).replace('{{ %s_name }}' % other, other_name))
fp_old.close()
fp_new.close()
+ shutil.copymode(path_old, path_new)
def startproject(project_name, directory):
"Creates a Django project for the given project_name in the given directory."
from random import choice
if project_name in INVALID_PROJECT_NAMES:
- sys.stderr.write("Error: %r isn't a valid project name. Please try another.\n" % project_name)
+ sys.stderr.write(style.ERROR("Error: %r isn't a valid project name. Please try another.\n" % project_name))
sys.exit(1)
_start_helper('project', project_name, directory)
# Create a random SECRET_KEY hash, and put it in the main settings.
@@ -513,71 +668,19 @@ def startapp(app_name, directory):
startapp.help_doc = "Creates a Django app directory structure for the given app name in the current directory."
startapp.args = "[appname]"
-def createsuperuser(username=None, email=None, password=None):
- "Creates a superuser account."
- from django.core import validators
- from django.models.auth import users
- import getpass
- try:
- while 1:
- if not username:
- username = raw_input('Username (only letters, digits and underscores): ')
- if not username.isalnum():
- sys.stderr.write("Error: That username is invalid.\n")
- username = None
- try:
- users.get_object(username__exact=username)
- except users.UserDoesNotExist:
- break
- else:
- sys.stderr.write("Error: That username is already taken.\n")
- username = None
- while 1:
- if not email:
- email = raw_input('E-mail address: ')
- try:
- validators.isValidEmail(email, None)
- except validators.ValidationError:
- sys.stderr.write("Error: That e-mail address is invalid.\n")
- email = None
- else:
- break
- while 1:
- if not password:
- password = getpass.getpass()
- password2 = getpass.getpass('Password (again): ')
- if password != password2:
- sys.stderr.write("Error: Your passwords didn't match.\n")
- password = None
- continue
- if password.strip() == '':
- sys.stderr.write("Error: Blank passwords aren't allowed.\n")
- password = None
- continue
- break
- except KeyboardInterrupt:
- sys.stderr.write("\nOperation cancelled.\n")
- sys.exit(1)
- u = users.create_user(username, email, password)
- u.is_staff = True
- u.is_active = True
- u.is_superuser = True
- u.save()
- print "User created successfully."
-createsuperuser.args = '[username] [email] [password] (Either all or none)'
-
-def inspectdb(db_name):
+def inspectdb():
"Generator that introspects the tables in the given database name and returns a Django model, one line at a time."
- from django.core import db
+ from django.db import connection, get_introspection_module
from django.conf import settings
import keyword
+ introspection_module = get_introspection_module()
+
def table2model(table_name):
object_name = table_name.title().replace('_', '')
return object_name.endswith('s') and object_name[:-1] or object_name
- settings.DATABASE_NAME = db_name
- cursor = db.db.cursor()
+ cursor = connection.cursor()
yield "# This is an auto-generated Django model module."
yield "# You'll have to do the following manually to clean this up:"
yield "# * Rearrange models' order"
@@ -587,19 +690,19 @@ def inspectdb(db_name):
yield "# Also note: You'll have to insert the output of 'django-admin.py sqlinitialdata [appname]'"
yield "# into your database."
yield ''
- yield 'from django.core import meta'
+ yield 'from django.db import models'
yield ''
- for table_name in db.get_table_list(cursor):
- yield 'class %s(meta.Model):' % table2model(table_name)
+ for table_name in introspection_module.get_table_list(cursor):
+ yield 'class %s(models.Model):' % table2model(table_name)
try:
- relations = db.get_relations(cursor, table_name)
+ relations = introspection_module.get_relations(cursor, table_name)
except NotImplementedError:
relations = {}
try:
- indexes = db.get_indexes(cursor, table_name)
+ indexes = introspection_module.get_indexes(cursor, table_name)
except NotImplementedError:
indexes = {}
- for i, row in enumerate(db.get_table_description(cursor, table_name)):
+ for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
att_name = row[0]
comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
extra_params = {} # Holds Field parameters such as 'db_column'.
@@ -618,7 +721,7 @@ def inspectdb(db_name):
extra_params['db_column'] = att_name
else:
try:
- field_type = db.DATA_TYPES_REVERSE[row[1]]
+ field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
except KeyError:
field_type = 'TextField'
comment_notes.append('This field type is a guess.')
@@ -652,7 +755,14 @@ def inspectdb(db_name):
if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}:
continue
- field_desc = '%s = meta.%s' % (att_name, field_type)
+ # Add 'null' and 'blank', if the 'null_ok' flag was present in the
+ # table description.
+ if row[6]: # If it's NULL...
+ extra_params['blank'] = True
+ if not field_type in ('TextField', 'CharField'):
+ extra_params['null'] = True
+
+ field_desc = '%s = models.%s' % (att_name, field_type)
if extra_params:
if not field_desc.endswith('('):
field_desc += ', '
@@ -661,11 +771,11 @@ def inspectdb(db_name):
if comment_notes:
field_desc += ' # ' + ' '.join(comment_notes)
yield ' %s' % field_desc
- yield ' class META:'
+ yield ' class Meta:'
yield ' db_table = %r' % table_name
yield ''
inspectdb.help_doc = "Introspects the database tables in the given database and outputs a Django model module."
-inspectdb.args = "[dbname]"
+inspectdb.args = ""
class ModelErrorCollection:
def __init__(self, outfile=sys.stdout):
@@ -674,123 +784,182 @@ class ModelErrorCollection:
def add(self, opts, error):
self.errors.append((opts, error))
- self.outfile.write("%s.%s: %s\n" % (opts.app_label, opts.module_name, error))
+ self.outfile.write(style.ERROR("%s.%s: %s\n" % (opts.app_label, opts.module_name, error)))
+
+def get_validation_errors(outfile, app=None):
+ """
+ Validates all models that are part of the specified app. If no app name is provided,
+ validates all models of all installed apps. Writes errors, if any, to outfile.
+ Returns number of errors.
+ """
+ from django.db import models
+ from django.db.models.fields.related import RelatedObject
-def get_validation_errors(outfile):
- "Validates all installed models. Writes errors, if any, to outfile. Returns number of errors."
- import django.models
- from django.core import meta
e = ModelErrorCollection(outfile)
- module_list = meta.get_installed_model_modules()
- for module in module_list:
- for mod in module._MODELS:
- opts = mod._meta
+ for cls in models.get_models(app):
+ opts = cls._meta
- # Do field-specific validation.
- for f in opts.fields:
- if isinstance(f, meta.CharField) and f.maxlength in (None, 0):
- e.add(opts, '"%s" field: CharFields require a "maxlength" attribute.' % f.name)
- if isinstance(f, meta.FloatField):
- if f.decimal_places is None:
- e.add(opts, '"%s" field: FloatFields require a "decimal_places" attribute.' % f.name)
- if f.max_digits is None:
- e.add(opts, '"%s" field: FloatFields require a "max_digits" attribute.' % f.name)
- if isinstance(f, meta.FileField) and not f.upload_to:
- e.add(opts, '"%s" field: FileFields require an "upload_to" attribute.' % f.name)
- if isinstance(f, meta.ImageField):
- try:
- from PIL import Image
- except ImportError:
- e.add(opts, '"%s" field: To use ImageFields, you need to install the Python Imaging Library. Get it at http://www.pythonware.com/products/pil/ .' % f.name)
- if f.prepopulate_from is not None and type(f.prepopulate_from) not in (list, tuple):
- e.add(opts, '"%s" field: prepopulate_from should be a list or tuple.' % f.name)
- if f.choices:
- if not type(f.choices) in (tuple, list):
- e.add(opts, '"%s" field: "choices" should be either a tuple or list.' % f.name)
- else:
- for c in f.choices:
- if not type(c) in (tuple, list) or len(c) != 2:
- e.add(opts, '"%s" field: "choices" should be a sequence of two-tuples.' % f.name)
- if f.db_index not in (None, True, False):
- e.add(opts, '"%s" field: "db_index" should be either None, True or False.' % f.name)
+ # Do field-specific validation.
+ for f in opts.fields:
+ # Check for deprecated args
+ dep_args = getattr(f, 'deprecated_args', None)
+ if dep_args:
+ e.add(opts, "'%s' Initialized with deprecated args:%s" % (f.name, ",".join(dep_args)))
+ if isinstance(f, models.CharField) and f.maxlength in (None, 0):
+ e.add(opts, '"%s": CharFields require a "maxlength" attribute.' % f.name)
+ if isinstance(f, models.FloatField):
+ if f.decimal_places is None:
+ e.add(opts, '"%s": FloatFields require a "decimal_places" attribute.' % f.name)
+ if f.max_digits is None:
+ e.add(opts, '"%s": FloatFields require a "max_digits" attribute.' % f.name)
+ if isinstance(f, models.FileField) and not f.upload_to:
+ e.add(opts, '"%s": FileFields require an "upload_to" attribute.' % f.name)
+ if isinstance(f, models.ImageField):
+ try:
+ from PIL import Image
+ except ImportError:
+ e.add(opts, '"%s": To use ImageFields, you need to install the Python Imaging Library. Get it at http://www.pythonware.com/products/pil/ .' % f.name)
+ if f.prepopulate_from is not None and type(f.prepopulate_from) not in (list, tuple):
+ e.add(opts, '"%s": prepopulate_from should be a list or tuple.' % f.name)
+ if f.choices:
+ if not type(f.choices) in (tuple, list):
+ e.add(opts, '"%s": "choices" should be either a tuple or list.' % f.name)
+ else:
+ for c in f.choices:
+ if not type(c) in (tuple, list) or len(c) != 2:
+ e.add(opts, '"%s": "choices" should be a sequence of two-tuples.' % f.name)
+ if f.db_index not in (None, True, False):
+ e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)
+
+ # Check to see if the related field will clash with any
+ # existing fields, m2m fields, m2m related objects or related objects
+ if f.rel:
+ rel_opts = f.rel.to._meta
+ if f.rel.to not in models.get_models():
+ e.add(opts, "'%s' has relation with uninstalled model %s" % (f.name, rel_opts.object_name))
+
+ rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
+ for r in rel_opts.fields:
+ if r.name == rel_name:
+ e.add(opts, "'%s' accessor name '%s.%s' clashes with another field. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
+ for r in rel_opts.many_to_many:
+ if r.name == rel_name:
+ e.add(opts, "'%s' accessor name '%s.%s' clashes with a m2m field. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
+ for r in rel_opts.get_all_related_many_to_many_objects():
+ if r.get_accessor_name() == rel_name:
+ e.add(opts, "'%s' accessor name '%s.%s' clashes with a related m2m field. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
+ for r in rel_opts.get_all_related_objects():
+ if r.get_accessor_name() == rel_name and r.field is not f:
+ e.add(opts, "'%s' accessor name '%s.%s' clashes with another related field. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
+
+ for i, f in enumerate(opts.many_to_many):
+ # Check to see if the related m2m field will clash with any
+ # existing fields, m2m fields, m2m related objects or related objects
+ rel_opts = f.rel.to._meta
+ if f.rel.to not in models.get_models():
+ e.add(opts, "'%s' has m2m relation with uninstalled model %s" % (f.name, rel_opts.object_name))
- # Check for multiple ManyToManyFields to the same object, and
- # verify "singular" is set in that case.
- for i, f in enumerate(opts.many_to_many):
- for previous_f in opts.many_to_many[:i]:
- if f.rel.to == previous_f.rel.to and f.rel.singular == previous_f.rel.singular:
- e.add(opts, 'The "%s" field requires a "singular" parameter, because the %s model has more than one ManyToManyField to the same model (%s).' % (f.name, opts.object_name, previous_f.rel.to.object_name))
+ rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
+ for r in rel_opts.fields:
+ if r.name == rel_name:
+ e.add(opts, "'%s' m2m accessor name '%s.%s' clashes with another field. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
+ for r in rel_opts.many_to_many:
+ if r.name == rel_name:
+ e.add(opts, "'%s' m2m accessor name '%s.%s' clashes with a m2m field. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
+ for r in rel_opts.get_all_related_many_to_many_objects():
+ if r.get_accessor_name() == rel_name and r.field is not f:
+ e.add(opts, "'%s' m2m accessor name '%s.%s' clashes with a related m2m field. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
+ for r in rel_opts.get_all_related_objects():
+ if r.get_accessor_name() == rel_name:
+ e.add(opts, "'%s' m2m accessor name '%s.%s' clashes with another related field. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
- # Check admin attribute.
- if opts.admin is not None:
- if not isinstance(opts.admin, meta.Admin):
- e.add(opts, '"admin" attribute, if given, must be set to a meta.Admin() instance.')
+ # Check admin attribute.
+ if opts.admin is not None:
+ if not isinstance(opts.admin, models.AdminOptions):
+ e.add(opts, '"admin" attribute, if given, must be set to a models.AdminOptions() instance.')
+ else:
+ # list_display
+ if not isinstance(opts.admin.list_display, (list, tuple)):
+ e.add(opts, '"admin.list_display", if given, must be set to a list or tuple.')
else:
- # list_display
- if not isinstance(opts.admin.list_display, (list, tuple)):
- e.add(opts, '"admin.list_display", if given, must be set to a list or tuple.')
- else:
- for fn in opts.admin.list_display:
- try:
- f = opts.get_field(fn)
- except meta.FieldDoesNotExist:
- klass = opts.get_model_module().Klass
- if not hasattr(klass, fn) or not callable(getattr(klass, fn)):
- e.add(opts, '"admin.list_display" refers to %r, which isn\'t a field or method.' % fn)
- else:
- if isinstance(f, meta.ManyToManyField):
- e.add(opts, '"admin.list_display" doesn\'t support ManyToManyFields (%r).' % fn)
- # list_filter
- if not isinstance(opts.admin.list_filter, (list, tuple)):
- e.add(opts, '"admin.list_filter", if given, must be set to a list or tuple.')
- else:
- for fn in opts.admin.list_filter:
- try:
- f = opts.get_field(fn)
- except meta.FieldDoesNotExist:
- e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
+ for fn in opts.admin.list_display:
+ try:
+ f = opts.get_field(fn)
+ except models.FieldDoesNotExist:
+ if not hasattr(cls, fn):
+ e.add(opts, '"admin.list_display" refers to %r, which isn\'t an attribute, method or property.' % fn)
+ else:
+ if isinstance(f, models.ManyToManyField):
+ e.add(opts, '"admin.list_display" doesn\'t support ManyToManyFields (%r).' % fn)
+ # list_filter
+ if not isinstance(opts.admin.list_filter, (list, tuple)):
+ e.add(opts, '"admin.list_filter", if given, must be set to a list or tuple.')
+ else:
+ for fn in opts.admin.list_filter:
+ try:
+ f = opts.get_field(fn)
+ except models.FieldDoesNotExist:
+ e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
- # Check ordering attribute.
- if opts.ordering:
- for field_name in opts.ordering:
- if field_name == '?': continue
- if field_name.startswith('-'):
- field_name = field_name[1:]
- if opts.order_with_respect_to and field_name == '_order':
- continue
- try:
- opts.get_field(field_name, many_to_many=False)
- except meta.FieldDoesNotExist:
- e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
+ # Check ordering attribute.
+ if opts.ordering:
+ for field_name in opts.ordering:
+ if field_name == '?': continue
+ if field_name.startswith('-'):
+ field_name = field_name[1:]
+ if opts.order_with_respect_to and field_name == '_order':
+ continue
+ try:
+ opts.get_field(field_name, many_to_many=False)
+ except models.FieldDoesNotExist:
+ e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
+
+ # Check core=True, if needed.
+ for related in opts.get_followed_related_objects():
+ try:
+ for f in related.opts.fields:
+ if f.core:
+ raise StopIteration
+ e.add(related.opts, "At least one field in %s should have core=True, because it's being edited inline by %s.%s." % (related.opts.object_name, opts.module_name, opts.object_name))
+ except StopIteration:
+ pass
- # Check core=True, if needed.
- for related in opts.get_followed_related_objects():
+ # Check unique_together.
+ for ut in opts.unique_together:
+ for field_name in ut:
try:
- for f in related.opts.fields:
- if f.core:
- raise StopIteration
- e.add(related.opts, "At least one field in %s should have core=True, because it's being edited inline by %s.%s." % (related.opts.object_name, opts.module_name, opts.object_name))
- except StopIteration:
- pass
+ f = opts.get_field(field_name, many_to_many=True)
+ except models.FieldDoesNotExist:
+ e.add(opts, '"unique_together" refers to %s, a field that doesn\'t exist. Check your syntax.' % field_name)
+ else:
+ if isinstance(f.rel, models.ManyToManyRel):
+ e.add(opts, '"unique_together" refers to %s. ManyToManyFields are not supported in unique_together.' % f.name)
- # Check unique_together.
- for ut in opts.unique_together:
- for field_name in ut:
- try:
- f = opts.get_field(field_name, many_to_many=True)
- except meta.FieldDoesNotExist:
- e.add(opts, '"unique_together" refers to %s, a field that doesn\'t exist. Check your syntax.' % field_name)
- else:
- if isinstance(f.rel, meta.ManyToManyRel):
- e.add(opts, '"unique_together" refers to %s. ManyToManyFields are not supported in unique_together.' % f.name)
return len(e.errors)
def validate(outfile=sys.stdout):
"Validates all installed models."
- num_errors = get_validation_errors(outfile)
- outfile.write('%s error%s found.\n' % (num_errors, num_errors != 1 and 's' or ''))
+ try:
+ num_errors = get_validation_errors(outfile)
+ outfile.write('%s error%s found.\n' % (num_errors, num_errors != 1 and 's' or ''))
+ except ImproperlyConfigured:
+ 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(style.ERROR("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
@@ -798,13 +967,13 @@ def runserver(addr, port):
if not addr:
addr = '127.0.0.1'
if not port.isdigit():
- sys.stderr.write("Error: %r is not a valid port number.\n" % port)
+ sys.stderr.write(style.ERROR("Error: %r is not a valid port number.\n" % port))
sys.exit(1)
def inner_run():
- from django.conf.settings import SETTINGS_MODULE
+ from django.conf import settings
print "Validating models..."
validate()
- print "\nDjango version %s, using settings %r" % (get_version(), SETTINGS_MODULE)
+ print "\nDjango version %s, using settings %r" % (get_version(), settings.SETTINGS_MODULE)
print "Development server is running at http://%s:%s/" % (addr, port)
print "Quit the server with CONTROL-C (Unix) or CTRL-BREAK (Windows)."
try:
@@ -820,7 +989,7 @@ def runserver(addr, port):
error_text = ERRORS[e.args[0].args[0]]
except (AttributeError, KeyError):
error_text = str(e)
- sys.stderr.write("Error: %s\n" % error_text)
+ sys.stderr.write(style.ERROR("Error: %s" % error_text) + '\n')
sys.exit(1)
except KeyboardInterrupt:
sys.exit(0)
@@ -830,17 +999,18 @@ runserver.args = '[optional port number, or ipaddr:port]'
def createcachetable(tablename):
"Creates the table needed to use the SQL cache backend"
- from django.core import db, meta
+ from django.db import backend, connection, transaction, get_creation_module, models
+ data_types = get_creation_module().DATA_TYPES
fields = (
# "key" is a reserved word in MySQL, so use "cache_key" instead.
- meta.CharField(name='cache_key', maxlength=255, unique=True, primary_key=True),
- meta.TextField(name='value'),
- meta.DateTimeField(name='expires', db_index=True),
+ models.CharField(name='cache_key', maxlength=255, unique=True, primary_key=True),
+ models.TextField(name='value'),
+ models.DateTimeField(name='expires', db_index=True),
)
table_output = []
index_output = []
for f in fields:
- field_output = [db.db.quote_name(f.column), db.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")
@@ -849,18 +1019,18 @@ 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, db.db.quote_name(tablename),
- db.db.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 (" % db.db.quote_name(tablename)]
+ full_statement = ["CREATE TABLE %s (" % backend.quote_name(tablename)]
for i, line in enumerate(table_output):
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
full_statement.append(');')
- curs = db.db.cursor()
+ curs = connection.cursor()
curs.execute("\n".join(full_statement))
for statement in index_output:
curs.execute(statement)
- db.db.commit()
+ transaction.commit_unless_managed()
createcachetable.args = "[tablename]"
def run_shell(use_plain=False):
@@ -877,17 +1047,22 @@ def run_shell(use_plain=False):
code.interact()
run_shell.args = '[--plain]'
+def dbshell():
+ "Runs the command-line client for the current DATABASE_ENGINE."
+ from django.db import runshell
+ runshell()
+dbshell.args = ""
+
# Utilities for command-line script
DEFAULT_ACTION_MAPPING = {
'adminindex': get_admin_index,
- 'createsuperuser': createsuperuser,
'createcachetable' : createcachetable,
-# 'dbcheck': database_check,
- 'init': init,
+ 'dbshell': dbshell,
+ 'diffsettings': diffsettings,
'inspectdb': inspectdb,
'install': install,
- 'installperms': installperms,
+ 'reset': reset,
'runserver': runserver,
'shell': run_shell,
'sql': get_sql_create,
@@ -899,10 +1074,20 @@ DEFAULT_ACTION_MAPPING = {
'sqlsequencereset': get_sql_sequence_reset,
'startapp': startapp,
'startproject': startproject,
+ 'syncdb': syncdb,
'validate': validate,
}
-NO_SQL_TRANSACTION = ('adminindex', 'createcachetable', 'dbcheck', 'install', 'installperms', 'sqlindexes')
+NO_SQL_TRANSACTION = (
+ 'adminindex',
+ 'createcachetable',
+ 'dbshell',
+ 'diffsettings',
+ 'install',
+ 'reset',
+ 'sqlindexes',
+ 'syncdb',
+)
class DjangoOptionParser(OptionParser):
def print_usage_and_exit(self):
@@ -914,18 +1099,18 @@ def get_usage(action_mapping):
Returns a usage string. Doesn't do the options stuff, because optparse
takes care of that.
"""
- usage = ["usage: %prog action [options]\nactions:"]
+ usage = ["%prog action [options]\nactions:"]
available_actions = action_mapping.keys()
available_actions.sort()
for a in available_actions:
func = action_mapping[a]
usage.append(" %s %s" % (a, func.args))
- usage.extend(textwrap.wrap(getattr(func, 'help_doc', func.__doc__), initial_indent=' ', subsequent_indent=' '))
+ usage.extend(textwrap.wrap(getattr(func, 'help_doc', textwrap.dedent(func.__doc__.strip())), initial_indent=' ', subsequent_indent=' '))
usage.append("")
return '\n'.join(usage[:-1]) # Cut off last list element, an empty space.
def print_error(msg, cmd):
- sys.stderr.write('Error: %s\nRun "%s --help" for help.\n' % (msg, cmd))
+ sys.stderr.write(style.ERROR('Error: %s' % msg) + '\nRun "%s --help" for help.\n' % cmd)
sys.exit(1)
def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING):
@@ -961,31 +1146,16 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING):
from django.utils import translation
translation.activate('en-us')
- if action == 'createsuperuser':
- try:
- username, email, password = args[1], args[2], args[3]
- except IndexError:
- if len(args) == 1: # We got no arguments, just the action.
- action_mapping[action]()
- else:
- sys.stderr.write("Error: %r requires arguments of 'username email password' or no argument at all.\n")
- sys.exit(1)
- else:
- action_mapping[action](username, email, password)
- elif action == 'shell':
+ if action == 'shell':
action_mapping[action](options.plain is True)
- elif action in ('init', 'validate'):
+ elif action in ('syncdb', 'validate', 'diffsettings', 'dbshell'):
action_mapping[action]()
elif action == 'inspectdb':
try:
- param = args[1]
- except IndexError:
- parser.print_usage_and_exit()
- try:
- for line in action_mapping[action](param):
+ for line in action_mapping[action]():
print line
except NotImplementedError:
- sys.stderr.write("Error: %r isn't supported for the currently selected database backend.\n" % action)
+ sys.stderr.write(style.ERROR("Error: %r isn't supported for the currently selected database backend.\n" % action))
sys.exit(1)
elif action == 'createcachetable':
try:
@@ -1009,25 +1179,22 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING):
addr, port = '', args[1]
action_mapping[action](addr, port)
else:
- from django.core import meta
- if action == 'dbcheck':
- mod_list = meta.get_all_installed_modules()
- else:
- try:
- mod_list = [meta.get_app(app_label) for app_label in args[1:]]
- except ImportError, e:
- sys.stderr.write("Error: %s. Are you sure your INSTALLED_APPS setting is correct?\n" % e)
- sys.exit(1)
- if not mod_list:
- parser.print_usage_and_exit()
+ from django.db import models
+ try:
+ mod_list = [models.get_app(app_label) for app_label in args[1:]]
+ except ImportError, e:
+ sys.stderr.write(style.ERROR("Error: %s. Are you sure your INSTALLED_APPS setting is correct?\n" % e))
+ sys.exit(1)
+ if not mod_list:
+ parser.print_usage_and_exit()
if action not in NO_SQL_TRANSACTION:
- print "BEGIN;"
+ print style.SQL_KEYWORD("BEGIN;")
for mod in mod_list:
output = action_mapping[action](mod)
if output:
print '\n'.join(output)
if action not in NO_SQL_TRANSACTION:
- print "COMMIT;"
+ print style.SQL_KEYWORD("COMMIT;")
def execute_manager(settings_mod):
# Add this project to sys.path so that it's importable in the conventional
@@ -1042,10 +1209,18 @@ def execute_manager(settings_mod):
# Set DJANGO_SETTINGS_MODULE appropriately.
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.settings' % project_name
+ action_mapping = DEFAULT_ACTION_MAPPING.copy()
+
# Remove the "startproject" command from the action_mapping, because that's
# a django-admin.py command, not a manage.py command.
- action_mapping = DEFAULT_ACTION_MAPPING.copy()
del action_mapping['startproject']
+ # Override the startapp handler so that it always uses the
+ # project_directory, not the current working directory (which is default).
+ action_mapping['startapp'] = lambda app_name, directory: startapp(app_name, project_directory)
+ action_mapping['startapp'].__doc__ = startapp.__doc__
+ action_mapping['startapp'].help_doc = startapp.help_doc
+ action_mapping['startapp'].args = startapp.args
+
# Run the django-admin.py command.
execute_from_command_line(action_mapping)