diff --git a/sql_server/pyodbc/base.py b/sql_server/pyodbc/base.py index b6f3eec3..6bc6d743 100644 --- a/sql_server/pyodbc/base.py +++ b/sql_server/pyodbc/base.py @@ -7,8 +7,8 @@ from django.core.exceptions import ImproperlyConfigured from django import VERSION -if VERSION[:3] < (1,8,3) or VERSION[:2] >= (1,9): - raise ImproperlyConfigured("Django %d.%d.%d is not supported." % VERSION[:3]) +# if VERSION[:3] < (1,8,3) or VERSION[:2] >= (1,9): +# raise ImproperlyConfigured("Django %d.%d.%d is not supported." % VERSION[:3]) try: import pyodbc as Database @@ -125,6 +125,8 @@ class DatabaseWrapper(BaseDatabaseWrapper): 10: 2008, 11: 2012, 12: 2014, + 14: 2017, + 15: 2019, } def __init__(self, *args, **kwargs): diff --git a/sql_server/pyodbc/features.py b/sql_server/pyodbc/features.py index a3157415..2646289f 100644 --- a/sql_server/pyodbc/features.py +++ b/sql_server/pyodbc/features.py @@ -21,7 +21,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): requires_literal_defaults = True requires_sqlparse_for_splitting = False supports_1000_query_parameters = False - supports_nullable_unique_constraints = False supports_paramstyle_pyformat = False supports_partially_nullable_unique_constraints = False supports_regex_backreferencing = False diff --git a/sql_server/pyodbc/schema.py b/sql_server/pyodbc/schema.py index 3dab8f38..639b01c9 100644 --- a/sql_server/pyodbc/schema.py +++ b/sql_server/pyodbc/schema.py @@ -32,6 +32,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_delete_table = "DROP TABLE %(table)s" sql_rename_column = "EXEC sp_rename '%(table)s.%(old_column)s', %(new_column)s, 'COLUMN'" sql_rename_table = "EXEC sp_rename %(old_table)s, %(new_table)s" + sql_create_unique_null = "CREATE UNIQUE INDEX %(name)s ON %(table)s(%(columns)s) " \ + "WHERE %(columns)s IS NOT NULL" def _alter_column_type_sql(self, table, old_field, new_field, new_type): new_type = self._set_field_new_type_null_status(old_field, new_type) @@ -218,7 +220,14 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type, self.execute(sql, params) # Added a unique? if not old_field.unique and new_field.unique: - self.execute(self._create_unique_sql(model, [new_field.column])) + if new_field.null: + self.execute( + self._create_index_sql( + model, [new_field], sql=self.sql_create_unique_null, suffix="_uniq" + ) + ) + else: + self.execute(self._create_unique_sql(model, [new_field.column])) # Added an index? if (not old_field.db_index and new_field.db_index and not new_field.unique and not @@ -340,6 +349,11 @@ def add_field(self, model, field): # It might not actually have a column behind it if definition is None: return + if field.null and field.unique: + definition = definition.replace(' UNIQUE', '') + self.deferred_sql.append(self._create_index_sql( + model, [field], sql=self.sql_create_unique_null, suffix="_uniq" + )) # Check constraints can go on the column SQL here db_params = field.db_parameters(connection=self.connection) if db_params['check']: @@ -393,6 +407,11 @@ def create_model(self, model): definition, extra_params = self.column_sql(model, field) if definition is None: continue + if field.null and field.unique: + definition = definition.replace(' UNIQUE', '') + self.deferred_sql.append(self._create_index_sql( + model, [field], sql=self.sql_create_unique_null, suffix="_uniq" + )) # Check constraints can go on the column SQL here db_params = field.db_parameters(connection=self.connection) if db_params['check']: