У меня есть три модели: пользователь, организация и роль. У пользователя есть доступ ко многим организациям через роль, и у организации может быть много пользователей через их роли. Кроме того, пользователи могут иметь доступ к другим моделям через роли, поэтому модель ролей имеет полиморфную c принадлежность_ к ассоциации с именем "access_to", а таблица ролей имеет поля "user_id", "access_to_id" и "access_to_type" для отслеживания ассоциации. Все это прекрасно работает, я могу использовать коллекции organisation.users и user.organisations и вернуть ожидаемые записи.
Тем не менее, было решено переименовать модель Организации в «Организация», используя вместо английского «Английский» sh на английском языке Engli *1031* (это вымышленный пример, реальная проблема похожа, но с дополнительной сложностью, не относящейся к этому вопросу). Приложение работает в течение многих лет, поэтому в таблице ролей находятся тысячи записей, для которых «access_to_type» установлен в «Organization» (с «s»). Кроме того, модель «Организация» должна храниться для устаревшего кода, в то время как модель «Организация» используется новым кодом.
Для достижения этой цели к исходному тексту добавляется «source_type: 'Organization» ». has_many through: ассоциации для пользователя и новой модели организации, поэтому полный код выглядит следующим образом:
class Role < ApplicationRecord
belongs_to :user
belongs_to :access_to, polymorphic: true
end
class User < ApplicationRecord
has_many :roles, autosave: true, foreign_key: "user_id"
has_many(
:organizations,
through: :roles,
source: :access_to,
source_type: "Organisation"
)
end
class Organization < ApplicationRecord
self.table_name = 'organisations'
has_many :roles, as: :access_to
has_many :users, through: :roles, source: :access_to, source_type: "Organisation"
end
class Organisation < ApplicationRecord
has_many :roles, as: :access_to
has_many :users, through: :roles
end
Вызов «User.first.organizations» по-прежнему работает, как ожидается, и возвращает ожидаемые записи с этим SQL оператор:
SELECT "organisations".* FROM "organisations"
INNER JOIN "roles" ON "organisations"."id" = "roles"."access_to_id"
WHERE "roles"."user_id" = ? AND "roles"."access_to_type" = ?
LIMIT ? [["user_id", 1], ["access_to_type", "Organisation"], ["LIMIT", 11]]
И вызов «Organisation.first.users» для устаревшей модели, записанный с «s», работает нормально, генерируя ожидаемое SQL:
SELECT "users".* FROM "users" INNER JOIN "roles"
ON "users"."id" = "roles"."user_id"
WHERE "roles"."access_to_id" = ?
AND "roles"."access_to_type" = ?
LIMIT ?
[["access_to_id", 1],
["access_to_type", "Organisation"],
["LIMIT", 11]]
Однако вызов «Organization.first.users» не возвращает никаких записей, и причина очевидна, если посмотреть на оператор SQL, который Rails генерирует:
SELECT "organisations".* FROM "organisations"
INNER JOIN "roles" ON "organisations"."id" = "roles"."access_to_id"
WHERE "roles"."access_to_id" = ?
AND "roles"."access_to_type" = ?
AND "roles"."access_to_type" = ?
LIMIT ?
[["access_to_id", 1],
["access_to_type", "Organization"],
["access_to_type", "Organisation"],
["LIMIT", 11]]
Оператор SQL ищет записи ролей. где access_to_type - это и «Организация» (с «z»), и «Организация» (с «s»). Кажется, что установка source_type: «Organization» добавляет дополнительное условие для access_to_type вместо замены условия по умолчанию, где «Organization» пишется с «z».
Также это меняет ассоциацию, чтобы посмотреть в «» таблица организаций вместо таблицы пользователей. Я бы ожидал, что он просто изменит условие «access_to_type».
Почему это работает в одном направлении (поиск организаций для пользователя), а не в другом направлении (поиск пользователей для организации)? Это ошибка в Rails (двойное условие может указывать на это), или я могу что-то исправить в конфигурации ассоциации, чтобы она работала? Как source_type может так сильно портить в одном месте и нормально работать в другом?
(Изменение значений access_to_type в базе данных, к сожалению, не вариант, так как есть другой код, ожидающий, что данные останутся неизменными. )
Вот проблема, воспроизводимая в минимальном приложении Rails 6.0: https://github.com/RSpace/polymorphic-issue