Я работал с некоторыми более опытными разработчиками Ruby, когда вернулся на работу, и мы пришли к выводу, что это необработанный крайний случай в ActiveRecord (подробнее см. Ниже).
Мой возможный обходной путь (с комментариями) состоит в том, чтобы переопределить существующие методы в проблеме и включать эту проблему только тогда, когда она нам нужна:
module Concerns::ARBugPrimaryKeyNotIDColumnWorkaround
extend ActiveSupport::Concern
# Override this (buggy?) method to allow us to update a column called "ID", even if there's a different primary key
# ActiveRecord tries to map ID attribute to the legacy table's primary key column...
# Doesn't check to make sure there isn't already another column called ID that we're actually trying to update
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_methods/write.rb
# TODO: Submit a patch to rails/activerecord
def write_attribute(attr_name, value)
name = if self.class.attribute_alias?(attr_name)
self.class.attribute_alias(attr_name).to_s
else
attr_name.to_s
end
primary_key = self.class.primary_key
# name = primary_key if name == "id".freeze && primary_key # BUG: (?) assumes primary key is always called ID
sync_with_transaction_state if name == primary_key
_write_attribute(name, value)
end
# Also need to clone this for some reason
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_methods/write.rb
def _write_attribute(attr_name, value) # :nodoc:
@attributes.write_from_user(attr_name.to_s, value)
value
end
end
Мы по-прежнему не можем вызвать Condition.new(id: 12345, other_column: other_value)
(или Condition.create(...
или condition.id
), но обходной путь позволяет явно установить идентификатор с помощью condition[:id]
:
condition = ::Condition.new(mapped_attributes) # Don't include ID in mapped_attributes
condition[:id] = 12345 # Must be set explicitly using [:id] due to AR bug
condition.save
Как только закончится наш текущий спринт, моя цель - добавить тест в ActiveRecord, чтобы продемонстрировать это поведение и поднять его.
Дополнительная информация
В Rails есть тесты для пользовательских первичных ключей. У него также есть тесты на наличие столбца ID, который не является первичным ключом. Случай необработанного края, кажется, когда вы оба одновременно ...
Каждый раз, когда вы пытаетесь обновить столбец идентификатора (передавая атрибут id), пользовательская обработка первичного ключа запускается и обновляет первичный ключ.
Ответы на мои конкретные вопросы:
- Учитывая, что я не могу изменить устаревшую схему, у меня нет большого выбора. Я закончил тем, что переписал метод
write_attribute
(только когда мы включили патч обезьяны), а не добавил метод write_id_attribute
в существующий модуль.
- Я правильно открывал модуль (но смотри предыдущий и следующий ответы)
- Я все еще не уверен, почему я не смог вызвать существующий метод
_write_attribute
. Я подозреваю, что есть какая-то "магия" Ruby или Rails, которая по-разному относится к _методам. В конце концов, мне тоже пришлось это переопределить (см. Решение выше)