связь «has_many through» не работает при использовании пользовательского столбца ActiveRecord::Type (двоичный)

#ruby-on-rails #ruby #activerecord

#ruby-on-rails #ruby #activerecord

Вопрос:

Я создал следующий тип ActiveRecord, и хотя has_many ассоциации работают хорошо, has_many :through не:

 # app/types/uuid_type.rb
class UuidType < ActiveRecord::Type::Binary
  def serialize(value)
    super(cast_to_uuid(value)amp;.raw)
  end

  def deserialize(value)
    cast_to_uuid(super(value)).to_s
  end

  def cast_value(value)
    cast_to_uuid(value).to_s
  end

  private

  def cast_to_uuid(value)
    return if value.nil?

    case value.size
    when 16 then UUIDTools::UUID.parse_raw(value) # From `uuidtools` gem
    when 36 then UUIDTools::UUID.parse(value)     # From `uuidtools` gem
    end
  end
end

  
 # app/models/token.rb
class Token < ActiveRecord::Base
  attribute :id, UuidType.new, default: SecureRandom.uuid

  has_many :token_users
  has_many :users, through: :token_users
end
  

(Я также написал целый код, чтобы воспроизвести проблему).


where Предложение SQL, сгенерированное для has_many , похоже на следующее и работает отлично:

 WHERE column = x'4e254953bcdb4793a485ac04131565a7'
  

В то время как столбец, созданный для has_many :through , не работает:

 WHERE column = '4e254953-bcdb-4793-a485-ac04131565a7'
  

Он не возвращает никакой ошибки, но также не возвращает никаких результатов.

Проблема в том, что второй столбец ( has_many :through ) не включает x префикс, а также не удаляет дефисы (если я сделаю это вручную, это решит проблему).

Я смог воспроизвести проблему как с MySQL, так и с SQLite, а также с Rails 5 и 6.

Почему has_many :through связь не выдает тот же SQL для двоичного типа? И как вы можете заставить его работать так, чтобы он работал?

Комментарии:

1. Почему вы используете пользовательский AR-тип для создания, uuid вместо использования столбца типа mysql uuid ?

2. @MartinZinovsky В отличие от PostgreSQL, я думаю, что у MySQL нет uuid типа 🙂 В любом случае, я бы не смог изменить это сейчас, поскольку в этой таблице уже много записей, и ее изменение нарушило бы другие вещи. Спасибо.

3. К ВАШЕМУ СВЕДЕНИЮ Mysql 8.0 has UUID support

4. Спасибо, @MartinZinovsky! Тип столбца, используемый в примере из отправленной вами ссылки, является binary . Это то, что я уже использую 🙂 Но Rails еще не сериализует / десериализует его из коробки, верно?

Ответ №1:

Оказывается, это была ошибка Rails.

Я отправил проблему в официальное репозиторий, и они ее исправили:

👉 https://github.com/rails/rails/pull/36847

Ответ №2:

Я не уверен, поможет ли это, но перейдите в config/initializers /type.rb и добавьте

 ActiveRecord::Type.register(:token_users, UuidType)
  

Комментарии:

1. На основе ActiveRecord::Type.register docs первого аргумента должно быть type_name , но здесь у вас есть имя таблицы

2. Я не думаю, что это проблема, иначе has_many (без :through ) не сработало бы.

3. Я только что протестировал, это действительно не сработало. В любом случае, спасибо.