Обмен значениями, проверенными на уникальность, с ActiveRecord - PullRequest
0 голосов
/ 24 июня 2018

Учитывая модель, определенную следующим образом:

class Foo < ActiveRecord::Base
  validates_uniqueness_of :bar
end

И служба, которая выполняет определенную операцию над ними:

class FooService
  class << self
    def run(bars_by_foo_id)
      foos = Foo.find(bars_by_foo_id.keys).index_by(&:id)
      ActiveRecord::Base.transaction do
        bars_by_foo_id.each do |foo_id, bar|
          foo = foos[foo_id]
          foo.bar = bar
          foo.save!
        end
      end
    end
  end
end

Когда я пытаюсь это сделать:

# Works
FooService.run({ 1: "a", 2: "b" })
# Breaks
FooService.run({ 1: "b", 2: "a" })

Первый вызов .run назначает правильный bar каждому Foo, но второй прерывается, очевидно, потому что при попытке присвоить "b" первому Foo это значение не является уникальнымбольше.

Я пытался:

def run(bars_by_foo_id)
  Foo.update(
    bars_by_foo_id.keys,
    bars_by_foo_id.values.map do |bar|
      { bar: bar }
    end
  )
end

Но, видимо, это только делает несколько отдельных обновлений и все еще ломается.

Каков был бы правильный способ достичь этого?Могу ли я сделать это в одном выражении SQL?У меня нет уникального ограничения в моей базе данных, только в моей модели.

1 Ответ

0 голосов
/ 25 июня 2018

Наименее уродливый способ, которым я смог придумать, это:

def run(bars_by_foo_id)
  ActiveRecord::Base.transaction do
    ActiveRecord::Base.connection.execute(sql_update(bars_by_foo_id))
    foos = Foo.find(bars_by_foo_id.keys)
    foos.each(&:validate!)
  end
end

private

def sql_update(bars_by_foo_id)
  <<-SQL
    UPDATE foos SET
      bar = bars_by_foo_id.bar
    FROM (VALUES
      #{update_values(bars_by_foo_id)}
    ) AS bars_by_foo_id(id, bar)
    WHERE bars_by_foo_id.id = foos.id;
  SQL
end

def update_values(bars_by_foo_id)
  bars_by_foo_id.map do |id, bar|
    "(#{id}, #{bar})"
  end.join(",")
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...