From 4043dc69cd41d100f201e4c71b1515db341fbd45 Mon Sep 17 00:00:00 2001 From: Rob Golding-Day Date: Sun, 5 May 2019 16:26:06 -0400 Subject: [PATCH] Fixed #30444 -- Moved SQL generation for tables to BaseDatabaseSchemaEditor.table_sql(). --- AUTHORS | 1 + django/db/backends/base/schema.py | 112 ++++++++++++++++-------------- 2 files changed, 59 insertions(+), 54 deletions(-) diff --git a/AUTHORS b/AUTHORS index e9c027167c..da44e980e4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -740,6 +740,7 @@ answer newbie questions, and generally made Django that much better: Roberto Aguilar Robert Rock Howard Robert Wittams + Rob Golding-Day Rob Hudson Robin Munn Rodrigo Pinheiro Marques de Araújo diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index 1280666924..990a7f55fb 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -140,6 +140,63 @@ class BaseDatabaseSchemaEditor: def quote_name(self, name): return self.connection.ops.quote_name(name) + def table_sql(self, model): + """Take a model and return its table definition.""" + # Add any unique_togethers (always deferred, as some fields might be + # created afterwards, like geometry fields with some backends). + for fields in model._meta.unique_together: + columns = [model._meta.get_field(field).column for field in fields] + self.deferred_sql.append(self._create_unique_sql(model, columns)) + # Create column SQL, add FK deferreds if needed. + column_sqls = [] + params = [] + for field in model._meta.local_fields: + # SQL. + definition, extra_params = self.column_sql(model, field) + if definition is None: + continue + # Check constraints can go on the column SQL here. + db_params = field.db_parameters(connection=self.connection) + if db_params['check']: + definition += ' ' + self.sql_check_constraint % db_params + # Autoincrement SQL (for backends with inline variant). + col_type_suffix = field.db_type_suffix(connection=self.connection) + if col_type_suffix: + definition += ' %s' % col_type_suffix + params.extend(extra_params) + # FK. + if field.remote_field and field.db_constraint: + to_table = field.remote_field.model._meta.db_table + to_column = field.remote_field.model._meta.get_field(field.remote_field.field_name).column + if self.sql_create_inline_fk: + definition += ' ' + self.sql_create_inline_fk % { + 'to_table': self.quote_name(to_table), + 'to_column': self.quote_name(to_column), + } + elif self.connection.features.supports_foreign_keys: + self.deferred_sql.append(self._create_fk_sql(model, field, '_fk_%(to_table)s_%(to_column)s')) + # Add the SQL to our big list. + column_sqls.append('%s %s' % ( + self.quote_name(field.column), + definition, + )) + # Autoincrement SQL (for backends with post table definition + # variant). + if field.get_internal_type() in ('AutoField', 'BigAutoField'): + autoinc_sql = self.connection.ops.autoinc_sql(model._meta.db_table, field.column) + if autoinc_sql: + self.deferred_sql.extend(autoinc_sql) + constraints = [constraint.constraint_sql(model, self) for constraint in model._meta.constraints] + sql = self.sql_create_table % { + 'table': self.quote_name(model._meta.db_table), + 'definition': ', '.join(constraint for constraint in (*column_sqls, *constraints) if constraint), + } + if model._meta.db_tablespace: + tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace) + if tablespace_sql: + sql += ' ' + tablespace_sql + return sql, params + # Field <-> database mapping functions def column_sql(self, model, field, include_default=False): @@ -250,60 +307,7 @@ class BaseDatabaseSchemaEditor: Create a table and any accompanying indexes or unique constraints for the given `model`. """ - # Create column SQL, add FK deferreds if needed - column_sqls = [] - params = [] - for field in model._meta.local_fields: - # SQL - definition, extra_params = self.column_sql(model, field) - if definition is None: - continue - # Check constraints can go on the column SQL here - db_params = field.db_parameters(connection=self.connection) - if db_params['check']: - definition += " " + self.sql_check_constraint % db_params - # Autoincrement SQL (for backends with inline variant) - col_type_suffix = field.db_type_suffix(connection=self.connection) - if col_type_suffix: - definition += " %s" % col_type_suffix - params.extend(extra_params) - # FK - if field.remote_field and field.db_constraint: - to_table = field.remote_field.model._meta.db_table - to_column = field.remote_field.model._meta.get_field(field.remote_field.field_name).column - if self.sql_create_inline_fk: - definition += " " + self.sql_create_inline_fk % { - "to_table": self.quote_name(to_table), - "to_column": self.quote_name(to_column), - } - elif self.connection.features.supports_foreign_keys: - self.deferred_sql.append(self._create_fk_sql(model, field, "_fk_%(to_table)s_%(to_column)s")) - # Add the SQL to our big list - column_sqls.append("%s %s" % ( - self.quote_name(field.column), - definition, - )) - # Autoincrement SQL (for backends with post table definition variant) - if field.get_internal_type() in ("AutoField", "BigAutoField"): - autoinc_sql = self.connection.ops.autoinc_sql(model._meta.db_table, field.column) - if autoinc_sql: - self.deferred_sql.extend(autoinc_sql) - - # Add any unique_togethers (always deferred, as some fields might be - # created afterwards, like geometry fields with some backends) - for fields in model._meta.unique_together: - columns = [model._meta.get_field(field).column for field in fields] - self.deferred_sql.append(self._create_unique_sql(model, columns)) - constraints = [constraint.constraint_sql(model, self) for constraint in model._meta.constraints] - # Make the table - sql = self.sql_create_table % { - "table": self.quote_name(model._meta.db_table), - "definition": ", ".join(constraint for constraint in (*column_sqls, *constraints) if constraint), - } - if model._meta.db_tablespace: - tablespace_sql = self.connection.ops.tablespace_sql(model._meta.db_tablespace) - if tablespace_sql: - sql += ' ' + tablespace_sql + sql, params = self.table_sql(model) # Prevent using [] as params, in the case a literal '%' is used in the definition self.execute(sql, params or None)