Как обеспечить уникальность с помощью значений NULL в PostgreSQL
У меня есть модель с ненулевым CharField
и 2 x нулевыми CharField
:
class MyModel(models.Model):
name = models.CharField('Name', max_length=255, null=False)
title = models.CharField('Title', max_length=255, blank=True)
position = models.CharField('Position', max_length=255, blank=True)
Я хочу убедиться, что name
, title
и position
уникальны вместе, и поэтому использую UniqueConstraint
:
def Meta:
constraints = [
models.UniqueConstraint(
fields=['name', 'title', 'position'],
name="unique_name_title_position"
),
]
Однако, если title
является None
, то это ограничение не работает.
Если разобраться, то это происходит потому, что вы можете вставлять значения NULL
в столбцы с ограничением UNIQUE
, поскольку NULL
- это отсутствие значения, поэтому оно никогда не равно другим значениям NULL
и не считается дубликатом. Это означает, что можно вставлять строки, которые кажутся дубликатами, если одно из значений NULL
.
Какой правильный способ строго обеспечить эту уникальность в Django?
UniqueConstraint
применяются не на уровне Django, а непосредственно к базе данных.
PostgreSQL, например, позволяет использовать несколько Null
записей для уникальных, а MSSQL - нет
Вы можете проверить это для каждого подмножества столбцов с помощью CheckConstraint
, но, как вы увидите, это утомительно, поскольку требует, чтобы каждая комбинация была уникально индексирована .
Чтобы избежать даже всей этой неразберихи, вы можете следовать руководству Django для CharField
как документированный
Избегайте использования null в строковых полях, таких как CharField и TextField. Если поле на основе строки имеет значение null=True, это означает, что оно имеет два возможных значения для "нет данных": NULL и пустая строка. В большинстве случаях, иметь два возможных значения для "нет данных" излишне. Django принято использовать пустую строку, а не NULL. Одно исключение это когда для поля CharField установлены значения unique=True и blank=True. В этой ситуации, null=True требуется, чтобы избежать нарушений ограничения уникальности при сохранении нескольких объектов с пустыми значениями.
Пустая строка проверяется на уникальность
Вы можете выбрать значения, которые никогда не встречаются для title
и position
и использовать индекс следующим образом:
CREATE UNIQUE INDEX ON mytable (
name,
coalesce(title, '#impossible#'),
coalesce(position, '#impossible#'),
);
Это заменит значения NULL на что-то другое, так что дубликаты будут предотвращены.