Как получить экземпляр базового класса в классе Ruby on Rails с унаследованными классами (STI)

У меня есть пользователь, а у них есть домашние животные. Все виды домашних животных. Рептилии, рыбы, птицы и млекопитающие. Модель млекопитающих имеет два подкласса, унаследованных посредством наследования одной таблицы (STI): кошки и собаки. У моего пользователя есть способ получить своего последнего питомца.

В контроллере статических страниц у меня есть метод для посещения последнего питомца пользователя:

  def go_to_pet
    redirect_to current_user.last_pet || root_path
  end

Теперь проблема, с которой я сталкиваюсь, заключается в том, что когда кошка или собака возвращаются в качестве последнего домашнего животного пользователя, приложение ищет контроллер кошек или собак, но у меня есть и нужен только контроллер млекопитающих. Контроллер млекопитающих довольно хорошо обрабатывает как кошек, так и собак, когда он фактически вызывается.

Мой вопрос таков: как я могу получить экземпляр класса Mammal? Я пробовал Mammal.find(cat.id), но он автоматически возвращает кошку, потому что в этом весь смысл STI.

Единственный уродливый обходной путь, который я могу придумать, - это проверить класс в контроллере статических страниц (в блоке кода выше), а затем использовать redirect_to для использования контроллера млекопитающих, но я бы предпочел избежать этого.

Я попытался явно загрузить базовый класс через Mammal.find(cat.id), но он автоматически вернул экземпляр унаследованного класса Cat на основе столбца типа. Я надеялся получить экземпляр класса Mammal.

current_user.last_pet.becomes(Mammal)api.rubyonrails.org/classes/ActiveRecord/…
Alex 11.07.2023 20:32

ооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо) Почему-то я предположил, что это работает только для изменения базового класса на производный класс. Большое спасибо!

Matthias Benjamin Schönborn 11.07.2023 21:03

если вам не нужен экземпляр Mammal в представлении, вы можете redirect_to mammal_url(current_user.last_pet.id) и передать кота как @pet = current_user.last_pet, который будет экземпляром Cat

Les Nightingill 11.07.2023 22:57

К сожалению, это не сработает, потому что это может также вернуть рептилию, рыбу или птицу, у которых нет url-адреса млекопитающее_.

Matthias Benjamin Schönborn 12.07.2023 08:29
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Обновление: после того, как я написал этот ответ, был опубликован уже принятый ответ, который назначает правильный контроллер в route.rb. Это решает мою проблему еще лучше.


Алексей написал правильный ответ в комментарии к вопросу. Это:

becomes(Mammal)

Документация по api.rubyonrails.org

Я вызываю это в пользовательской модели при получении последнего питомца следующим образом:

if @pet.instance_of?(Cat) || @pet.instance_of?(Dog)
  @pet = @pet.becomes(Mammal)
end

Так что теперь я могу уверенно перенаправлять с помощью redirect_to current_user.last_pet || root_path. Еще раз спасибо Алексу.

Если вы добавите больше Mammal, это может стать неуклюжим. Я не уверен, является ли Mammal абстрактным или нет, но что-то вроде @pet.becomes(@pet.class.base_class) может сработать.

engineersmnky 12.07.2023 15:37
Ответ принят как подходящий

Я думаю, ты пытаешься выстрелить себе в ногу. Если у вас есть класс Dog и Cat, а не просто класс Mammal, то они, вероятно, заслуживают своих собственных контроллеров. И если они ведут себя одинаково, просто сделайте их тривиальными подклассами родителя с общим поведением.

Но если вы действительно хотите сделать то, что описываете, я бы, наверное, сделал что-то вроде:

  def go_to_pet
    last_pet = current_user.last_pet
    if last_pet.class.superclass.abstract_class?
      redirect_to url_for(controller: last_pet.class.base_class.name.tableize, id; last_pet.id)
    else
      redirect_to current_user.last_pet || root_path
    end
  end

Но в основном я бы, вероятно, просто отключил его на маршрутах.

  resources :dogs, controller: 'mammals'

Назначение контроллера в route.rb — это именно то, что мне нужно. Спасибо!

Matthias Benjamin Schönborn 13.07.2023 11:56

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