Как обеспечить уникальность с помощью значений 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 на что-то другое, так что дубликаты будут предотвращены.