1
0
mirror of https://github.com/django/django.git synced 2025-10-31 09:41:08 +00:00

Fixed #31702 -- Added support for PostgreSQL opclasses in UniqueConstraint.

This commit is contained in:
Hannes Ljungberg
2020-06-11 21:37:12 +02:00
committed by Mariusz Felisiak
parent 69e0d9c553
commit 7edc6e53a7
6 changed files with 210 additions and 14 deletions

View File

@@ -1092,13 +1092,16 @@ class BaseDatabaseSchemaEditor:
if deferrable == Deferrable.IMMEDIATE:
return ' DEFERRABLE INITIALLY IMMEDIATE'
def _unique_sql(self, model, fields, name, condition=None, deferrable=None, include=None):
def _unique_sql(
self, model, fields, name, condition=None, deferrable=None,
include=None, opclasses=None,
):
if (
deferrable and
not self.connection.features.supports_deferrable_unique_constraints
):
return None
if condition or include:
if condition or include or opclasses:
# Databases support conditional and covering unique constraints via
# a unique index.
sql = self._create_unique_sql(
@@ -1107,6 +1110,7 @@ class BaseDatabaseSchemaEditor:
name=name,
condition=condition,
include=include,
opclasses=opclasses,
)
if sql:
self.deferred_sql.append(sql)
@@ -1120,7 +1124,10 @@ class BaseDatabaseSchemaEditor:
'constraint': constraint,
}
def _create_unique_sql(self, model, columns, name=None, condition=None, deferrable=None, include=None):
def _create_unique_sql(
self, model, columns, name=None, condition=None, deferrable=None,
include=None, opclasses=None,
):
if (
(
deferrable and
@@ -1139,8 +1146,8 @@ class BaseDatabaseSchemaEditor:
name = IndexName(model._meta.db_table, columns, '_uniq', create_unique_name)
else:
name = self.quote_name(name)
columns = Columns(table, columns, self.quote_name)
if condition or include:
columns = self._index_columns(table, columns, col_suffixes=(), opclasses=opclasses)
if condition or include or opclasses:
sql = self.sql_create_unique_index
else:
sql = self.sql_create_unique
@@ -1154,7 +1161,10 @@ class BaseDatabaseSchemaEditor:
include=self._index_include_sql(model, include),
)
def _delete_unique_sql(self, model, name, condition=None, deferrable=None, include=None):
def _delete_unique_sql(
self, model, name, condition=None, deferrable=None, include=None,
opclasses=None,
):
if (
(
deferrable and
@@ -1164,7 +1174,7 @@ class BaseDatabaseSchemaEditor:
(include and not self.connection.features.supports_covering_indexes)
):
return None
if condition or include:
if condition or include or opclasses:
sql = self.sql_delete_index
else:
sql = self.sql_delete_unique

View File

@@ -77,7 +77,16 @@ class Deferrable(Enum):
class UniqueConstraint(BaseConstraint):
def __init__(self, *, fields, name, condition=None, deferrable=None, include=None):
def __init__(
self,
*,
fields,
name,
condition=None,
deferrable=None,
include=None,
opclasses=(),
):
if not fields:
raise ValueError('At least one field is required to define a unique constraint.')
if not isinstance(condition, (type(None), Q)):
@@ -92,10 +101,18 @@ class UniqueConstraint(BaseConstraint):
)
if not isinstance(include, (type(None), list, tuple)):
raise ValueError('UniqueConstraint.include must be a list or tuple.')
if not isinstance(opclasses, (list, tuple)):
raise ValueError('UniqueConstraint.opclasses must be a list or tuple.')
if opclasses and len(fields) != len(opclasses):
raise ValueError(
'UniqueConstraint.fields and UniqueConstraint.opclasses must '
'have the same number of elements.'
)
self.fields = tuple(fields)
self.condition = condition
self.deferrable = deferrable
self.include = tuple(include) if include else ()
self.opclasses = opclasses
super().__init__(name)
def _get_condition_sql(self, model, schema_editor):
@@ -114,6 +131,7 @@ class UniqueConstraint(BaseConstraint):
return schema_editor._unique_sql(
model, fields, self.name, condition=condition,
deferrable=self.deferrable, include=include,
opclasses=self.opclasses,
)
def create_sql(self, model, schema_editor):
@@ -123,6 +141,7 @@ class UniqueConstraint(BaseConstraint):
return schema_editor._create_unique_sql(
model, fields, self.name, condition=condition,
deferrable=self.deferrable, include=include,
opclasses=self.opclasses,
)
def remove_sql(self, model, schema_editor):
@@ -130,15 +149,16 @@ class UniqueConstraint(BaseConstraint):
include = [model._meta.get_field(field_name).column for field_name in self.include]
return schema_editor._delete_unique_sql(
model, self.name, condition=condition, deferrable=self.deferrable,
include=include,
include=include, opclasses=self.opclasses,
)
def __repr__(self):
return '<%s: fields=%r name=%r%s%s%s>' % (
return '<%s: fields=%r name=%r%s%s%s%s>' % (
self.__class__.__name__, self.fields, self.name,
'' if self.condition is None else ' condition=%s' % self.condition,
'' if self.deferrable is None else ' deferrable=%s' % self.deferrable,
'' if not self.include else ' include=%s' % repr(self.include),
'' if not self.opclasses else ' opclasses=%s' % repr(self.opclasses),
)
def __eq__(self, other):
@@ -148,7 +168,8 @@ class UniqueConstraint(BaseConstraint):
self.fields == other.fields and
self.condition == other.condition and
self.deferrable == other.deferrable and
self.include == other.include
self.include == other.include and
self.opclasses == other.opclasses
)
return super().__eq__(other)
@@ -161,4 +182,6 @@ class UniqueConstraint(BaseConstraint):
kwargs['deferrable'] = self.deferrable
if self.include:
kwargs['include'] = self.include
if self.opclasses:
kwargs['opclasses'] = self.opclasses
return path, args, kwargs