From cab044c66ca3d1f3cfc704f64364aced2b9645af Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Sat, 18 Aug 2012 12:29:31 +0100 Subject: [PATCH] First stab at MySQL support --- django/db/backends/mysql/base.py | 5 +++ django/db/backends/mysql/introspection.py | 32 +++++++++++++++++++ django/db/backends/mysql/schema.py | 24 ++++++++++++++ .../postgresql_psycopg2/introspection.py | 2 +- tests/modeltests/schema/tests.py | 3 -- 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 django/db/backends/mysql/schema.py diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 2222f89cf0075..b3e09edc57bb6 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -37,6 +37,7 @@ from django.db.backends.mysql.creation import DatabaseCreation from django.db.backends.mysql.introspection import DatabaseIntrospection from django.db.backends.mysql.validation import DatabaseValidation +from django.db.backends.mysql.schema import DatabaseSchemaEditor from django.utils.functional import cached_property from django.utils.safestring import SafeString, SafeUnicode from django.utils import six @@ -488,3 +489,7 @@ def check_constraints(self, table_names=None): % (table_name, bad_row[0], table_name, column_name, bad_row[1], referenced_table_name, referenced_column_name)) + + def schema_editor(self): + "Returns a new instance of this backend's SchemaEditor" + return DatabaseSchemaEditor(self) diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py index 6aab0b99ab090..61ab3038c44f4 100644 --- a/django/db/backends/mysql/introspection.py +++ b/django/db/backends/mysql/introspection.py @@ -102,3 +102,35 @@ def get_indexes(self, cursor, table_name): indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])} return indexes + def get_constraints(self, cursor, table_name): + """ + Retrieves any constraints (unique, pk, fk, check) across one or more columns. + Returns {'cnname': {'columns': set(columns), 'primary_key': bool, 'unique': bool}} + """ + constraints = {} + # Loop over the constraint tables, collecting things as constraints + ifsc_tables = ["constraint_column_usage", "key_column_usage"] + for ifsc_table in ifsc_tables: + cursor.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, [self.connection.settings_dict['NAME'], table_name]) + for constraint, column, kind in cursor.fetchall(): + # If we're the first column, make the record + if constraint not in constraints: + constraints[constraint] = { + "columns": set(), + "primary_key": kind.lower() == "primary key", + "foreign_key": kind.lower() == "foreign key", + "unique": kind.lower() in ["primary key", "unique"], + } + # Record the details + constraints[constraint]['columns'].add(column) + return constraints diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py new file mode 100644 index 0000000000000..2c0ad5b8af025 --- /dev/null +++ b/django/db/backends/mysql/schema.py @@ -0,0 +1,24 @@ +from django.db.backends.schema import BaseDatabaseSchemaEditor + + +class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): + + sql_rename_table = "RENAME TABLE %(old_table)s TO %(new_table)s" + + sql_alter_column_null = "MODIFY %(column)s %(type)s NULL" + sql_alter_column_not_null = "MODIFY %(column)s %(type)s NULL" + + sql_delete_unique = "ALTER TABLE %(table)s DROP INDEX %(name)s" + + sql_create_fk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s FOREIGN KEY (%(column)s) REFERENCES %(to_table)s (%(to_column)s)" + sql_delete_fk = "ALTER TABLE %(table)s DROP FOREIGN KEY %(name)s" + + sql_delete_index = "DROP INDEX %(name)s ON %(table_name)s" + + sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY" + + + + + alter_string_set_null = 'MODIFY %(column)s %(type)s NULL;' + alter_string_drop_null = 'MODIFY %(column)s %(type)s NOT NULL;' diff --git a/django/db/backends/postgresql_psycopg2/introspection.py b/django/db/backends/postgresql_psycopg2/introspection.py index c8b8ec833bc9d..d85bdc9d6bdf5 100644 --- a/django/db/backends/postgresql_psycopg2/introspection.py +++ b/django/db/backends/postgresql_psycopg2/introspection.py @@ -91,7 +91,7 @@ def get_indexes(self, cursor, table_name): def get_constraints(self, cursor, table_name): """ - Retrieves any constraints (unique, pk, check) across one or more columns. + Retrieves any constraints (unique, pk, fk, check) across one or more columns. Returns {'cnname': {'columns': set(columns), 'primary_key': bool, 'unique': bool}} """ constraints = {} diff --git a/tests/modeltests/schema/tests.py b/tests/modeltests/schema/tests.py index 8813d3ca236da..7b1de268a418a 100644 --- a/tests/modeltests/schema/tests.py +++ b/tests/modeltests/schema/tests.py @@ -167,7 +167,6 @@ def test_alter(self): # Ensure the field is right to begin with columns = self.column_classes(Author) self.assertEqual(columns['name'][0], "CharField") - self.assertEqual(columns['name'][1][3], 255) self.assertEqual(columns['name'][1][6], False) # Alter the name field to a TextField new_field = TextField(null=True) @@ -197,7 +196,6 @@ def test_rename(self): # Ensure the field is right to begin with columns = self.column_classes(Author) self.assertEqual(columns['name'][0], "CharField") - self.assertEqual(columns['name'][1][3], 255) self.assertNotIn("display_name", columns) # Alter the name field's name new_field = CharField(max_length=254) @@ -213,7 +211,6 @@ def test_rename(self): # Ensure the field is right afterwards columns = self.column_classes(Author) self.assertEqual(columns['display_name'][0], "CharField") - self.assertEqual(columns['display_name'][1][3], 254) self.assertNotIn("name", columns) def test_m2m(self):