Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kylemacfarlane/841659 to your computer and use it in GitHub Desktop.
Save kylemacfarlane/841659 to your computer and use it in GitHub Desktop.
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
# This can get stupid. When running all migrations from scratch such as
# during tests then dependant apps will need to be initialised before this
# migration otherwise there will be a data type mismatch. But all those apps
# will need to depend on 0001_inital from this app (or put needed by in
# there).
depends_on = (
('downloads', '0001_initial'),
)
def get_fks(self, table, column, db_name=None):
if db_name is None:
# We need the database name or things go crazy in tests or other
# cases where the database user might have access to more than one
# database.
if db.backend_name == 'mysql':
db_name = db.execute('SELECT database()')[0][0]
elif db.backend_name == 'postgres':
db_name = db.execute('SELECT current_database()')[0][0]
if db.backend_name == 'mysql':
query = """
SELECT DISTINCT
table_name, column_name, constraint_name, referenced_table_name, referenced_column_name
FROM
information_schema.key_column_usage
WHERE
referenced_table_schema = '%s'
AND referenced_table_name = '%s'
AND referenced_column_name = '%s'
"""
elif db.backend_name == 'postgres':
query = """
SELECT DISTINCT
kcu.table_name, kcu.column_name, kcu.constraint_name, ccu.table_name, ccu.column_name
FROM
information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
WHERE
tc.constraint_type = 'FOREIGN KEY'
AND kcu.table_catalog = '%s'
AND ccu.table_name = '%s'
AND ccu.column_name = '%s'
"""
fks = db.execute(query % (db_name, table, column))
child_fks = []
for fk in fks:
child_fks = child_fks + self.get_fks(fk[0], fk[1], db_name)
return list(fks) + child_fks
def get_and_drop_fks(self, table, column):
fks = []
if db.backend_name in ('mysql', 'postgres') and not db.dry_run:
fks = self.get_fks(table, column)
for fk in fks:
if db.backend_name == 'mysql':
query = 'ALTER TABLE %s DROP FOREIGN KEY %s'
elif db.backend_name == 'postgres':
query = 'ALTER TABLE %s DROP CONSTRAINT %s'
db.execute(query % (fk[0], fk[2]))
return fks
def update_fks(self, fks, field):
for fk in fks:
# This loses whether or not a field allows NULL.
db.alter_column(fk[0], fk[1], field)
# Add FKs after all columns have been altered to prevent data type
# conflicts.
for fk in fks:
db.execute(
'ALTER TABLE %s ADD CONSTRAINT '
'%s FOREIGN KEY (%s) '
'REFERENCES %s(%s)'
% (fk[0], fk[2], fk[1], fk[3], fk[4])
)
def forwards(self, orm):
fks = self.get_and_drop_fks('shop_order', 'id')
db.alter_column('shop_order', 'id', self.gf('django.db.models.fields.CharField')(max_length=16, primary_key=True))
self.update_fks(fks, self.gf('django.db.models.fields.CharField')(max_length=16, primary_key=True))
fks = self.get_and_drop_fks('shop_credit_note', 'id')
db.alter_column('shop_credit_note', 'id', self.gf('django.db.models.fields.CharField')(max_length=16, primary_key=True))
self.update_fks(fks, self.gf('django.db.models.fields.CharField')(max_length=16, primary_key=True))
def backwards(self, orm):
fks = self.get_and_drop_fks('shop_order', 'id')
db.alter_column('shop_order', 'id', self.gf('django.db.models.fields.AutoField')(primary_key=True))
self.update_fks(fks, self.gf('django.db.models.fields.AutoField')(primary_key=True))
fks = self.get_and_drop_fks('shop_credit_note', 'id')
db.alter_column('shop_credit_note', 'id', self.gf('django.db.models.fields.AutoField')(primary_key=True))
self.update_fks(fks, self.gf('django.db.models.fields.AutoField')(primary_key=True))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment