2005-07-20 23:00:34 +08:00
# Django management-related functions, including "CREATE TABLE" generation and
# development-server initialization.
import django
2006-05-02 09:31:56 +08:00
from django . core . exceptions import ImproperlyConfigured
import os , re , shutil , sys , textwrap
2005-12-06 13:17:52 +08:00
from optparse import OptionParser
2006-05-02 09:31:56 +08:00
from django . utils import termcolors
2005-07-20 23:00:34 +08:00
2006-02-05 04:08:30 +08:00
# For Python 2.3
if not hasattr ( __builtins__ , ' set ' ) :
from sets import Set as set
2005-07-20 23:00:34 +08:00
MODULE_TEMPLATE = ''' { %% if perms. %(app)s . %(addperm)s or perms. %(app)s . %(changeperm)s %% }
< tr >
2005-08-07 09:02:48 +08:00
< th > { % % if perms . % ( app ) s . % ( changeperm ) s % % } < a href = " %(app)s / %(mod)s / " > { % % endif % % } % ( name ) s { % % if perms . % ( app ) s . % ( changeperm ) s % % } < / a > { % % endif % % } < / th >
< td class = " x50 " > { % % if perms . % ( app ) s . % ( addperm ) s % % } < a href = " %(app)s / %(mod)s /add/ " class = " addlink " > { % % endif % % } Add { % % if perms . % ( app ) s . % ( addperm ) s % % } < / a > { % % endif % % } < / td >
< td class = " x75 " > { % % if perms . % ( app ) s . % ( changeperm ) s % % } < a href = " %(app)s / %(mod)s / " class = " changelink " > { % % endif % % } Change { % % if perms . % ( app ) s . % ( changeperm ) s % % } < / a > { % % endif % % } < / td >
2005-07-20 23:00:34 +08:00
< / tr >
{ % % endif % % } '''
2006-05-02 09:31:56 +08:00
APP_ARGS = ' [appname ...] '
2005-07-20 23:00:34 +08:00
# Use django.__path__[0] because we don't know which directory django into
# which has been installed.
2005-10-06 22:30:35 +08:00
PROJECT_TEMPLATE_DIR = os . path . join ( django . __path__ [ 0 ] , ' conf ' , ' %s _template ' )
2005-07-20 23:00:34 +08:00
2005-12-06 13:17:52 +08:00
INVALID_PROJECT_NAMES = ( ' django ' , ' test ' )
2006-05-02 09:31:56 +08:00
# 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 disable_termcolors ( ) :
class dummy :
def __getattr__ ( self , attr ) :
return lambda x : x
global style
style = dummy ( )
2006-07-14 13:26:55 +08:00
# Disable terminal coloring on Windows, Pocket PC, or if somebody's piping the output.
if sys . platform == ' win32 ' or sys . platform == ' Pocket PC ' or not sys . stdout . isatty ( ) :
2006-05-02 09:31:56 +08:00
disable_termcolors ( )
2005-07-20 23:00:34 +08:00
def _is_valid_dir_name ( s ) :
return bool ( re . search ( r ' ^ \ w+$ ' , s ) )
2006-05-02 09:31:56 +08:00
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 )
2006-01-15 13:48:13 +08:00
# 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
# field as the field to which it points.
get_rel_data_type = lambda f : ( f . get_internal_type ( ) in ( ' AutoField ' , ' PositiveIntegerField ' , ' PositiveSmallIntegerField ' ) ) and ' IntegerField ' or f . get_internal_type ( )
2005-09-26 02:29:59 +08:00
2006-02-18 02:33:09 +08:00
def get_version ( ) :
" Returns the version as a human-format string. "
from django import VERSION
v = ' . ' . join ( [ str ( i ) for i in VERSION [ : - 1 ] ] )
2006-05-02 09:31:56 +08:00
if VERSION [ - 1 ] :
2006-07-28 01:03:35 +08:00
v + = ' - ' + VERSION [ - 1 ]
2006-02-18 02:33:09 +08:00
return v
2006-05-02 09:31:56 +08:00
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 )
2006-08-09 15:16:35 +08:00
# Get installed models, so we generate REFERENCES right.
# We trim models from the current app so that the sqlreset command does not
# generate invalid SQL (leaving models out of known_models is harmless, so
# we can be conservative).
app_models = models . get_models ( app )
2005-07-20 23:00:34 +08:00
final_output = [ ]
2006-08-09 15:16:35 +08:00
known_models = set ( [ model for model in _get_installed_models ( _get_table_list ( ) ) if model not in app_models ] )
2006-05-02 09:31:56 +08:00
pending_references = { }
2006-07-21 11:55:07 +08:00
for model in app_models :
output , references = _get_sql_model_create ( model , known_models )
2006-05-02 09:31:56 +08:00
final_output . extend ( output )
2006-06-21 11:39:47 +08:00
for refto , refs in references . items ( ) :
2006-07-21 11:56:06 +08:00
pending_references . setdefault ( refto , [ ] ) . extend ( refs )
2006-07-21 11:55:07 +08:00
final_output . extend ( _get_sql_for_pending_references ( model , pending_references ) )
2006-05-02 09:31:56 +08:00
# Keep track of the fact that we've created the table for this model.
2006-07-21 11:55:07 +08:00
known_models . add ( model )
2006-05-02 09:31:56 +08:00
# Create the many-to-many join tables.
2006-07-21 11:55:07 +08:00
for model in app_models :
final_output . extend ( _get_many_to_many_sql_for_model ( model ) )
2006-05-02 09:31:56 +08:00
# 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 :
2006-08-09 15:16:35 +08:00
alter_sql = [ ]
2006-07-21 11:55:07 +08:00
for model in not_installed_models :
2006-08-09 15:16:35 +08:00
alter_sql . extend ( [ ' -- ' + sql for sql in
2006-07-21 11:55:07 +08:00
_get_sql_for_pending_references ( model , pending_references ) ] )
2006-08-09 15:16:35 +08:00
if alter_sql :
final_output . append ( ' -- The following references should be added but depend on non-existent tables: ' )
final_output . extend ( alter_sql )
2006-05-02 09:31:56 +08:00
2005-07-20 23:00:34 +08:00
return final_output
2006-05-02 09:31:56 +08:00
get_sql_create . help_doc = " Prints the CREATE TABLE SQL statements for the given app name(s). "
2005-07-20 23:00:34 +08:00
get_sql_create . args = APP_ARGS
2006-07-21 11:55:07 +08:00
def _get_sql_model_create ( model , known_models = set ( ) ) :
2006-05-02 09:31:56 +08:00
"""
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
2006-07-21 11:55:07 +08:00
opts = model . _meta
2006-05-02 09:31:56 +08:00
final_output = [ ]
table_output = [ ]
pending_references = { }
for f in opts . fields :
2006-09-26 10:58:36 +08:00
if isinstance ( f , ( models . ForeignKey , models . OneToOneField ) ) :
2006-05-02 09:31:56 +08:00
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 ( ' %s NULL ' % ( 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 :
2006-07-21 11:55:07 +08:00
if f . rel . to in known_models :
2006-05-02 09:31:56 +08:00
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.
2006-07-21 11:55:07 +08:00
pr = pending_references . setdefault ( f . rel . to , [ ] ) . append ( ( model , f ) )
2006-05-02 09:31:56 +08:00
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
2006-07-21 11:55:07 +08:00
def _get_sql_for_pending_references ( model , pending_references ) :
2006-05-02 09:31:56 +08:00
"""
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 :
2006-07-21 11:55:07 +08:00
opts = model . _meta
if model in pending_references :
for rel_class , f in pending_references [ model ] :
2006-05-02 09:31:56 +08:00
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
2006-08-02 05:46:51 +08:00
# For MySQL, r_name must be unique in the first 64 characters.
# So we are careful with character usage here.
r_name = ' %s _refs_ %s _ %x ' % ( r_col , col , abs ( hash ( ( r_table , table ) ) ) )
2006-05-02 09:31:56 +08:00
final_output . append ( style . SQL_KEYWORD ( ' ALTER TABLE ' ) + ' %s ADD CONSTRAINT %s FOREIGN KEY ( %s ) REFERENCES %s ( %s ); ' % \
2006-07-19 18:14:43 +08:00
( backend . quote_name ( r_table ) , r_name ,
2006-05-02 09:31:56 +08:00
backend . quote_name ( r_col ) , backend . quote_name ( table ) , backend . quote_name ( col ) ) )
2006-07-21 11:55:07 +08:00
del pending_references [ model ]
2006-05-02 09:31:56 +08:00
return final_output
2006-07-21 11:55:07 +08:00
def _get_many_to_many_sql_for_model ( model ) :
2006-05-02 09:31:56 +08:00
from django . db import backend , get_creation_module
2006-06-17 03:18:30 +08:00
from django . db . models import GenericRel
2006-07-28 01:03:35 +08:00
2006-05-02 09:31:56 +08:00
data_types = get_creation_module ( ) . DATA_TYPES
2006-07-21 11:55:07 +08:00
opts = model . _meta
2006-05-02 09:31:56 +08:00
final_output = [ ]
for f in opts . many_to_many :
2006-06-17 03:18:30 +08:00
if not isinstance ( f . rel , GenericRel ) :
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 ) )
2006-05-02 09:31:56 +08:00
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
2005-07-20 23:00:34 +08:00
try :
2006-05-02 09:31:56 +08:00
cursor = connection . cursor ( )
2005-07-20 23:00:34 +08:00
except :
cursor = None
2005-11-10 07:50:32 +08:00
2006-05-02 09:31:56 +08:00
# Figure out which tables already exist
if cursor :
table_names = introspection . get_table_list ( cursor )
2005-11-10 07:50:32 +08:00
else :
2006-05-02 09:31:56 +08:00
table_names = [ ]
2005-11-10 07:50:32 +08:00
2005-07-20 23:00:34 +08:00
output = [ ]
2005-11-10 07:50:32 +08:00
# Output DROP TABLE statements for standard application tables.
2006-05-02 09:31:56 +08:00
to_delete = set ( )
references_to_delete = { }
app_models = models . get_models ( app )
2006-07-21 11:55:07 +08:00
for model in app_models :
if cursor and model . _meta . db_table in table_names :
2006-05-02 09:31:56 +08:00
# The table exists, so it needs to be dropped
2006-07-21 11:55:07 +08:00
opts = model . _meta
2006-05-02 09:31:56 +08:00
for f in opts . fields :
if f . rel and f . rel . to not in to_delete :
2006-07-21 11:55:07 +08:00
references_to_delete . setdefault ( f . rel . to , [ ] ) . append ( ( model , f ) )
2006-05-02 09:31:56 +08:00
2006-07-21 11:55:07 +08:00
to_delete . add ( model )
2006-05-02 09:31:56 +08:00
2006-07-21 11:55:07 +08:00
for model in app_models :
if cursor and model . _meta . db_table in table_names :
2006-05-02 09:31:56 +08:00
# Drop the table now
output . append ( ' %s %s ; ' % ( style . SQL_KEYWORD ( ' DROP TABLE ' ) ,
2006-07-21 11:55:07 +08:00
style . SQL_TABLE ( backend . quote_name ( model . _meta . db_table ) ) ) )
if backend . supports_constraints and references_to_delete . has_key ( model ) :
for rel_class , f in references_to_delete [ model ] :
2006-05-02 09:31:56 +08:00
table = rel_class . _meta . db_table
col = f . column
2006-07-21 11:55:07 +08:00
r_table = model . _meta . db_table
r_col = model . _meta . get_field ( f . rel . field_name ) . column
2006-05-02 09:31:56 +08:00
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 ( ) ) ,
2006-08-10 11:48:34 +08:00
style . SQL_FIELD ( backend . quote_name ( ' %s _refs_ %s _ %x ' % ( col , r_col , abs ( hash ( ( table , r_table ) ) ) ) ) ) ) )
2006-07-21 11:55:07 +08:00
del references_to_delete [ model ]
2005-11-10 07:50:32 +08:00
# Output DROP TABLE statements for many-to-many tables.
2006-07-21 11:55:07 +08:00
for model in app_models :
opts = model . _meta
2005-07-20 23:00:34 +08:00
for f in opts . many_to_many :
2006-05-02 09:31:56 +08:00
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 = app_models [ 0 ] . _meta . app_label
2005-07-27 06:38:54 +08:00
2005-10-11 04:14:56 +08:00
# Close database connection explicitly, in case this output is being piped
# directly into a database client, to avoid locking issues.
2006-05-02 09:31:56 +08:00
if cursor :
2006-02-02 12:50:16 +08:00
cursor . close ( )
2006-05-02 09:31:56 +08:00
connection . close ( )
2005-10-11 04:14:56 +08:00
2005-07-20 23:00:34 +08:00
return output [ : : - 1 ] # Reverse it, to deal with table dependencies.
2006-05-02 09:31:56 +08:00
get_sql_delete . help_doc = " Prints the DROP TABLE SQL statements for the given app name(s). "
2005-07-20 23:00:34 +08:00
get_sql_delete . args = APP_ARGS
2006-05-02 09:31:56 +08:00
def get_sql_reset ( app ) :
2005-07-20 23:00:34 +08:00
" Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module. "
2006-05-02 09:31:56 +08:00
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). "
2005-07-20 23:00:34 +08:00
get_sql_reset . args = APP_ARGS
2006-05-02 09:31:56 +08:00
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 ' ) )
2005-07-20 23:00:34 +08:00
output = [ ]
2005-09-30 07:43:03 +08:00
2006-05-26 13:20:21 +08:00
# Some backends can't execute more than one SQL statement at a time,
# so split into separate statements.
2006-06-20 16:00:44 +08:00
statements = re . compile ( r " ;[ \ t]*$ " , re . M )
2006-05-26 13:20:21 +08:00
2006-05-02 09:31:56 +08:00
# 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 ) :
2006-09-25 15:36:46 +08:00
fp = open ( sql_file , ' U ' )
2006-06-20 16:00:44 +08:00
for statement in statements . split ( fp . read ( ) ) :
if statement . strip ( ) :
output . append ( statement + " ; " )
2006-05-02 09:31:56 +08:00
fp . close ( )
return output
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 ' ) )
2006-07-21 11:55:07 +08:00
for model in app_models :
output . extend ( get_sql_initial_data_for_model ( model ) )
2006-05-02 09:31:56 +08:00
2005-07-20 23:00:34 +08:00
return output
2006-05-02 09:31:56 +08:00
get_sql_initial_data . help_doc = " Prints the initial INSERT SQL statements for the given app name(s). "
2005-07-20 23:00:34 +08:00
get_sql_initial_data . args = APP_ARGS
2006-05-02 09:31:56 +08:00
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
2005-07-20 23:00:34 +08:00
output = [ ]
2006-07-21 11:55:07 +08:00
for model in models . get_models ( app ) :
for f in model . _meta . fields :
2006-05-02 09:31:56 +08:00
if isinstance ( f , models . AutoField ) :
output . append ( " %s setval( ' %s ' , ( %s max( %s ) %s %s )); " % \
( style . SQL_KEYWORD ( ' SELECT ' ) ,
2006-07-21 11:55:07 +08:00
style . SQL_FIELD ( ' %s _ %s _seq ' % ( model . _meta . db_table , f . column ) ) ,
2006-05-02 09:31:56 +08:00
style . SQL_KEYWORD ( ' SELECT ' ) ,
style . SQL_FIELD ( backend . quote_name ( f . column ) ) ,
style . SQL_KEYWORD ( ' FROM ' ) ,
2006-07-21 11:55:07 +08:00
style . SQL_TABLE ( backend . quote_name ( model . _meta . db_table ) ) ) )
2006-05-02 09:31:56 +08:00
break # Only one AutoField is allowed per model, so don't bother continuing.
2006-07-21 11:55:07 +08:00
for f in model . _meta . many_to_many :
2006-05-02 09:31:56 +08:00
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 ( ) ) ) )
2005-07-20 23:00:34 +08:00
return output
2006-05-02 09:31:56 +08:00
get_sql_sequence_reset . help_doc = " Prints the SQL statements for resetting PostgreSQL sequences for the given app name(s). "
2005-07-20 23:00:34 +08:00
get_sql_sequence_reset . args = APP_ARGS
2006-05-02 09:31:56 +08:00
def get_sql_indexes ( app ) :
2006-10-03 21:23:49 +08:00
" Returns a list of the CREATE INDEX SQL statements for all models in the given app. "
2006-10-03 21:05:10 +08:00
from django . db import models
2005-07-20 23:00:34 +08:00
output = [ ]
2006-07-21 11:55:07 +08:00
for model in models . get_models ( app ) :
2006-10-03 21:58:33 +08:00
output . extend ( get_sql_indexes_for_model ( model ) )
2005-07-20 23:00:34 +08:00
return output
2005-09-03 02:26:16 +08:00
get_sql_indexes . help_doc = " Prints the CREATE INDEX SQL statements for the given model module name(s). "
2005-07-20 23:00:34 +08:00
get_sql_indexes . args = APP_ARGS
2006-10-03 21:58:33 +08:00
def get_sql_indexes_for_model ( model ) :
2006-10-03 21:23:49 +08:00
" Returns the CREATE INDEX SQL statements for a single model "
2006-10-03 21:05:10 +08:00
from django . db import backend
output = [ ]
for f in model . _meta . fields :
if f . db_index :
unique = f . unique and ' UNIQUE ' or ' '
output . append (
style . SQL_KEYWORD ( ' CREATE %s INDEX ' % unique ) + ' ' + \
style . SQL_TABLE ( ' %s _ %s ' % ( model . _meta . db_table , f . column ) ) + ' ' + \
style . SQL_KEYWORD ( ' ON ' ) + ' ' + \
style . SQL_TABLE ( backend . quote_name ( model . _meta . db_table ) ) + ' ' + \
" ( %s ); " % style . SQL_FIELD ( backend . quote_name ( f . column ) )
)
return output
2006-05-02 09:31:56 +08:00
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). "
2005-07-20 23:00:34 +08:00
get_sql_all . args = APP_ARGS
2006-10-03 20:57:05 +08:00
def syncdb ( verbosity = 1 , interactive = True ) :
2006-05-02 09:31:56 +08:00
" 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
disable_termcolors ( )
# First, try validating the models.
_check_for_validation_errors ( )
# Import the 'management' module within each installed app, to register
# dispatcher events.
for app_name in settings . INSTALLED_APPS :
2005-07-20 23:00:34 +08:00
try :
2006-05-02 09:31:56 +08:00
__import__ ( app_name + ' .management ' , ' ' , ' ' , [ ' ' ] )
except ImportError :
pass
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 ( ) :
2006-10-03 21:23:49 +08:00
app_name = app . __name__ . split ( ' . ' ) [ - 2 ]
2006-05-02 09:31:56 +08:00
model_list = models . get_models ( app )
for model in model_list :
# Create the model's database table, if it doesn't already exist.
2006-10-03 20:57:05 +08:00
if verbosity > = 2 :
2006-10-03 21:23:49 +08:00
print " Processing %s . %s model " % ( app_name , model . _meta . object_name )
2006-05-02 09:31:56 +08:00
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 )
2006-06-21 11:39:47 +08:00
for refto , refs in references . items ( ) :
2006-10-03 20:57:05 +08:00
pending_references . setdefault ( refto , [ ] ) . extend ( refs )
2006-05-02 09:31:56 +08:00
sql . extend ( _get_sql_for_pending_references ( model , pending_references ) )
2006-10-03 20:57:05 +08:00
if verbosity > = 1 :
2006-08-27 20:46:39 +08:00
print " Creating table %s " % model . _meta . db_table
2006-05-02 09:31:56 +08:00
for statement in sql :
cursor . execute ( statement )
2006-05-05 10:38:32 +08:00
table_list . append ( model . _meta . db_table )
2006-05-02 09:31:56 +08:00
for model in model_list :
if model in created_models :
sql = _get_many_to_many_sql_for_model ( model )
if sql :
2006-08-27 20:46:39 +08:00
if verbosity > = 2 :
2006-10-03 21:23:49 +08:00
print " Creating many-to-many tables for %s . %s model " % ( app_name , model . _meta . object_name )
2006-05-02 09:31:56 +08:00
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 ( ) :
2006-10-03 21:23:49 +08:00
app_name = app . __name__ . split ( ' . ' ) [ - 2 ]
2006-10-03 20:57:05 +08:00
if verbosity > = 2 :
2006-10-03 21:23:49 +08:00
print " Running post-sync handlers for application " , app_name
2006-05-02 09:31:56 +08:00
dispatcher . send ( signal = signals . post_syncdb , sender = app ,
2006-09-12 02:42:56 +08:00
app = app , created_models = created_models ,
2006-08-27 20:46:39 +08:00
verbosity = verbosity , interactive = interactive )
2006-05-02 09:31:56 +08:00
# 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 :
2006-10-03 20:57:05 +08:00
if verbosity > = 1 :
2006-10-09 13:55:04 +08:00
print " Installing initial data for %s . %s model " % ( app_name , model . _meta . object_name )
2006-05-02 09:31:56 +08:00
try :
for sql in initial_sql :
cursor . execute ( sql )
except Exception , e :
2006-10-03 21:23:49 +08:00
sys . stderr . write ( " Failed to install initial SQL data for %s . %s model: %s " % \
( app_name , model . _meta . object_name , e ) )
2006-05-02 09:31:56 +08:00
transaction . rollback_unless_managed ( )
else :
transaction . commit_unless_managed ( )
2006-10-03 21:46:11 +08:00
# Install SQL indicies for all newly created models
for app in models . get_apps ( ) :
app_name = app . __name__ . split ( ' . ' ) [ - 2 ]
for model in models . get_models ( app ) :
if model in created_models :
2006-10-03 21:58:33 +08:00
index_sql = get_sql_indexes_for_model ( model )
2006-10-03 21:46:11 +08:00
if index_sql :
if verbosity > = 1 :
print " Installing index for %s . %s model " % ( app_name , model . _meta . object_name )
try :
for sql in index_sql :
cursor . execute ( sql )
except Exception , e :
sys . stderr . write ( " Failed to install index for %s . %s model: %s " % \
( app_name , model . _meta . object_name , e ) )
transaction . rollback_unless_managed ( )
else :
transaction . commit_unless_managed ( )
2006-10-03 21:23:49 +08:00
2006-05-02 09:31:56 +08:00
syncdb . args = ' '
def get_admin_index ( app ) :
" Returns admin-index template snippet (in list form) for the given app. "
2005-08-02 05:29:52 +08:00
from django . utils . text import capfirst
2006-05-02 09:31:56 +08:00
from django . db . models import get_models
2005-07-20 23:00:34 +08:00
output = [ ]
2006-05-02 09:31:56 +08:00
app_models = get_models ( app )
app_label = app_models [ 0 ] . _meta . app_label
2005-07-20 23:00:34 +08:00
output . append ( ' { %% if perms. %s %% } ' % app_label )
output . append ( ' <div class= " module " ><h2> %s </h2><table> ' % app_label . title ( ) )
2006-07-21 11:55:07 +08:00
for model in app_models :
if model . _meta . admin :
2005-07-20 23:00:34 +08:00
output . append ( MODULE_TEMPLATE % {
' app ' : app_label ,
2006-07-21 11:55:07 +08:00
' mod ' : model . _meta . module_name ,
' name ' : capfirst ( model . _meta . verbose_name_plural ) ,
' addperm ' : model . _meta . get_add_permission ( ) ,
' changeperm ' : model . _meta . get_change_permission ( ) ,
2005-07-20 23:00:34 +08:00
} )
output . append ( ' </table></div> ' )
output . append ( ' { % e ndif % } ' )
return output
2006-05-02 09:31:56 +08:00
get_admin_index . help_doc = " Prints the admin-index template snippet for the given app name(s). "
2005-07-20 23:00:34 +08:00
get_admin_index . args = APP_ARGS
2006-05-02 09:31:56 +08:00
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
2006-09-12 02:42:56 +08:00
user_settings = _module_to_dict ( settings . _target )
2006-05-02 09:31:56 +08:00
default_settings = _module_to_dict ( global_settings )
2005-07-20 23:00:34 +08:00
2006-05-02 09:31:56 +08:00
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 ) :
2005-07-20 23:00:34 +08:00
" Executes the equivalent of ' get_sql_all ' in the current database. "
2006-05-02 09:31:56 +08:00
from django . db import connection , transaction
app_name = app . __name__ . split ( ' . ' ) [ - 2 ]
disable_termcolors ( )
2005-09-03 02:38:23 +08:00
# First, try validating the models.
2006-05-02 09:31:56 +08:00
_check_for_validation_errors ( app )
sql_list = get_sql_all ( app )
2005-09-03 02:38:23 +08:00
2005-07-20 23:00:34 +08:00
try :
2006-05-02 09:31:56 +08:00
cursor = connection . cursor ( )
2005-07-20 23:00:34 +08:00
for sql in sql_list :
cursor . execute ( sql )
except Exception , e :
2006-05-02 09:31:56 +08:00
sys . stderr . write ( style . ERROR ( """ Error: %s couldn ' t be installed. Possible reasons:
2005-07-20 23:00:34 +08:00
* 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 .
2006-05-02 09:31:56 +08:00
The full error : """ % (app_name, app_name)) + style.ERROR_OUTPUT(str(e)) + ' \n ' )
transaction . rollback_unless_managed ( )
2005-07-20 23:00:34 +08:00
sys . exit ( 1 )
2006-05-02 09:31:56 +08:00
transaction . commit_unless_managed ( )
install . help_doc = " Executes ``sqlall`` for the given app(s) in the current database. "
2005-07-20 23:00:34 +08:00
install . args = APP_ARGS
2006-10-03 18:01:50 +08:00
def reset ( app , interactive = True ) :
2006-05-02 09:31:56 +08:00
" Executes the equivalent of ' get_sql_reset ' in the current database. "
from django . db import connection , transaction
app_name = app . __name__ . split ( ' . ' ) [ - 2 ]
disable_termcolors ( )
# First, try validating the models.
_check_for_validation_errors ( app )
sql_list = get_sql_reset ( app )
2006-10-03 18:01:50 +08:00
if interactive :
confirm = raw_input ( """
2006-05-02 09:31:56 +08:00
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 : """ )
2006-10-03 18:01:50 +08:00
else :
confirm = ' yes '
2006-05-02 09:31:56 +08:00
if confirm == ' yes ' :
try :
cursor = connection . cursor ( )
for sql in sql_list :
cursor . execute ( sql )
except Exception , e :
2006-10-03 18:01:50 +08:00
sys . stderr . write ( style . ERROR ( """ Error: %s couldn ' t be reset. Possible reasons:
2006-05-02 09:31:56 +08:00
* The database isn ' t running or isn ' t configured correctly .
2006-10-03 18:01:50 +08:00
* At least one of the database tables doesn ' t exist.
2006-05-02 09:31:56 +08:00
* 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
2005-10-24 06:43:24 +08:00
2005-07-20 23:00:34 +08:00
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 ) :
2006-05-02 09:31:56 +08:00
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 ) ) )
2005-07-20 23:00:34 +08:00
sys . exit ( 1 )
top_dir = os . path . join ( directory , name )
try :
os . mkdir ( top_dir )
except OSError , e :
2006-05-02 09:31:56 +08:00
sys . stderr . write ( style . ERROR ( " Error: %s \n " % e ) )
2005-07-20 23:00:34 +08:00
sys . exit ( 1 )
template_dir = PROJECT_TEMPLATE_DIR % app_or_project
for d , subdirs , files in os . walk ( template_dir ) :
relative_dir = d [ len ( template_dir ) + 1 : ] . replace ( ' %s _name ' % app_or_project , name )
if relative_dir :
os . mkdir ( os . path . join ( top_dir , relative_dir ) )
for i , subdir in enumerate ( subdirs ) :
if subdir . startswith ( ' . ' ) :
del subdirs [ i ]
for f in files :
if f . endswith ( ' .pyc ' ) :
continue
2006-05-02 09:31:56 +08:00
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 ' )
2005-07-20 23:00:34 +08:00
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 ( )
2006-05-02 09:31:56 +08:00
shutil . copymode ( path_old , path_new )
2005-07-20 23:00:34 +08:00
def startproject ( project_name , directory ) :
" Creates a Django project for the given project_name in the given directory. "
2005-07-21 10:20:40 +08:00
from random import choice
2005-12-06 13:17:52 +08:00
if project_name in INVALID_PROJECT_NAMES :
2006-05-02 09:31:56 +08:00
sys . stderr . write ( style . ERROR ( " Error: %r isn ' t a valid project name. Please try another. \n " % project_name ) )
2005-12-06 13:17:52 +08:00
sys . exit ( 1 )
2005-07-20 23:00:34 +08:00
_start_helper ( ' project ' , project_name , directory )
# Create a random SECRET_KEY hash, and put it in the main settings.
2005-10-19 09:09:05 +08:00
main_settings_file = os . path . join ( directory , project_name , ' settings.py ' )
2005-07-20 23:00:34 +08:00
settings_contents = open ( main_settings_file , ' r ' ) . read ( )
fp = open ( main_settings_file , ' w ' )
secret_key = ' ' . join ( [ choice ( ' abcdefghijklmnopqrstuvwxyz0123456789!@#$ % ^&*(-_=+) ' ) for i in range ( 50 ) ] )
settings_contents = re . sub ( r " (?<=SECRET_KEY = ' ) ' " , secret_key + " ' " , settings_contents )
fp . write ( settings_contents )
fp . close ( )
startproject . help_doc = " Creates a Django project directory structure for the given project name in the current directory. "
startproject . args = " [projectname] "
def startapp ( app_name , directory ) :
2005-09-03 02:26:16 +08:00
" Creates a Django app for the given app_name in the given directory. "
2005-07-20 23:00:34 +08:00
# Determine the project_name a bit naively -- by looking at the name of
# the parent directory.
2005-10-06 22:30:35 +08:00
project_dir = os . path . normpath ( os . path . join ( directory , ' .. ' ) )
2005-07-20 23:00:34 +08:00
project_name = os . path . basename ( project_dir )
2006-05-19 15:40:06 +08:00
if app_name == os . path . basename ( directory ) :
sys . stderr . write ( style . ERROR ( " Error: You cannot create an app with the same name ( %r ) as your project. \n " % app_name ) )
sys . exit ( 1 )
2005-07-20 23:00:34 +08:00
_start_helper ( ' app ' , app_name , directory , project_name )
startapp . help_doc = " Creates a Django app directory structure for the given app name in the current directory. "
startapp . args = " [appname] "
2006-05-02 09:31:56 +08:00
def inspectdb ( ) :
2005-08-03 01:08:24 +08:00
" Generator that introspects the tables in the given database name and returns a Django model, one line at a time. "
2006-05-02 09:31:56 +08:00
from django . db import connection , get_introspection_module
2006-02-05 04:18:18 +08:00
import keyword
2005-08-03 06:33:39 +08:00
2006-05-02 09:31:56 +08:00
introspection_module = get_introspection_module ( )
2006-08-11 23:32:35 +08:00
table2model = lambda table_name : table_name . title ( ) . replace ( ' _ ' , ' ' )
2005-08-03 06:33:39 +08:00
2006-05-02 09:31:56 +08:00
cursor = connection . cursor ( )
2005-08-03 06:33:39 +08:00
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 "
2006-02-19 06:06:42 +08:00
yield " # * Make sure each model has one field with primary_key=True "
2005-08-03 06:33:39 +08:00
yield " # Feel free to rename the models, but don ' t rename db_table values or field names. "
2005-08-04 22:51:46 +08:00
yield " # "
yield " # Also note: You ' ll have to insert the output of ' django-admin.py sqlinitialdata [appname] ' "
yield " # into your database. "
2005-08-03 06:33:39 +08:00
yield ' '
2006-05-02 09:31:56 +08:00
yield ' from django.db import models '
2005-08-03 01:08:24 +08:00
yield ' '
2006-05-02 09:31:56 +08:00
for table_name in introspection_module . get_table_list ( cursor ) :
yield ' class %s (models.Model): ' % table2model ( table_name )
2005-08-03 06:33:39 +08:00
try :
2006-05-02 09:31:56 +08:00
relations = introspection_module . get_relations ( cursor , table_name )
2005-08-03 06:33:39 +08:00
except NotImplementedError :
relations = { }
2006-02-19 05:26:28 +08:00
try :
2006-05-02 09:31:56 +08:00
indexes = introspection_module . get_indexes ( cursor , table_name )
2006-02-19 05:26:28 +08:00
except NotImplementedError :
indexes = { }
2006-05-02 09:31:56 +08:00
for i , row in enumerate ( introspection_module . get_table_description ( cursor , table_name ) ) :
2006-02-19 05:26:28 +08:00
att_name = row [ 0 ]
2006-02-05 04:08:30 +08:00
comment_notes = [ ] # Holds Field notes, to be displayed in a Python comment.
extra_params = { } # Holds Field parameters such as 'db_column'.
2006-08-11 10:22:42 +08:00
if ' ' in att_name :
extra_params [ ' db_column ' ] = att_name
att_name = att_name . replace ( ' ' , ' ' )
comment_notes . append ( ' Field renamed to remove spaces. ' )
2006-02-19 05:26:28 +08:00
if keyword . iskeyword ( att_name ) :
extra_params [ ' db_column ' ] = att_name
att_name + = ' _field '
2006-02-05 04:08:30 +08:00
comment_notes . append ( ' Field renamed because it was a Python reserved word. ' )
2005-08-03 06:33:39 +08:00
if relations . has_key ( i ) :
2006-02-05 04:08:30 +08:00
rel_to = relations [ i ] [ 1 ] == table_name and " ' self ' " or table2model ( relations [ i ] [ 1 ] )
field_type = ' ForeignKey( %s ' % rel_to
2006-02-19 05:26:28 +08:00
if att_name . endswith ( ' _id ' ) :
att_name = att_name [ : - 3 ]
2005-08-26 12:06:18 +08:00
else :
2006-02-19 05:26:28 +08:00
extra_params [ ' db_column ' ] = att_name
2005-08-03 06:33:39 +08:00
else :
2005-08-04 11:49:24 +08:00
try :
2006-05-02 09:31:56 +08:00
field_type = introspection_module . DATA_TYPES_REVERSE [ row [ 1 ] ]
2005-08-04 11:49:24 +08:00
except KeyError :
field_type = ' TextField '
2006-02-05 04:08:30 +08:00
comment_notes . append ( ' This field type is a guess. ' )
2005-12-01 14:32:25 +08:00
# This is a hook for DATA_TYPES_REVERSE to return a tuple of
# (field_type, extra_params_dict).
if type ( field_type ) is tuple :
2006-02-05 04:08:30 +08:00
field_type , new_params = field_type
extra_params . update ( new_params )
2005-12-01 14:32:25 +08:00
2006-02-19 05:26:28 +08:00
# Add maxlength for all CharFields.
2005-12-01 14:32:25 +08:00
if field_type == ' CharField ' and row [ 3 ] :
extra_params [ ' maxlength ' ] = row [ 3 ]
2006-02-19 07:04:09 +08:00
if field_type == ' FloatField ' :
extra_params [ ' max_digits ' ] = row [ 4 ]
extra_params [ ' decimal_places ' ] = row [ 5 ]
2006-02-19 05:26:28 +08:00
# Add primary_key and unique, if necessary.
column_name = extra_params . get ( ' db_column ' , att_name )
if column_name in indexes :
2006-02-19 05:57:38 +08:00
if indexes [ column_name ] [ ' primary_key ' ] :
2006-02-19 05:26:28 +08:00
extra_params [ ' primary_key ' ] = True
elif indexes [ column_name ] [ ' unique ' ] :
extra_params [ ' unique ' ] = True
2006-02-05 04:08:30 +08:00
field_type + = ' ( '
2006-02-19 05:26:28 +08:00
# Don't output 'id = meta.AutoField(primary_key=True)', because
# that's assumed if it doesn't exist.
if att_name == ' id ' and field_type == ' AutoField( ' and extra_params == { ' primary_key ' : True } :
continue
2006-05-02 09:31:56 +08:00
# 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
2006-05-09 23:36:14 +08:00
if not field_type in ( ' TextField( ' , ' CharField( ' ) :
2006-05-02 09:31:56 +08:00
extra_params [ ' null ' ] = True
field_desc = ' %s = models. %s ' % ( att_name , field_type )
2006-02-05 04:08:30 +08:00
if extra_params :
if not field_desc . endswith ( ' ( ' ) :
field_desc + = ' , '
field_desc + = ' , ' . join ( [ ' %s = %r ' % ( k , v ) for k , v in extra_params . items ( ) ] )
field_desc + = ' ) '
if comment_notes :
field_desc + = ' # ' + ' ' . join ( comment_notes )
2005-11-29 10:05:32 +08:00
yield ' %s ' % field_desc
2006-05-02 09:31:56 +08:00
yield ' class Meta: '
2005-08-26 12:06:18 +08:00
yield ' db_table = %r ' % table_name
2005-08-03 01:08:24 +08:00
yield ' '
inspectdb . help_doc = " Introspects the database tables in the given database and outputs a Django model module. "
2006-05-02 09:31:56 +08:00
inspectdb . args = " "
2005-08-03 01:08:24 +08:00
2005-08-15 13:05:52 +08:00
class ModelErrorCollection :
def __init__ ( self , outfile = sys . stdout ) :
self . errors = [ ]
self . outfile = outfile
2006-06-25 12:24:15 +08:00
def add ( self , context , error ) :
self . errors . append ( ( context , error ) )
self . outfile . write ( style . ERROR ( " %s : %s \n " % ( context , error ) ) )
2006-05-02 09:31:56 +08:00
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 .
"""
2006-09-27 10:42:31 +08:00
from django . conf import settings
from django . db import models , connection
2006-06-25 12:24:15 +08:00
from django . db . models . loading import get_app_errors
2006-05-02 09:31:56 +08:00
from django . db . models . fields . related import RelatedObject
2005-08-15 13:05:52 +08:00
2005-09-03 02:28:26 +08:00
e = ModelErrorCollection ( outfile )
2006-07-28 01:03:35 +08:00
2006-06-25 12:24:15 +08:00
for ( app_name , error ) in get_app_errors ( ) . items ( ) :
e . add ( app_name , error )
2006-07-28 01:03:35 +08:00
2006-05-02 09:31:56 +08:00
for cls in models . get_models ( app ) :
opts = cls . _meta
2005-08-15 13:05:52 +08:00
2006-05-02 09:31:56 +08:00
# Do field-specific validation.
for f in opts . fields :
2006-06-07 12:22:42 +08:00
if f . name == ' id ' and not f . primary_key and opts . pk . name == ' id ' :
e . add ( opts , ' " %s " : You can \' t use " id " as a field name, because each model automatically gets an " id " field if none of the fields have primary_key=True. You need to either remove/rename your " id " field or add primary_key=True to a field. ' % f . name )
2006-05-02 09:31:56 +08:00
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 :
2006-06-07 10:46:08 +08:00
if not hasattr ( f . choices , ' __iter__ ' ) :
e . add ( opts , ' " %s " : " choices " should be iterable (e.g., a tuple or list). ' % f . name )
2005-09-20 10:48:25 +08:00
else :
2006-05-02 09:31:56 +08:00
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 )
2006-09-27 10:42:31 +08:00
# Check that maxlength <= 255 if using older MySQL versions.
if settings . DATABASE_ENGINE == ' mysql ' :
db_version = connection . get_server_version ( )
if db_version < ( 5 , 0 , 3 ) and isinstance ( f , ( models . CharField , models . CommaSeparatedIntegerField , models . SlugField ) ) and f . maxlength > 255 :
2006-09-27 10:50:46 +08:00
e . add ( opts , ' " %s " : %s cannot have a " maxlength " greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s ). ' % ( f . name , f . __class__ . __name__ , ' . ' . join ( [ str ( n ) for n in db_version [ : 3 ] ] ) ) )
2006-09-27 10:42:31 +08:00
2006-05-02 09:31:56 +08:00
# 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 ( ) :
2006-06-23 12:37:00 +08:00
e . add ( opts , " ' %s ' has relation with model %s , which has not been installed " % ( f . name , rel_opts . object_name ) )
2006-05-02 09:31:56 +08:00
rel_name = RelatedObject ( f . rel . to , cls , f ) . get_accessor_name ( )
2006-06-27 23:21:43 +08:00
rel_query_name = f . related_query_name ( )
2006-05-02 09:31:56 +08:00
for r in rel_opts . fields :
if r . name == rel_name :
2006-06-27 23:21:43 +08:00
e . add ( opts , " Accessor for field ' %s ' clashes with field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
if r . name == rel_query_name :
e . add ( opts , " Reverse query name for field ' %s ' clashes with field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
2006-05-02 09:31:56 +08:00
for r in rel_opts . many_to_many :
if r . name == rel_name :
2006-06-27 23:21:43 +08:00
e . add ( opts , " Accessor for field ' %s ' clashes with m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
if r . name == rel_query_name :
e . add ( opts , " Reverse query name for field ' %s ' clashes with m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
2006-05-02 09:31:56 +08:00
for r in rel_opts . get_all_related_many_to_many_objects ( ) :
if r . get_accessor_name ( ) == rel_name :
2006-06-27 23:21:43 +08:00
e . add ( opts , " Accessor for field ' %s ' clashes with related m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
if r . get_accessor_name ( ) == rel_query_name :
e . add ( opts , " Reverse query name for field ' %s ' clashes with related m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
2006-05-02 09:31:56 +08:00
for r in rel_opts . get_all_related_objects ( ) :
2006-06-27 23:21:43 +08:00
if r . field is not f :
if r . get_accessor_name ( ) == rel_name :
e . add ( opts , " Accessor for field ' %s ' clashes with related field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
if r . get_accessor_name ( ) == rel_query_name :
e . add ( opts , " Reverse query name for field ' %s ' clashes with related field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
2006-05-02 09:31:56 +08:00
2006-07-28 01:03:35 +08:00
2006-05-02 09:31:56 +08:00
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 ( ) :
2006-06-23 12:37:00 +08:00
e . add ( opts , " ' %s ' has m2m relation with model %s , which has not been installed " % ( f . name , rel_opts . object_name ) )
2006-05-02 09:31:56 +08:00
rel_name = RelatedObject ( f . rel . to , cls , f ) . get_accessor_name ( )
2006-06-27 23:21:43 +08:00
rel_query_name = f . related_query_name ( )
2006-09-07 21:29:56 +08:00
# If rel_name is none, there is no reverse accessor.
2006-09-12 02:42:56 +08:00
# (This only occurs for symmetrical m2m relations to self).
2006-09-07 21:29:56 +08:00
# If this is the case, there are no clashes to check for this field, as
# there are no reverse descriptors for this field.
if rel_name is not None :
for r in rel_opts . fields :
if r . name == rel_name :
e . add ( opts , " Accessor for m2m field ' %s ' clashes with field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
if r . name == rel_query_name :
e . add ( opts , " Reverse query name for m2m field ' %s ' clashes with field ' %s . %s ' . 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 , " Accessor for m2m field ' %s ' clashes with m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
if r . name == rel_query_name :
e . add ( opts , " Reverse query name for m2m field ' %s ' clashes with m2m field ' %s . %s ' . 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 . field is not f :
if r . get_accessor_name ( ) == rel_name :
e . add ( opts , " Accessor for m2m field ' %s ' clashes with related m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
if r . get_accessor_name ( ) == rel_query_name :
e . add ( opts , " Reverse query name for m2m field ' %s ' clashes with related m2m field ' %s . %s ' . 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 ( ) :
2006-06-27 23:21:43 +08:00
if r . get_accessor_name ( ) == rel_name :
2006-09-07 21:29:56 +08:00
e . add ( opts , " Accessor for m2m field ' %s ' clashes with related field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
2006-06-27 23:21:43 +08:00
if r . get_accessor_name ( ) == rel_query_name :
2006-09-07 21:29:56 +08:00
e . add ( opts , " Reverse query name for m2m field ' %s ' clashes with related field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
2006-05-02 09:31:56 +08:00
# 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 :
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 )
2006-07-10 12:16:26 +08:00
# list_display_links
if opts . admin . list_display_links and not opts . admin . list_display :
e . add ( opts , ' " admin.list_display " must be defined for " admin.list_display_links " to be used. ' )
if not isinstance ( opts . admin . list_display_links , ( list , tuple ) ) :
e . add ( opts , ' " admin.list_display_links " , if given, must be set to a list or tuple. ' )
else :
for fn in opts . admin . list_display_links :
try :
f = opts . get_field ( fn )
except models . FieldDoesNotExist :
2006-09-22 20:22:32 +08:00
if not hasattr ( cls , fn ) :
e . add ( opts , ' " admin.list_display_links " refers to %r , which isn \' t an attribute, method or property. ' % fn )
2006-07-10 12:16:26 +08:00
if fn not in opts . admin . list_display :
e . add ( opts , ' " admin.list_display_links " refers to %r , which is not defined in " admin.list_display " . ' % fn )
2006-05-02 09:31:56 +08:00
# 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 )
2006-08-02 23:05:51 +08:00
# date_hierarchy
if opts . admin . date_hierarchy :
try :
f = opts . get_field ( opts . admin . date_hierarchy )
except models . FieldDoesNotExist :
e . add ( opts , ' " admin.date_hierarchy " refers to %r , which isn \' t a field. ' % opts . admin . date_hierarchy )
2006-05-02 09:31:56 +08:00
# 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
2006-06-07 10:47:53 +08:00
if ' . ' in field_name : continue # Skip ordering in the format 'table.field'.
2006-05-02 09:31:56 +08:00
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 )
2005-08-15 13:05:52 +08:00
2006-05-02 09:31:56 +08:00
# Check core=True, if needed.
for related in opts . get_followed_related_objects ( ) :
2006-07-04 18:48:07 +08:00
if not related . edit_inline :
continue
2006-05-02 09:31:56 +08:00
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 unique_together.
for ut in opts . unique_together :
for field_name in ut :
2005-08-15 13:05:52 +08:00
try :
2006-05-02 09:31:56 +08:00
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 )
2005-09-03 02:38:23 +08:00
return len ( e . errors )
2005-08-15 13:05:52 +08:00
2006-09-23 19:13:43 +08:00
def validate ( outfile = sys . stdout , silent_success = False ) :
2005-09-03 02:38:23 +08:00
" Validates all installed models. "
2006-05-02 09:31:56 +08:00
try :
num_errors = get_validation_errors ( outfile )
2006-09-23 19:13:43 +08:00
if silent_success and num_errors == 0 :
return
2006-05-02 09:31:56 +08:00
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. " )
2005-08-15 13:05:52 +08:00
validate . args = ' '
2006-05-02 09:31:56 +08:00
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 :
2006-07-05 13:11:20 +08:00
if app :
sys . stderr . write ( style . ERROR ( " Error: %s couldn ' t be installed, because there were errors in your model: \n " % app ) )
else :
sys . stderr . write ( style . ERROR ( " Error: Couldn ' t install apps, because there were errors in one or more models: \n " ) )
2006-05-02 09:31:56 +08:00
s . seek ( 0 )
sys . stderr . write ( s . read ( ) )
sys . exit ( 1 )
2006-09-24 16:17:47 +08:00
def runserver ( addr , port , use_reloader = True , admin_media_dir = ' ' ) :
2005-07-20 23:00:34 +08:00
" Starts a lightweight Web server for development. "
2005-08-12 12:16:29 +08:00
from django . core . servers . basehttp import run , AdminMediaHandler , WSGIServerException
from django . core . handlers . wsgi import WSGIHandler
2005-08-20 05:23:56 +08:00
if not addr :
addr = ' 127.0.0.1 '
2005-07-20 23:00:34 +08:00
if not port . isdigit ( ) :
2006-05-02 09:31:56 +08:00
sys . stderr . write ( style . ERROR ( " Error: %r is not a valid port number. \n " % port ) )
2005-07-20 23:00:34 +08:00
sys . exit ( 1 )
2006-05-06 11:29:22 +08:00
quit_command = sys . platform == ' win32 ' and ' CTRL-BREAK ' or ' CONTROL-C '
2005-07-21 12:08:54 +08:00
def inner_run ( ) :
2006-05-02 09:31:56 +08:00
from django . conf import settings
2005-08-15 23:24:56 +08:00
print " Validating models... "
validate ( )
2006-05-02 09:31:56 +08:00
print " \n Django version %s , using settings %r " % ( get_version ( ) , settings . SETTINGS_MODULE )
2006-02-19 07:48:11 +08:00
print " Development server is running at http:// %s : %s / " % ( addr , port )
2006-05-06 11:29:22 +08:00
print " Quit the server with %s . " % quit_command
2005-07-20 23:00:34 +08:00
try :
2006-09-24 16:17:47 +08:00
import django
path = admin_media_dir or django . __path__ [ 0 ] + ' /contrib/admin/media '
handler = AdminMediaHandler ( WSGIHandler ( ) , path )
run ( addr , int ( port ) , handler )
2005-07-21 12:08:54 +08:00
except WSGIServerException , e :
# Use helpful error messages instead of ugly tracebacks.
ERRORS = {
13 : " You don ' t have permission to access that port. " ,
98 : " That port is already in use. " ,
2005-08-20 05:23:56 +08:00
99 : " That IP address can ' t be assigned-to. " ,
2005-07-21 12:08:54 +08:00
}
try :
error_text = ERRORS [ e . args [ 0 ] . args [ 0 ] ]
except ( AttributeError , KeyError ) :
error_text = str ( e )
2006-05-02 09:31:56 +08:00
sys . stderr . write ( style . ERROR ( " Error: %s " % error_text ) + ' \n ' )
2005-07-21 12:08:54 +08:00
sys . exit ( 1 )
except KeyboardInterrupt :
sys . exit ( 0 )
2006-07-14 13:43:26 +08:00
if use_reloader :
from django . utils import autoreload
autoreload . main ( inner_run )
else :
inner_run ( )
2006-09-24 16:17:47 +08:00
runserver . args = ' [--noreload] [--adminmedia=ADMIN_MEDIA_PATH] [optional port number, or ipaddr:port] '
2005-09-26 06:03:30 +08:00
def createcachetable ( tablename ) :
" Creates the table needed to use the SQL cache backend "
2006-05-02 09:31:56 +08:00
from django . db import backend , connection , transaction , get_creation_module , models
data_types = get_creation_module ( ) . DATA_TYPES
2005-09-26 06:03:30 +08:00
fields = (
2005-09-26 11:14:37 +08:00
# "key" is a reserved word in MySQL, so use "cache_key" instead.
2006-05-02 09:31:56 +08:00
models . CharField ( name = ' cache_key ' , maxlength = 255 , unique = True , primary_key = True ) ,
models . TextField ( name = ' value ' ) ,
models . DateTimeField ( name = ' expires ' , db_index = True ) ,
2005-09-26 06:03:30 +08:00
)
table_output = [ ]
index_output = [ ]
for f in fields :
2006-05-02 09:31:56 +08:00
field_output = [ backend . quote_name ( f . name ) , data_types [ f . get_internal_type ( ) ] % f . __dict__ ]
2005-09-26 06:03:30 +08:00
field_output . append ( " %s NULL " % ( not f . null and " NOT " or " " ) )
if f . unique :
field_output . append ( " UNIQUE " )
if f . primary_key :
field_output . append ( " PRIMARY KEY " )
if f . db_index :
unique = f . unique and " UNIQUE " or " "
2005-11-14 09:44:35 +08:00
index_output . append ( " CREATE %s INDEX %s _ %s ON %s ( %s ); " % \
2006-05-02 09:31:56 +08:00
( unique , tablename , f . name , backend . quote_name ( tablename ) ,
backend . quote_name ( f . name ) ) )
2005-09-26 06:03:30 +08:00
table_output . append ( " " . join ( field_output ) )
2006-05-02 09:31:56 +08:00
full_statement = [ " CREATE TABLE %s ( " % backend . quote_name ( tablename ) ]
2005-09-26 06:03:30 +08:00
for i , line in enumerate ( table_output ) :
full_statement . append ( ' %s %s ' % ( line , i < len ( table_output ) - 1 and ' , ' or ' ' ) )
full_statement . append ( ' ); ' )
2006-05-02 09:31:56 +08:00
curs = connection . cursor ( )
2005-09-26 06:03:30 +08:00
curs . execute ( " \n " . join ( full_statement ) )
for statement in index_output :
curs . execute ( statement )
2006-05-02 09:31:56 +08:00
transaction . commit_unless_managed ( )
2005-09-30 07:43:03 +08:00
createcachetable . args = " [tablename] "
2005-12-06 13:17:52 +08:00
2006-01-13 04:56:10 +08:00
def run_shell ( use_plain = False ) :
" Runs a Python interactive interpreter. Tries to use IPython, if it ' s available. "
try :
if use_plain :
# Don't bother loading IPython, because the user wants plain Python.
raise ImportError
import IPython
2006-06-03 03:04:09 +08:00
# Explicitly pass an empty list as arguments, because otherwise IPython
# would use sys.argv from this script.
shell = IPython . Shell . IPShell ( argv = [ ] )
2006-01-13 04:56:10 +08:00
shell . mainloop ( )
except ImportError :
import code
2006-05-05 11:17:51 +08:00
try : # Try activating rlcompleter, because it's handy.
import readline
except ImportError :
pass
else :
# We don't have to wrap the following import in a 'try', because
# we already know 'readline' was imported successfully.
import rlcompleter
readline . parse_and_bind ( " tab:complete " )
2006-01-13 04:56:10 +08:00
code . interact ( )
run_shell . args = ' [--plain] '
2006-01-11 09:57:22 +08:00
2006-05-02 09:31:56 +08:00
def dbshell ( ) :
" Runs the command-line client for the current DATABASE_ENGINE. "
from django . db import runshell
runshell ( )
dbshell . args = " "
2006-06-20 13:24:19 +08:00
def runfcgi ( args ) :
2006-08-12 13:55:28 +08:00
" Runs this project as a FastCGI application. Requires flup. "
from django . conf import settings
from django . utils import translation
# Activate the current language, because it won't get activated later.
try :
translation . activate ( settings . LANGUAGE_CODE )
except AttributeError :
pass
2006-06-20 13:24:19 +08:00
from django . core . servers . fastcgi import runfastcgi
runfastcgi ( args )
runfcgi . args = ' [various KEY=val options, use `runfcgi help` for help] '
2006-10-03 17:53:12 +08:00
def test ( app_labels , verbosity = 1 ) :
2006-08-27 20:46:39 +08:00
" Runs the test suite for the specified applications "
from django . conf import settings
from django . db . models import get_app , get_apps
if len ( app_labels ) == 0 :
app_list = get_apps ( )
else :
app_list = [ get_app ( app_label ) for app_label in app_labels ]
2006-09-12 02:42:56 +08:00
2006-08-27 20:46:39 +08:00
test_path = settings . TEST_RUNNER . split ( ' . ' )
# Allow for Python 2.5 relative paths
if len ( test_path ) > 1 :
test_module_name = ' . ' . join ( test_path [ : - 1 ] )
else :
test_module_name = ' . '
test_module = __import__ ( test_module_name , [ ] , [ ] , test_path [ - 1 ] )
test_runner = getattr ( test_module , test_path [ - 1 ] )
2006-09-12 02:42:56 +08:00
2006-08-27 20:46:39 +08:00
test_runner ( app_list , verbosity )
test . help_doc = ' Runs the test suite for the specified applications, or the entire site if no apps are specified '
test . args = ' [--verbosity] ' + APP_ARGS
2005-12-06 13:17:52 +08:00
# Utilities for command-line script
2005-12-06 13:35:07 +08:00
DEFAULT_ACTION_MAPPING = {
2005-12-06 13:17:52 +08:00
' adminindex ' : get_admin_index ,
' createcachetable ' : createcachetable ,
2006-05-02 09:31:56 +08:00
' dbshell ' : dbshell ,
' diffsettings ' : diffsettings ,
2005-12-06 13:17:52 +08:00
' inspectdb ' : inspectdb ,
' install ' : install ,
2006-05-02 09:31:56 +08:00
' reset ' : reset ,
2006-06-20 13:24:19 +08:00
' runfcgi ' : runfcgi ,
2005-12-06 13:17:52 +08:00
' runserver ' : runserver ,
2006-01-11 09:57:22 +08:00
' shell ' : run_shell ,
2005-12-06 13:17:52 +08:00
' sql ' : get_sql_create ,
' sqlall ' : get_sql_all ,
' sqlclear ' : get_sql_delete ,
' sqlindexes ' : get_sql_indexes ,
' sqlinitialdata ' : get_sql_initial_data ,
' sqlreset ' : get_sql_reset ,
' sqlsequencereset ' : get_sql_sequence_reset ,
' startapp ' : startapp ,
' startproject ' : startproject ,
2006-05-02 09:31:56 +08:00
' syncdb ' : syncdb ,
2005-12-06 13:17:52 +08:00
' validate ' : validate ,
2006-08-27 20:46:39 +08:00
' test ' : test ,
2005-12-06 13:17:52 +08:00
}
2006-05-02 09:31:56 +08:00
NO_SQL_TRANSACTION = (
' adminindex ' ,
' createcachetable ' ,
' dbshell ' ,
' diffsettings ' ,
' install ' ,
' reset ' ,
' sqlindexes ' ,
' syncdb ' ,
)
2005-12-06 13:17:52 +08:00
class DjangoOptionParser ( OptionParser ) :
def print_usage_and_exit ( self ) :
self . print_help ( sys . stderr )
sys . exit ( 1 )
2005-12-06 13:35:07 +08:00
def get_usage ( action_mapping ) :
2005-12-06 13:17:52 +08:00
"""
Returns a usage string . Doesn ' t do the options stuff, because optparse
takes care of that .
"""
2006-05-02 09:31:56 +08:00
usage = [ " % prog action [options] \n actions: " ]
2005-12-06 13:35:07 +08:00
available_actions = action_mapping . keys ( )
2005-12-06 13:17:52 +08:00
available_actions . sort ( )
for a in available_actions :
2005-12-06 13:35:07 +08:00
func = action_mapping [ a ]
2005-12-06 13:17:52 +08:00
usage . append ( " %s %s " % ( a , func . args ) )
2006-05-02 09:31:56 +08:00
usage . extend ( textwrap . wrap ( getattr ( func , ' help_doc ' , textwrap . dedent ( func . __doc__ . strip ( ) ) ) , initial_indent = ' ' , subsequent_indent = ' ' ) )
2005-12-06 13:17:52 +08:00
usage . append ( " " )
return ' \n ' . join ( usage [ : - 1 ] ) # Cut off last list element, an empty space.
def print_error ( msg , cmd ) :
2006-05-02 09:31:56 +08:00
sys . stderr . write ( style . ERROR ( ' Error: %s ' % msg ) + ' \n Run " %s --help " for help. \n ' % cmd )
2005-12-06 13:17:52 +08:00
sys . exit ( 1 )
2006-06-05 23:20:47 +08:00
def execute_from_command_line ( action_mapping = DEFAULT_ACTION_MAPPING , argv = None ) :
# Use sys.argv if we've not passed in a custom argv
if argv is None :
argv = sys . argv
2005-12-06 13:17:52 +08:00
# Parse the command-line arguments. optparse handles the dirty work.
2006-02-18 02:33:09 +08:00
parser = DjangoOptionParser ( usage = get_usage ( action_mapping ) , version = get_version ( ) )
2005-12-06 13:17:52 +08:00
parser . add_option ( ' --settings ' ,
help = ' Python path to settings module, e.g. " myproject.settings.main " . If this isn \' t provided, the DJANGO_SETTINGS_MODULE environment variable will be used. ' )
parser . add_option ( ' --pythonpath ' ,
help = ' Lets you manually add a directory the Python path, e.g. " /home/djangoprojects/myproject " . ' )
2006-01-13 04:56:10 +08:00
parser . add_option ( ' --plain ' , action = ' store_true ' , dest = ' plain ' ,
help = ' Tells Django to use plain Python, not IPython, for " shell " command. ' )
2006-08-27 20:46:39 +08:00
parser . add_option ( ' --noinput ' , action = ' store_false ' , dest = ' interactive ' , default = True ,
help = ' Tells Django to NOT prompt the user for input of any kind. ' )
2006-07-14 13:43:26 +08:00
parser . add_option ( ' --noreload ' , action = ' store_false ' , dest = ' use_reloader ' , default = True ,
help = ' Tells Django to NOT use the auto-reloader when running the development server. ' )
2006-10-03 20:57:05 +08:00
parser . add_option ( ' --verbosity ' , action = ' store ' , dest = ' verbosity ' , default = ' 1 ' ,
2006-08-27 20:46:39 +08:00
type = ' choice ' , choices = [ ' 0 ' , ' 1 ' , ' 2 ' ] ,
2006-09-24 16:17:47 +08:00
help = ' Verbosity level; 0=minimal output, 1=normal output, 2=all output ' ) ,
2006-09-26 01:50:36 +08:00
parser . add_option ( ' --adminmedia ' , dest = ' admin_media_path ' , default = ' ' , help = ' Specifies the directory from which to serve admin media for runserver. ' ) ,
2006-08-27 20:46:39 +08:00
2006-06-05 23:25:12 +08:00
options , args = parser . parse_args ( argv [ 1 : ] )
2005-12-06 13:17:52 +08:00
# Take care of options.
if options . settings :
os . environ [ ' DJANGO_SETTINGS_MODULE ' ] = options . settings
if options . pythonpath :
sys . path . insert ( 0 , options . pythonpath )
# Run the appropriate action. Unfortunately, optparse can't handle
# positional arguments, so this has to parse/validate them.
try :
action = args [ 0 ]
except IndexError :
parser . print_usage_and_exit ( )
2005-12-06 13:35:07 +08:00
if not action_mapping . has_key ( action ) :
2006-06-05 23:20:47 +08:00
print_error ( " Your action, %r , was invalid. " % action , argv [ 0 ] )
2005-12-06 13:17:52 +08:00
2005-12-06 13:38:56 +08:00
# Switch to English, because django-admin.py creates database content
2005-12-06 13:17:52 +08:00
# like permissions, and those shouldn't contain any translations.
# But only do this if we should have a working settings file.
if action not in ( ' startproject ' , ' startapp ' ) :
from django . utils import translation
translation . activate ( ' en-us ' )
2006-05-02 09:31:56 +08:00
if action == ' shell ' :
2006-01-13 04:56:10 +08:00
action_mapping [ action ] ( options . plain is True )
2006-08-27 20:46:39 +08:00
elif action in ( ' validate ' , ' diffsettings ' , ' dbshell ' ) :
2005-12-06 13:35:07 +08:00
action_mapping [ action ] ( )
2006-08-27 20:46:39 +08:00
elif action == ' syncdb ' :
action_mapping [ action ] ( int ( options . verbosity ) , options . interactive )
2005-12-06 13:17:52 +08:00
elif action == ' inspectdb ' :
try :
2006-05-02 09:31:56 +08:00
for line in action_mapping [ action ] ( ) :
2005-12-06 13:17:52 +08:00
print line
except NotImplementedError :
2006-05-02 09:31:56 +08:00
sys . stderr . write ( style . ERROR ( " Error: %r isn ' t supported for the currently selected database backend. \n " % action ) )
2005-12-06 13:17:52 +08:00
sys . exit ( 1 )
elif action == ' createcachetable ' :
try :
2005-12-06 13:35:07 +08:00
action_mapping [ action ] ( args [ 1 ] )
2005-12-06 13:17:52 +08:00
except IndexError :
parser . print_usage_and_exit ( )
2006-08-27 20:46:39 +08:00
elif action == ' test ' :
try :
2006-10-03 17:53:12 +08:00
action_mapping [ action ] ( args [ 1 : ] , int ( options . verbosity ) )
2006-08-27 20:46:39 +08:00
except IndexError :
parser . print_usage_and_exit ( )
2005-12-06 13:17:52 +08:00
elif action in ( ' startapp ' , ' startproject ' ) :
try :
name = args [ 1 ]
except IndexError :
parser . print_usage_and_exit ( )
2005-12-06 13:35:07 +08:00
action_mapping [ action ] ( name , os . getcwd ( ) )
2005-12-06 13:17:52 +08:00
elif action == ' runserver ' :
if len ( args ) < 2 :
addr = ' '
port = ' 8000 '
else :
try :
addr , port = args [ 1 ] . split ( ' : ' )
except ValueError :
addr , port = ' ' , args [ 1 ]
2006-09-24 16:17:47 +08:00
action_mapping [ action ] ( addr , port , options . use_reloader , options . admin_media_path )
2006-06-20 13:24:19 +08:00
elif action == ' runfcgi ' :
action_mapping [ action ] ( args [ 1 : ] )
2005-12-06 13:17:52 +08:00
else :
2006-05-02 09:31:56 +08:00
from django . db import models
2006-09-23 19:13:43 +08:00
validate ( silent_success = True )
2006-05-02 09:31:56 +08:00
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 ( )
2005-12-06 13:17:52 +08:00
if action not in NO_SQL_TRANSACTION :
2006-05-02 09:31:56 +08:00
print style . SQL_KEYWORD ( " BEGIN; " )
2005-12-06 13:17:52 +08:00
for mod in mod_list :
2006-10-03 18:01:50 +08:00
if action == ' reset ' :
output = action_mapping [ action ] ( mod , options . interactive )
else :
output = action_mapping [ action ] ( mod )
2005-12-06 13:17:52 +08:00
if output :
print ' \n ' . join ( output )
if action not in NO_SQL_TRANSACTION :
2006-05-02 09:31:56 +08:00
print style . SQL_KEYWORD ( " COMMIT; " )
2005-12-06 13:53:31 +08:00
2006-08-06 10:29:38 +08:00
def setup_environ ( settings_mod ) :
"""
Configure the runtime environment . This can also be used by external
scripts wanting to set up a similar environment to manage . py .
"""
2005-12-06 13:53:31 +08:00
# Add this project to sys.path so that it's importable in the conventional
# way. For example, if this file (manage.py) lives in a directory
# "myproject", this code would add "/path/to/myproject" to sys.path.
project_directory = os . path . dirname ( settings_mod . __file__ )
project_name = os . path . basename ( project_directory )
sys . path . append ( os . path . join ( project_directory , ' .. ' ) )
project_module = __import__ ( project_name , ' ' , ' ' , [ ' ' ] )
sys . path . pop ( )
# Set DJANGO_SETTINGS_MODULE appropriately.
os . environ [ ' DJANGO_SETTINGS_MODULE ' ] = ' %s .settings ' % project_name
2006-08-06 18:35:25 +08:00
return project_directory
2005-12-06 13:53:31 +08:00
2006-08-06 10:29:38 +08:00
def execute_manager ( settings_mod , argv = None ) :
2006-08-06 18:35:25 +08:00
project_directory = setup_environ ( settings_mod )
2006-05-02 09:31:56 +08:00
action_mapping = DEFAULT_ACTION_MAPPING . copy ( )
2005-12-06 13:53:31 +08:00
# Remove the "startproject" command from the action_mapping, because that's
# a django-admin.py command, not a manage.py command.
del action_mapping [ ' startproject ' ]
2006-05-02 09:31:56 +08:00
# 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
2005-12-06 13:53:31 +08:00
# Run the django-admin.py command.
2006-06-05 23:20:47 +08:00
execute_from_command_line ( action_mapping , argv )