Согласитесь с шагами о том, что наилучшим подходом будет определение индекса в базе данных для принудительного применения отдельных записей или для добавления в модель средства проверки уникальности .
Другой вариант - перехватить вызов метода создания ActiveRecord для предотвращения дублирования:
class Example < ApplicationRecord
def self.create( attributes = nil, &block )
attributes.each {|attr| self.create(attr, &block) } if attributes.is_a? Array
return if find_by attributes
super attributes, &block
end
end
Основная идея здесь в том, чтобы прервать работу, если вы уже видите запись с такими же значениями атрибутов в базе данных ... но затем позволить ActiveRecord продолжать выполнять всю тяжелую работу, вызывая ее с помощью super.
Если есть ситуация, когда по какой-то причине вы не можете осуществлять контроль над базой данных, чтобы предотвратить ввод дубликатов, вы можете попробовать подход, при котором вы создаете метод класса для периодической очистки дубликатов из системы.
Вот пример метода, который будет перебирать весь набор записей и использовать хеш для отслеживания того, видел ли он значения раньше, удаляя их, если это так:
class Example < ApplicationRecord
def self.dedup
distinct = {}
all.each do |row|
if distinct[row.user_id] && distinct[row.user_id] == row.value
row.delete
else
distinct[row.user_id] = row.value
end
end
end
end
Затем вы можете назвать это так: Example.dedup
всякий раз, когда вы хотите удалить дубликаты записей.
Для тех, кто хочет следовать дома, вы можете использовать следующую миграцию:
rails g model example user_id:integer value:string
и исходный файл:
examples = Example.create([
{user_id: 1, value: 'A'},
{user_id: 1, value: 'A'},
{user_id: 1, value: 'A'},
{user_id: 1, value: 'A'},
{user_id: 1, value: 'A'},
{user_id: 1, value: 'A'},
{user_id: 1, value: 'A'},
{user_id: 2, value: 'B'},
{user_id: 2, value: 'B'},
{user_id: 3, value: 'C'},
])