Уникальная проблема с паранойей
У меня есть приложение рельсы, в котором я использую драгоценные камни devise и паранойи.
У меня есть таблица пользователей в postgres db, которая имеет уникальную проверку по столбцу электронной почты. Я использую паранойю для мягкого удаления, проблема в том, что когда я удаляю пользователя, а затем пытаюсь создать пользователя, используя электронную почту удаленного пользователя, он выдает ошибку PG::UniqueViolation: ERROR
,
Я читал об этом и знаю, что это можно решить с помощью функции частичного индекса рельсов.
http://scottsmerchek.com/2015/08/03/taking-the-d-out-of-crud/
https://devcenter.heroku.com/articles/postgresql-indexes
Как мне это реализовать?
Извините за плохое форматирование, набрав с мобильного телефона.
4 ответа
Поскольку вы удалили пользователя как мягкое удаление, чтобы электронная почта не удалялась из базы данных, только атрибут пользователя is_deleted
был установлен в true.
Разрешить PG::UniqueViolation: ERROR
Теперь вы должны создать уникальный индекс как для поля email, так и для удаленного_этапа. Таким образом, ваша миграция будет
class AddUniqueIndexToUsers < ActiveRecord::Migration
def change
remove_index :users, column: :email
add_index :users, [:email, :deleted_at], unique: true
end
end
Это не удастся на Postgresql, возможное решение будет
class AddUniqueIndexToUsers < ActiveRecord::Migration
def change
remove_index :users, column: :email
add_index :users, [:email, :deleted_at], unique: true, where: "deleted_at is null"
end
end
См. Https://www.ironin.it/blog/partial-unique-indexes-in-postgresql-and-rails.html
достаточно:
class AddUniqueIndexToUsers < ActiveRecord::Migration
def change
remove_index :users, column: :email
add_index :users, :email, unique: true, where: 'deleted_at IS NULL'
end
end
Ответ Ракеша неверен и допускает дублирование в базе данных, потому что соединение
UNIQUE
index завершится ошибкой, если одно из значений равно (и есть по умолчанию).
Чтобы это работало, вам действительно нужно использовать этот нюанс с уникальными индексами наоборот, вы можете иметь целочисленный столбец, например
acive:tinyint (default 1)
и составной индекс вроде этого:
add_index :users, [:email, :active], unique: true
При создании новых пользователей никаких специальных действий не требуется, так как столбец имеет значение по умолчанию
1
. Но каждый раз, когда вы удаляете запись, помимо установки
deleted_at
вам также необходимо установить значение
NULL
. Таким образом, у вас может быть только одна активная запись и множество удаленных с одного и того же адреса электронной почты.
В PostgreSQL или MySQL> 8.0.13 вы можете работать без этого дополнительного столбца и использовать такой функциональный индекс:
CREATE UNIQUE INDEX unique_email
ON users (email, (IF(deleted_at IS NULL, 1, NULL)));
Результат будет аналогичен примеру с
active
столбец. Как только запись будет удалена, уникальный индекс перестанет работать для этой записи, что позволит вам иметь несколько удаленных записей с одним и тем же
email
.