Usando Rails 7
Estou usando uma Herança de Tabela Única (STI) para armazenar algumas associações muito simples. O objeto de origem usa associações has_many com os modelos STI. Seguindo alguns conselhos em questão https://stackoverflow.com/a/45681641/1014251 Estou usando um relacionamento polimórfico no modelo de junção. Isso funciona muito bem com um incômodo:
Ao criar modelos de junção, o tipo de origem é obtido da classe STI raiz e não da origem real.
Os modelos:
class Guidance < ApplicationRecord
has_many :guidance_details
has_many :themes, through: :guidance_details, source: :detailable, source_type: "Theme"
end
class Detail < ApplicationRecord
has_many :guidance_details, as: :detailable
has_many :guidances, through: :guidance_details
end
class GuidanceDetail < ApplicationRecord
belongs_to :detailable, polymorphic: true
belongs_to :guidance
end
class Theme < Detail
end
O problema
Se eu criar um novo GuidanceDetail e não especificá-lo, detailable_source
o sistema insere "Detalhe" em vez de "Tema".:
guidance_detail = GuidanceDetail.create(guidance: Guidance.first, detailable: Theme.first)
guidance_detail.detailable_type => "Detail"
O tipo detalhável deve ser "Tema".
Para corrigir isso, atualmente estou tendo que especificar o detailable_type toda vez que crio um novo GuidanceDetail.
Uma correção que não funciona
Tentei especificar a associação has_many do objeto filho diretamente, mas obtive o mesmo resultado:
class Theme < Detail
has_many :guidance_details, as: :detailable
end
O método de criação alternativo não funciona
theme = Theme.first
guidance = Guidance.first
guidance.themes << theme
Saídas:
GuidanceDetail Create (1.3ms) INSERT INTO "guidance_details" ("detailable_id", "guidance_id", "position", "created_at", "updated_at", "detailable_type") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["detailable_id", 11], ["guidance_id", 1], ["position", nil], ["created_at", "2024-11-14 10:24:19.785623"], ["updated_at", "2024-11-14 10:24:19.785623"], ["detailable_type", "Detail"]]
Como você pode ver: "detailable_type" é "Detail".
Pelo que entendi, para armazenar o nome da classe
"Theme"
, parece queDetail
é necessário umabstract_class
.ActiveRecord::Associations::BelongsToPolymorphicAssociation define o
foreign_type
para opolymorphic_name
polymorphic_name
é definido como:e
base_class
Então você pode ver que
base_name
só usará o nome real da classe se a classe for a classe base ou a superclasse for uma classe abstrata. Pelo menos foi assim que eu rastreei.Provavelmente você não precisa/quer uma associação polimórfica aqui.
Associações polimórficas resolvem o problema em que uma associação aponta não apenas para várias classes, mas também para várias tabelas . Digamos que temos:
Precisamos de a
wheels.vehicle_id
ewheels.vechicle_type
column, poiswheels.vehicle_id
poderia apontar para qualquer tabela e a combinação dos dois cria uma espécie de chave estrangeira composta.Se mudarmos isso para que Carro e Avião usem herança de tabela única para herdar de Veículo, não precisaremos mais de uma associação polimórfica porque temos uma única tabela:
Aqui wheel não se importa realmente se Vehicle é um Car ou Airplane. Apenas que a associação vehicle aponta para a tabela vehicles e que pode ser resolvida unindo-se em
wheels.vehicle_id = vehicles.id
.Este é um argumento muito forte sobre o porquê de você usar Herança de Tabela Única, pois ela permite que você tenha restrições de chave estrangeira reais em vez de apenas recorrer ao que é um truque para contornar o problema de incompatibilidade de impedância de relação de objeto.
Quando o registro for carregado, o Rails lerá a
vehicles.type
coluna e fornecerá o subtipo correto.