@@ -52,6 +52,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
5252 sql_delete_table = "DROP TABLE %(table)s"
5353 sql_rename_column = "EXEC sp_rename '%(table)s.%(old_column)s', %(new_column)s, 'COLUMN'"
5454 sql_rename_table = "EXEC sp_rename %(old_table)s, %(new_table)s"
55+ sql_create_unique_null = "CREATE UNIQUE INDEX %(name)s ON %(table)s(%(columns)s) " \
56+ "WHERE %(columns)s IS NOT NULL"
5557
5658 def _alter_column_default_sql (self , model , old_field , new_field , drop = False ):
5759 """
@@ -207,6 +209,11 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
207209 # is to look at its name (refs #28053).
208210 continue
209211 self .execute (self ._delete_constraint_sql (self .sql_delete_index , model , index_name ))
212+ # Drop any unique nullable index/constraints, we'll remake them later if need be
213+ if old_field .unique and old_field .null :
214+ index_names = self ._constraint_names (model , [old_field .column ], unique = True , index = True )
215+ for index_name in index_names :
216+ self .execute (self ._delete_constraint_sql (self .sql_delete_index , model , index_name ))
210217 # Change check constraints?
211218 if (old_db_params ['check' ] != new_db_params ['check' ] and old_db_params ['check' ]) or (
212219 # SQL Server requires explicit deletion befor altering column type with the same constraint
@@ -316,9 +323,13 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
316323 if post_actions :
317324 for sql , params in post_actions :
318325 self .execute (sql , params )
319- # Added a unique?
320- if not old_field .unique and new_field .unique :
321- self .execute (self ._create_unique_sql (model , [new_field .column ]))
326+ if new_field .unique :
327+ if new_field .null :
328+ self .execute (
329+ self ._create_index_sql (model , [new_field ], sql = self .sql_create_unique_null , suffix = "_uniq" )
330+ )
331+ elif not old_field .unique :
332+ self .execute (self ._create_unique_sql (model , [new_field .column ]))
322333 # Added an index?
323334 # constraint will no longer be used in lieu of an index. The following
324335 # lines from the truth table show all True cases; the rest are False:
@@ -498,6 +509,10 @@ def add_field(self, model, field):
498509 # It might not actually have a column behind it
499510 if definition is None :
500511 return
512+ if field .null and field .unique :
513+ definition = definition .replace (' UNIQUE' , '' )
514+ self .deferred_sql .append (
515+ self ._create_index_sql (model , [field ], sql = self .sql_create_unique_null , suffix = "_uniq" ))
501516 # Check constraints can go on the column SQL here
502517 db_params = field .db_parameters (connection = self .connection )
503518 if db_params ['check' ]:
@@ -540,6 +555,10 @@ def create_model(self, model):
540555 definition , extra_params = self .column_sql (model , field )
541556 if definition is None :
542557 continue
558+ if field .null and field .unique :
559+ definition = definition .replace (' UNIQUE' , '' )
560+ self .deferred_sql .append (self ._create_index_sql (
561+ model , [field ], sql = self .sql_create_unique_null , suffix = "_uniq" ))
543562 # Check constraints can go on the column SQL here
544563 db_params = field .db_parameters (connection = self .connection )
545564 if db_params ['check' ]:
@@ -724,7 +743,7 @@ def remove_field(self, model, field):
724743 })
725744 # Drop unique constraints, SQL Server requires explicit deletion
726745 for name , infodict in constraints .items ():
727- if field .column in infodict ['columns' ] and infodict ['unique' ] and not infodict ['primary_key' ]:
746+ if field .column in infodict ['columns' ] and infodict ['unique' ] and not infodict ['primary_key' ] and not infodict [ 'index' ] :
728747 self .execute (self .sql_delete_unique % {
729748 "table" : self .quote_name (model ._meta .db_table ),
730749 "name" : self .quote_name (name ),
0 commit comments