Обработка условных ассоциаций `belongs_to` с различными типами внешних ключей в Rails

Я работаю над приложением Rails, в котором мне нужно управлять полиморфной ассоциацией с различными типами внешних ключей на основе атрибута reporter_type. Основная проблема заключается в том, что типы внешних ключей различаются (integer для reporter_id и string для partition_reporter_id), и мне нужно условно переключаться между этими ключами в пределах одной модели.

Я использую рельсы 7.0.8.4

Текущая настройка В моей модели Report у меня есть полиморфная ассоциация, определенная следующим образом:

class Report < ApplicationRecord

  belongs_to :report_configuration, counter_cache: true
  belongs_to :reporter, polymorphic: true, optional: true

  Other associations and validations...
end

reporter_id обычно соответствует customer_id (целому числу), но мне нужно ввести partition_reporter_id для обработки строкового UUID для идентификаторов Salesforce.

Требования Если reporter_type равен 'partition', внешний ключ должен быть partition_reporter_id. Для всех остальных значений reporter_type внешний ключ должен быть reporter_id.

Проблемы Стандартные полиморфные ассоциации Rails предполагают один и тот же тип данных для внешнего ключа в разных типах связанных записей. Использование двух разных типов данных усложняет логику модели и SQL-запросы, которые строит Rails. Rails изначально не поддерживает условную логику непосредственно в определениях ассоциаций.

Вопросы Как я могу изменить или расширить ассоциацию belongs_to :reporter, чтобы условно использовать разные внешние ключи (partition_reporter_id или reporter_id) на основе reporter_type? Существуют ли в Rails какие-либо передовые методы или шаблоны проектирования, позволяющие справиться с таким сценарием без ущерба для удобства обслуживания и производительности? Будем очень признательны за любые идеи или предложения о том, как решить эту проблему!

А как насчет введения промежуточной модели, на которую вы можете ссылаться из своего отчета и которая содержит внешний UUID?

Stefan 28.08.2024 17:13

На самом деле вы не можете этого сделать. Внешний ключ связи в Rails одинаков для всех строк. Можете ли вы даже представить себе, насколько сумасшедшим было бы присоединение, предварительная загрузка или нетерпеливая загрузка ассоциаций в противном случае? Я думаю, вам нужно подойти к проблеме, разработав схему по-другому.

max 28.08.2024 18:24
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это невозможно и по довольно веской причине.

В ActiveRecord все ассоциации (даже полиморфные) имеют фиксированный столбец внешнего ключа.

Когда вы присоединяетесь через ассоциацию, Rails создает следующий SQL:

SELECT "reports".* FROM "reports" 
INNER JOIN "reporters" 
ON "reports"."id" = "reporter"."report_id"

Если вы хотите выполнить соединение, где внешний столбец является динамическим, возможно, это можно сделать с помощью оператора Case:

SELECT "reports".* FROM "reports" 
INNER JOIN "reporters"
  ON CASE
  WHEN reports.reporter_type = 'partition' THEN
    reports.partition_reporter_id = reporters.some_column
  ELSE 
    reports.id = reporters.rapporter_id

Это не кажется таким уж плохим, пока вы не вспомните, что ActiveRecord должен быть многоязычным и поддерживать широкий спектр систем управления реляционными базами данных, а в Rails есть множество методов для объединения и быстрой загрузки. Если вы затем добавите к этому немного полиморфизма, станет еще хуже.

Эта небольшая функция добавит массу сложностей при очень незначительной реальной выгоде, потому что то, что вы хотите сделать, — это бороться с самой природой того, как должна работать реляционная база данных.

Лучшее решение — просто переосмыслить это и рассмотреть возможность использования как суррогатного ключа (автоинкрементного целого числа), так и внешнего идентификатора (uuid отдела продаж) или даже промежуточной модели, в которой хранится внешний идентификатор.

Спасибо вам огромное за такое. понятное объяснение :)

Shreya Gupta 29.08.2024 18:02

Другие вопросы по теме