importdatetimeimportstringimportrandomimportreimportsysfromdjango.core.management.colorimportno_stylefromdjango.dbimporttransaction,modelsfromdjango.db.utilsimportDatabaseErrorfromdjango.db.backends.utilimporttruncate_namefromdjango.db.models.fieldsimportNOT_PROVIDEDfromdjango.dispatchimportdispatcherfromdjango.confimportsettingsfromdjango.utils.datastructuresimportSortedDictfromsouth.loggerimportget_loggerdefalias(attrname):""" Returns a function which calls 'attrname' - for function aliasing. We can't just use foo = bar, as this breaks subclassing. """deffunc(self,*args,**kwds):returngetattr(self,attrname)(*args,**kwds)returnfuncdefinvalidate_table_constraints(func):def_cache_clear(self,table,*args,**opts):self._set_cache(table,value=INVALID)returnfunc(self,table,*args,**opts)return_cache_cleardefdelete_column_constraints(func):def_column_rm(self,table,column,*args,**opts):self._set_cache(table,column,value=[])returnfunc(self,table,column,*args,**opts)return_column_rmdefcopy_column_constraints(func):def_column_cp(self,table,column_old,column_new,*args,**opts):db_name=self._get_setting('NAME')self._set_cache(table,column_new,value=self.lookup_constraint(db_name,table,column_old))returnfunc(self,table,column_old,column_new,*args,**opts)return_column_cpclassINVALID(Exception):def__repr__(self):return'INVALID'classDryRunError(ValueError):passclassDatabaseOperations(object):""" Generic SQL implementation of the DatabaseOperations. Some of this code comes from Django Evolution. """# We assume the generic DB can handle DDL transactions. MySQL wil change this.has_ddl_transactions=Truealter_string_set_type='ALTER COLUMN %(column)s TYPE %(type)s'alter_string_set_null='ALTER COLUMN %(column)s DROP NOT NULL'alter_string_drop_null='ALTER COLUMN %(column)s SET NOT NULL'has_check_constraints=Truedelete_check_sql='ALTER TABLE %(table)s DROP CONSTRAINT %(constraint)s'allows_combined_alters=Trueadd_column_string='ALTER TABLE %s ADD COLUMN %s;'delete_unique_sql="ALTER TABLE %s DROP CONSTRAINT %s"delete_foreign_key_sql='ALTER TABLE %(table)s DROP CONSTRAINT %(constraint)s'supports_foreign_keys=Truemax_index_name_length=63drop_index_string='DROP INDEX %(index_name)s'delete_column_string='ALTER TABLE %s DROP COLUMN %s CASCADE;'create_primary_key_string="ALTER TABLE %(table)s ADD CONSTRAINT %(constraint)s PRIMARY KEY (%(columns)s)"delete_primary_key_sql="ALTER TABLE %(table)s DROP CONSTRAINT %(constraint)s"add_check_constraint_fragment="ADD CONSTRAINT %(constraint)s CHECK (%(check)s)"rename_table_sql="ALTER TABLE %s RENAME TO %s;"backend_name=Nonedefault_schema_name="public"def__init__(self,db_alias):self.debug=Falseself.deferred_sql=[]self.dry_run=Falseself.pending_transactions=0self.pending_create_signals=[]self.db_alias=db_aliasself._constraint_cache={}self._initialised=Falsedeflookup_constraint(self,db_name,table_name,column_name=None):""" return a set() of constraints for db_name.table_name.column_name """def_lookup():table=self._constraint_cache[db_name][table_name]iftableisINVALID:raiseINVALIDelifcolumn_nameisNone:returntable.items()else:returntable[column_name]try:ret=_lookup()returnretexceptINVALID,e:delself._constraint_cache[db_name][table_name]self._fill_constraint_cache(db_name,table_name)exceptKeyError,e:ifself._is_valid_cache(db_name,table_name):return[]self._fill_constraint_cache(db_name,table_name)returnself.lookup_constraint(db_name,table_name,column_name)def_set_cache(self,table_name,column_name=None,value=INVALID):db_name=self._get_setting('NAME')try:ifcolumn_nameisnotNone:self._constraint_cache[db_name][table_name][column_name]=valueelse:self._constraint_cache[db_name][table_name]=valueexcept(LookupError,TypeError):passdef_is_valid_cache(self,db_name,table_name):# we cache per-table so if the table is there it is validtry:returnself._constraint_cache[db_name][table_name]isnotINVALIDexceptKeyError:returnFalsedef_is_multidb(self):try:fromdjango.dbimportconnectionsexceptImportError:returnFalseelse:returnTruedef_get_connection(self):""" Returns a django connection for a given DB Alias """ifself._is_multidb():fromdjango.dbimportconnectionsreturnconnections[self.db_alias]else:fromdjango.dbimportconnectionreturnconnectiondef_get_setting(self,setting_name):""" Allows code to get a setting (like, for example, STORAGE_ENGINE) """setting_name=setting_name.upper()connection=self._get_connection()ifself._is_multidb():# Django 1.2 and abovereturnconnection.settings_dict[setting_name]else:# Django 1.1 and belowreturngetattr(settings,"DATABASE_%s"%setting_name)def_has_setting(self,setting_name):""" Existence-checking version of _get_setting. """try:self._get_setting(setting_name)except(KeyError,AttributeError):returnFalseelse:returnTruedef_get_schema_name(self):try:returnself._get_setting('schema')except(KeyError,AttributeError):returnself.default_schema_namedef_possibly_initialise(self):ifnotself._initialised:self.connection_init()self._initialised=Truedefconnection_init(self):""" Run before any SQL to let database-specific config be sent as a command, e.g. which storage engine (MySQL) or transaction serialisability level. """passdefquote_name(self,name):""" Uses the database backend to quote the given table/column name. """returnself._get_connection().ops.quote_name(name)defexecute(self,sql,params=[]):""" Executes the given SQL statement, with optional parameters. If the instance's debug attribute is True, prints out what it executes. """self._possibly_initialise()cursor=self._get_connection().cursor()ifself.debug:print" = %s"%sql,paramsget_logger().debug('south execute "%s" with params "%s"'%(sql,params))ifself.dry_run:return[]try:cursor.execute(sql,params)exceptDatabaseError,e:print>>sys.stderr,'FATAL ERROR - The following SQL query failed: %s'%sqlprint>>sys.stderr,'The error was: %s'%esys.exit(1)try:returncursor.fetchall()except:return[]defexecute_many(self,sql,regex=r"(?mx) ([^';]* (?:'[^']*'[^';]*)*)",comment_regex=r"(?mx) (?:^\s*$)|(?:--.*$)"):""" Takes a SQL file and executes it as many separate statements. (Some backends, such as Postgres, don't work otherwise.) """# Be warned: This function is full of dark magic. Make sure you really# know regexes before trying to edit it.# First, strip commentssql="\n".join([x.strip().replace("%","%%")forxinre.split(comment_regex,sql)ifx.strip()])# Now execute each statementforstinre.split(regex,sql)[1:][::2]:self.execute(st)defadd_deferred_sql(self,sql):""" Add a SQL statement to the deferred list, that won't be executed until this instance's execute_deferred_sql method is run. """self.deferred_sql.append(sql)defexecute_deferred_sql(self):""" Executes all deferred SQL, resetting the deferred_sql list """forsqlinself.deferred_sql:self.execute(sql)self.deferred_sql=[]defclear_deferred_sql(self):""" Resets the deferred_sql list to empty. """self.deferred_sql=[]defclear_run_data(self,pending_creates=None):""" Resets variables to how they should be before a run. Used for dry runs. If you want, pass in an old panding_creates to reset to. """self.clear_deferred_sql()self.pending_create_signals=pending_createsor[]defget_pending_creates(self):returnself.pending_create_signals@invalidate_table_constraintsdefcreate_table(self,table_name,fields):""" Creates the table 'table_name'. 'fields' is a tuple of fields, each repsented by a 2-part tuple of field name and a django.db.models.fields.Field object """iflen(table_name)>63:print" ! WARNING: You have a table name longer than 63 characters; this will not fully work on PostgreSQL or MySQL."columns=[self.column_sql(table_name,field_name,field)forfield_name,fieldinfields]self.execute('CREATE TABLE %s (%s);'%(self.quote_name(table_name),', '.join([colforcolincolumnsifcol]),))add_table=alias('create_table')# Alias for consistency's sake@invalidate_table_constraintsdefrename_table(self,old_table_name,table_name):""" Renames the table 'old_table_name' to 'table_name'. """ifold_table_name==table_name:# Short-circuit out.returnparams=(self.quote_name(old_table_name),self.quote_name(table_name))self.execute(self.rename_table_sql%params)# Invalidate the not-yet-indexed tableself._set_cache(table_name,value=INVALID)@invalidate_table_constraintsdefdelete_table(self,table_name,cascade=True):""" Deletes the table 'table_name'. """params=(self.quote_name(table_name),)ifcascade:self.execute('DROP TABLE %s CASCADE;'%params)else:self.execute('DROP TABLE %s;'%params)drop_table=alias('delete_table')@invalidate_table_constraintsdefclear_table(self,table_name):""" Deletes all rows from 'table_name'. """params=(self.quote_name(table_name),)self.execute('DELETE FROM %s;'%params)@invalidate_table_constraintsdefadd_column(self,table_name,name,field,keep_default=True):""" Adds the column 'name' to the table 'table_name'. Uses the 'field' paramater, a django.db.models.fields.Field instance, to generate the necessary sql @param table_name: The name of the table to add the column to @param name: The name of the column to add @param field: The field to use """sql=self.column_sql(table_name,name,field)ifsql:params=(self.quote_name(table_name),sql,)sql=self.add_column_string%paramsself.execute(sql)# Now, drop the default if we need toifnotkeep_defaultandfield.defaultisnotNone:field.default=NOT_PROVIDEDself.alter_column(table_name,name,field,explicit_name=False,ignore_constraints=True)def_db_type_for_alter_column(self,field):""" Returns a field's type suitable for ALTER COLUMN. By default it just returns field.db_type(). To be overriden by backend specific subclasses @param field: The field to generate type for """try:returnfield.db_type(connection=self._get_connection())exceptTypeError:returnfield.db_type()def_alter_add_column_mods(self,field,name,params,sqls):""" Subcommand of alter_column that modifies column definitions beyond the type string -- e.g. adding constraints where they cannot be specified as part of the type (overrideable) """passdef_alter_set_defaults(self,field,name,params,sqls):"Subcommand of alter_column that sets default values (overrideable)"# Next, set any defaultifnotfield.nullandfield.has_default():default=field.get_default()sqls.append(('ALTER COLUMN %s SET DEFAULT %%s '%(self.quote_name(name),),[default]))else:sqls.append(('ALTER COLUMN %s DROP DEFAULT'%(self.quote_name(name),),[]))@invalidate_table_constraintsdefalter_column(self,table_name,name,field,explicit_name=True,ignore_constraints=False):""" Alters the given column name so it will match the given field. Note that conversion between the two by the database must be possible. Will not automatically add _id by default; to have this behavour, pass explicit_name=False. @param table_name: The name of the table to add the column to @param name: The name of the column to alter @param field: The new field definition to use """ifself.dry_run:ifself.debug:print' - no dry run output for alter_column() due to dynamic DDL, sorry'return# hook for the field to do any resolution prior to it's attributes being queriedifhasattr(field,'south_init'):field.south_init()# Add _id or whatever if we need tofield.set_attributes_from_name(name)ifnotexplicit_name:name=field.columnelse:field.column=nameifnotignore_constraints:# Drop all check constraints. Note that constraints will be added back# with self.alter_string_set_type and self.alter_string_drop_null.ifself.has_check_constraints:check_constraints=self._constraints_affecting_columns(table_name,[name],"CHECK")forconstraintincheck_constraints:self.execute(self.delete_check_sql%{'table':self.quote_name(table_name),'constraint':self.quote_name(constraint),})# Drop or add UNIQUE constraintunique_constraint=list(self._constraints_affecting_columns(table_name,[name],"UNIQUE"))iffield.uniqueandnotunique_constraint:self.create_unique(table_name,[name])elifnotfield.uniqueandunique_constraint:self.delete_unique(table_name,[name])# Drop all foreign key constraintstry:self.delete_foreign_key(table_name,name)exceptValueError:# There weren't anypass# First, change the typeparams={"column":self.quote_name(name),"type":self._db_type_for_alter_column(field),"table_name":table_name}# SQLs is a list of (SQL, values) pairs.sqls=[]# Only alter the column if it has a type (Geometry ones sometimes don't)ifparams["type"]isnotNone:sqls.append((self.alter_string_set_type%params,[]))# Add any field- and backend- specific modificationsself._alter_add_column_mods(field,name,params,sqls)# Next, nullityiffield.null:sqls.append((self.alter_string_set_null%params,[]))else:sqls.append((self.alter_string_drop_null%params,[]))# Next, set any defaultself._alter_set_defaults(field,name,params,sqls)# Finally, actually change the columnifself.allows_combined_alters:sqls,values=zip(*sqls)self.execute("ALTER TABLE %s%s;"%(self.quote_name(table_name),", ".join(sqls)),flatten(values),)else:# Databases like e.g. MySQL don't like more than one alter at once.forsql,valuesinsqls:self.execute("ALTER TABLE %s%s;"%(self.quote_name(table_name),sql),values)ifnotignore_constraints:# Add back FK constraints if needediffield.relandself.supports_foreign_keys:self.execute(self.foreign_key_sql(table_name,field.column,field.rel.to._meta.db_table,field.rel.to._meta.get_field(field.rel.field_name).column))def_fill_constraint_cache(self,db_name,table_name):schema=self._get_schema_name()ifsc_tables=["constraint_column_usage","key_column_usage"]self._constraint_cache.setdefault(db_name,{})self._constraint_cache[db_name][table_name]={}forifsc_tableinifsc_tables:rows=self.execute(""" SELECT kc.constraint_name, kc.column_name, c.constraint_type FROM information_schema.%s AS kc JOIN information_schema.table_constraints AS c ON kc.table_schema = c.table_schema AND kc.table_name = c.table_name AND kc.constraint_name = c.constraint_name WHERE kc.table_schema = %%s AND kc.table_name = %%s """%ifsc_table,[schema,table_name])forconstraint,column,kindinrows:self._constraint_cache[db_name][table_name].setdefault(column,set())self._constraint_cache[db_name][table_name][column].add((kind,constraint))returndef_constraints_affecting_columns(self,table_name,columns,type="UNIQUE"):""" Gets the names of the constraints affecting the given columns. If columns is None, returns all constraints of the type on the table. """ifself.dry_run:raiseDryRunError("Cannot get constraints for columns.")ifcolumnsisnotNone:columns=set(map(lambdas:s.lower(),columns))db_name=self._get_setting('NAME')cnames={}forcol,constraintsinself.lookup_constraint(db_name,table_name):forkind,cnameinconstraints:ifkind==type:cnames.setdefault(cname,set())cnames[cname].add(col.lower())forcname,colsincnames.items():ifcols==columnsorcolumnsisNone:yieldcname@invalidate_table_constraintsdefcreate_unique(self,table_name,columns):""" Creates a UNIQUE constraint on the columns on the given table. """ifnotisinstance(columns,(list,tuple)):columns=[columns]name=self.create_index_name(table_name,columns,suffix="_uniq")cols=", ".join(map(self.quote_name,columns))self.execute("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE (%s)"%(self.quote_name(table_name),self.quote_name(name),cols,))returnname@invalidate_table_constraintsdefdelete_unique(self,table_name,columns):""" Deletes a UNIQUE constraint on precisely the columns on the given table. """ifnotisinstance(columns,(list,tuple)):columns=[columns]# Dry runs mean we can't do anything.ifself.dry_run:ifself.debug:print' - no dry run output for delete_unique_column() due to dynamic DDL, sorry'returnconstraints=list(self._constraints_affecting_columns(table_name,columns))ifnotconstraints:raiseValueError("Cannot find a UNIQUE constraint on table %s, columns %r"%(table_name,columns))forconstraintinconstraints:self.execute(self.delete_unique_sql%(self.quote_name(table_name),self.quote_name(constraint),))defcolumn_sql(self,table_name,field_name,field,tablespace='',with_name=True,field_prepared=False):""" Creates the SQL snippet for a column. Used by add_column and add_table. """# If the field hasn't already been told its attribute name, do so.ifnotfield_prepared:field.set_attributes_from_name(field_name)# hook for the field to do any resolution prior to it's attributes being queriedifhasattr(field,'south_init'):field.south_init()# Possible hook to fiddle with the fields (e.g. defaults & TEXT on MySQL)field=self._field_sanity(field)try:sql=field.db_type(connection=self._get_connection())exceptTypeError:sql=field.db_type()ifsql:# Some callers, like the sqlite stuff, just want the extended type.ifwith_name:field_output=[self.quote_name(field.column),sql]else:field_output=[sql]field_output.append('%sNULL'%(notfield.nulland'NOT 'or''))iffield.primary_key:field_output.append('PRIMARY KEY')eliffield.unique:# Just use UNIQUE (no indexes any more, we have delete_unique)field_output.append('UNIQUE')tablespace=field.db_tablespaceortablespaceiftablespaceandgetattr(self._get_connection().features,"supports_tablespaces",False)andfield.unique:# We must specify the index tablespace inline, because we# won't be generating a CREATE INDEX statement for this field.field_output.append(self._get_connection().ops.tablespace_sql(tablespace,inline=True))sql=' '.join(field_output)sqlparams=()# if the field is "NOT NULL" and a default value is provided, create the column with it# this allows the addition of a NOT NULL field to a table with existing rowsifnotgetattr(field,'_suppress_default',False):iffield.has_default():default=field.get_default()# If the default is actually None, don't add a default termifdefaultisnotNone:# If the default is a callable, then call it!ifcallable(default):default=default()default=field.get_db_prep_save(default,connection=self._get_connection())# Now do some very cheap quoting. TODO: Redesign return values to avoid this.ifisinstance(default,basestring):default="'%s'"%default.replace("'","''")# Escape any % signs in the output (bug #317)ifisinstance(default,basestring):default=default.replace("%","%%")# Add it insql+=" DEFAULT %s"sqlparams=(default)elif(notfield.nullandfield.blank)or(field.get_default()==''):iffield.empty_strings_allowedandself._get_connection().features.interprets_empty_strings_as_nulls:sql+=" DEFAULT ''"# Error here would be nice, but doesn't seem to play fair.#else:# raise ValueError("Attempting to add a non null column that isn't character based without an explicit default value.")iffield.relandself.supports_foreign_keys:self.add_deferred_sql(self.foreign_key_sql(table_name,field.column,field.rel.to._meta.db_table,field.rel.to._meta.get_field(field.rel.field_name).column))# Things like the contrib.gis module fields have this in 1.1 and belowifhasattr(field,'post_create_sql'):forstmtinfield.post_create_sql(no_style(),table_name):self.add_deferred_sql(stmt)# In 1.2 and above, you have to ask the DatabaseCreation stuff for it.# This also creates normal indexes in 1.1.ifhasattr(self._get_connection().creation,"sql_indexes_for_field"):# Make a fake model to pass in, with only db_tablemodel=self.mock_model("FakeModelForGISCreation",table_name)forstmtinself._get_connection().creation.sql_indexes_for_field(model,field,no_style()):self.add_deferred_sql(stmt)ifsql:returnsql%sqlparamselse:returnNonedef_field_sanity(self,field):""" Placeholder for DBMS-specific field alterations (some combos aren't valid, e.g. DEFAULT and TEXT on MySQL) """returnfielddefforeign_key_sql(self,from_table_name,from_column_name,to_table_name,to_column_name):""" Generates a full SQL statement to add a foreign key constraint """constraint_name='%s_refs_%s_%x'%(from_column_name,to_column_name,abs(hash((from_table_name,to_table_name))))return'ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;'%(self.quote_name(from_table_name),self.quote_name(truncate_name(constraint_name,self._get_connection().ops.max_name_length())),self.quote_name(from_column_name),self.quote_name(to_table_name),self.quote_name(to_column_name),self._get_connection().ops.deferrable_sql()# Django knows this)@invalidate_table_constraintsdefdelete_foreign_key(self,table_name,column):"Drop a foreign key constraint"ifself.dry_run:ifself.debug:print' - no dry run output for delete_foreign_key() due to dynamic DDL, sorry'return# We can't look at the DB to get the constraintsconstraints=self._find_foreign_constraints(table_name,column)ifnotconstraints:raiseValueError("Cannot find a FOREIGN KEY constraint on table %s, column %s"%(table_name,column))forconstraint_nameinconstraints:self.execute(self.delete_foreign_key_sql%{"table":self.quote_name(table_name),"constraint":self.quote_name(constraint_name),})drop_foreign_key=alias('delete_foreign_key')def_find_foreign_constraints(self,table_name,column_name=None):returnlist(self._constraints_affecting_columns(table_name,[column_name],"FOREIGN KEY"))defcreate_index_name(self,table_name,column_names,suffix=""):""" Generate a unique name for the index """table_name=table_name.replace('"','').replace('.','_')index_unique_name=''iflen(column_names)>1:index_unique_name='_%x'%abs(hash((table_name,','.join(column_names))))# If the index name is too long, truncate itindex_name=('%s_%s%s%s'%(table_name,column_names[0],index_unique_name,suffix)).replace('"','').replace('.','_')iflen(index_name)>self.max_index_name_length:part=('_%s%s%s'%(column_names[0],index_unique_name,suffix))index_name='%s%s'%(table_name[:(self.max_index_name_length-len(part))],part)returnindex_namedefcreate_index_sql(self,table_name,column_names,unique=False,db_tablespace=''):""" Generates a create index statement on 'table_name' for a list of 'column_names' """ifnotcolumn_names:print"No column names supplied on which to create an index"return''connection=self._get_connection()ifdb_tablespaceandconnection.features.supports_tablespaces:tablespace_sql=' '+connection.ops.tablespace_sql(db_tablespace)else:tablespace_sql=''index_name=self.create_index_name(table_name,column_names)return'CREATE %sINDEX %s ON %s (%s)%s;'%(uniqueand'UNIQUE 'or'',self.quote_name(index_name),self.quote_name(table_name),','.join([self.quote_name(field)forfieldincolumn_names]),tablespace_sql)@invalidate_table_constraintsdefcreate_index(self,table_name,column_names,unique=False,db_tablespace=''):""" Executes a create index statement """sql=self.create_index_sql(table_name,column_names,unique,db_tablespace)self.execute(sql)@invalidate_table_constraintsdefdelete_index(self,table_name,column_names,db_tablespace=''):""" Deletes an index created with create_index. This is possible using only columns due to the deterministic index naming function which relies on column names. """ifisinstance(column_names,(str,unicode)):column_names=[column_names]name=self.create_index_name(table_name,column_names)sql=self.drop_index_string%{"index_name":self.quote_name(name),"table_name":self.quote_name(table_name),}self.execute(sql)drop_index=alias('delete_index')@delete_column_constraintsdefdelete_column(self,table_name,name):""" Deletes the column 'column_name' from the table 'table_name'. """db_name=self._get_setting('NAME')params=(self.quote_name(table_name),self.quote_name(name))self.execute(self.delete_column_string%params,[])drop_column=alias('delete_column')defrename_column(self,table_name,old,new):""" Renames the column 'old' from the table 'table_name' to 'new'. """raiseNotImplementedError("rename_column has no generic SQL syntax")@invalidate_table_constraintsdefdelete_primary_key(self,table_name):""" Drops the old primary key. """# Dry runs mean we can't do anything.ifself.dry_run:ifself.debug:print' - no dry run output for delete_primary_key() due to dynamic DDL, sorry'returnconstraints=list(self._constraints_affecting_columns(table_name,None,type="PRIMARY KEY"))ifnotconstraints:raiseValueError("Cannot find a PRIMARY KEY constraint on table %s"%(table_name,))forconstraintinconstraints:self.execute(self.delete_primary_key_sql%{"table":self.quote_name(table_name),"constraint":self.quote_name(constraint),})drop_primary_key=alias('delete_primary_key')@invalidate_table_constraintsdefcreate_primary_key(self,table_name,columns):""" Creates a new primary key on the specified columns. """ifnotisinstance(columns,(list,tuple)):columns=[columns]self.execute(self.create_primary_key_string%{"table":self.quote_name(table_name),"constraint":self.quote_name(table_name+"_pkey"),"columns":", ".join(map(self.quote_name,columns)),})defstart_transaction(self):""" Makes sure the following commands are inside a transaction. Must be followed by a (commit|rollback)_transaction call. """ifself.dry_run:self.pending_transactions+=1transaction.commit_unless_managed()transaction.enter_transaction_management()transaction.managed(True)defcommit_transaction(self):""" Commits the current transaction. Must be preceded by a start_transaction call. """ifself.dry_run:returntransaction.commit()transaction.leave_transaction_management()defrollback_transaction(self):""" Rolls back the current transaction. Must be preceded by a start_transaction call. """ifself.dry_run:self.pending_transactions-=1transaction.rollback()transaction.leave_transaction_management()defrollback_transactions_dry_run(self):""" Rolls back all pending_transactions during this dry run. """ifnotself.dry_run:returnwhileself.pending_transactions>0:self.rollback_transaction()iftransaction.is_dirty():# Force an exception, if we're still in a dirty transaction.# This means we are missing a COMMIT/ROLLBACK.transaction.leave_transaction_management()defsend_create_signal(self,app_label,model_names):self.pending_create_signals.append((app_label,model_names))defsend_pending_create_signals(self,verbosity=0,interactive=False):# Group app_labels togethersignals=SortedDict()for(app_label,model_names)inself.pending_create_signals:try:signals[app_label].extend(model_names)exceptKeyError:signals[app_label]=list(model_names)# Send only one signal per app.for(app_label,model_names)insignals.iteritems():self.really_send_create_signal(app_label,list(set(model_names)),verbosity=verbosity,interactive=interactive)self.pending_create_signals=[]defreally_send_create_signal(self,app_label,model_names,verbosity=0,interactive=False):""" Sends a post_syncdb signal for the model specified. If the model is not found (perhaps it's been deleted?), no signal is sent. TODO: The behavior of django.contrib.* apps seems flawed in that they don't respect created_models. Rather, they blindly execute over all models within the app sending the signal. This is a patch we should push Django to make For now, this should work. """ifself.debug:print" - Sending post_syncdb signal for %s: %s"%(app_label,model_names)app=models.get_app(app_label)ifnotapp:returncreated_models=[]formodel_nameinmodel_names:model=models.get_model(app_label,model_name)ifmodel:created_models.append(model)ifcreated_models:ifhasattr(dispatcher,"send"):# Older djangosdispatcher.send(signal=models.signals.post_syncdb,sender=app,app=app,created_models=created_models,verbosity=verbosity,interactive=interactive)else:ifself._is_multidb():# Django 1.2+models.signals.post_syncdb.send(sender=app,app=app,created_models=created_models,verbosity=verbosity,interactive=interactive,db=self.db_alias,)else:# Django 1.1 - 1.0models.signals.post_syncdb.send(sender=app,app=app,created_models=created_models,verbosity=verbosity,interactive=interactive,)defmock_model(self,model_name,db_table,db_tablespace='',pk_field_name='id',pk_field_type=models.AutoField,pk_field_args=[],pk_field_kwargs={}):""" Generates a MockModel class that provides enough information to be used by a foreign key/many-to-many relationship. Migrations should prefer to use these rather than actual models as models could get deleted over time, but these can remain in migration files forever. Depreciated. """classMockOptions(object):def__init__(self):self.db_table=db_tableself.db_tablespace=db_tablespaceorsettings.DEFAULT_TABLESPACEself.object_name=model_nameself.module_name=model_name.lower()ifpk_field_type==models.AutoField:pk_field_kwargs['primary_key']=Trueself.pk=pk_field_type(*pk_field_args,**pk_field_kwargs)self.pk.set_attributes_from_name(pk_field_name)self.abstract=Falsedefget_field_by_name(self,field_name):# we only care about the pk fieldreturn(self.pk,self.model,True,False)defget_field(self,name):# we only care about the pk fieldreturnself.pkclassMockModel(object):_meta=None# We need to return an actual class object here, not an instanceMockModel._meta=MockOptions()MockModel._meta.model=MockModelreturnMockModeldef_db_positive_type_for_alter_column(self,field):""" A helper for subclasses overriding _db_type_for_alter_column: Remove the check constraint from the type string for PositiveInteger and PositiveSmallInteger fields. @param field: The field to generate type for """super_result=super(type(self),self)._db_type_for_alter_column(field)ifisinstance(field,(models.PositiveSmallIntegerField,models.PositiveIntegerField)):returnsuper_result.split(" ",1)[0]returnsuper_resultdef_alter_add_positive_check(self,field,name,params,sqls):""" A helper for subclasses overriding _alter_add_column_mods: Add a check constraint verifying positivity to PositiveInteger and PositiveSmallInteger fields. """super(type(self),self)._alter_add_column_mods(field,name,params,sqls)ifisinstance(field,(models.PositiveSmallIntegerField,models.PositiveIntegerField)):uniq_hash=abs(hash(tuple(params.values())))d=dict(constraint="CK_%s_PSTV_%s"%(name,hex(uniq_hash)[2:]),check="%s >= 0"%self.quote_name(name))sqls.append((self.add_check_constraint_fragment%d,[]))# Single-level flattening of listsdefflatten(ls):nl=[]forlinls:nl+=lreturnnl