Associação para pertences polimórficos de um tipo específico
Eu sou relativamente novo no Rails. Gostaria de adicionar uma associação a um modelo que usa a associação polimórfica, mas retorna apenas modelos de um tipo específico, por exemplo:
class Note < ActiveRecord::Base
# The true polymorphic association
belongs_to :subject, polymorphic: true
# Same as subject but where subject_type is 'Volunteer'
belongs_to :volunteer, source_association: :subject
# Same as subject but where subject_type is 'Participation'
belongs_to :participation, source_association: :subject
end
Eu tentei uma vasta gama de combinações lendo sobre as associações no ApiDock, mas nada parece fazer exatamente o que eu quero. Aqui está o melhor que tenho até agora:
class Note < ActiveRecord::Base
belongs_to :subject, polymorphic: true
belongs_to :volunteer, class_name: "Volunteer", foreign_key: :subject_id, conditions: {notes: {subject_type: "Volunteer"}}
belongs_to :participation, class_name: "Participation", foreign_key: :subject_id, conditions: {notes: {subject_type: "Participation"}}
end
E eu quero que passe neste teste:
describe Note do
context 'on volunteer' do
let!(:volunteer) { create(:volunteer) }
let!(:note) { create(:note, subject: volunteer) }
let!(:unrelated_note) { create(:note) }
it 'narrows note scope to volunteer' do
scoped = Note.scoped
scoped = scoped.joins(:volunteer).where(volunteers: {id: volunteer.id})
expect(scoped.count).to be 1
expect(scoped.first.id).to eq note.id
end
it 'allows access to the volunteer' do
expect(note.volunteer).to eq volunteer
end
it 'does not return participation' do
expect(note.participation).to be_nil
end
end
end
O primeiro teste passa, mas você não pode chamar a relação diretamente:
1) Note on volunteer allows access to the volunteer
Failure/Error: expect(note.reload.volunteer).to eq volunteer
ActiveRecord::StatementInvalid:
PG::Error: ERROR: missing FROM-clause entry for table "notes"
LINE 1: ...."deleted" = 'f' AND "volunteers"."id" = 7798 AND "notes"."s...
^
: SELECT "volunteers".* FROM "volunteers" WHERE "volunteers"."deleted" = 'f' AND "volunteers"."id" = 7798 AND "notes"."subject_type" = 'Volunteer' LIMIT 1
# ./spec/models/note_spec.rb:10:in `block (3 levels) in <top (required)>'
Por quê?A razão pela qual desejo fazer dessa maneira é porque estou construindo um escopo com base na análise de uma string de consulta, incluindo a junção a vários modelos / etc; o código usado para construir o escopo é consideravelmente mais complexo que o acima - ele usacollection.reflections
Minha solução atual funciona para isso, mas me ofende não poder chamar as relações diretamente de uma instância do Note.
Eu poderia resolvê-lo dividindo-o em dois problemas: usando escopos diretamente
scope :scoped_by_volunteer_id, lambda { |volunteer_id| where({subject_type: 'Volunteer', subject_id: volunteer_id}) }
scope :scoped_by_participation_id, lambda { |participation_id| where({subject_type: 'Participation', subject_id: participation_id}) }
e depois apenas usando um getter paranote.volunteer
/note.participation
que apenas retornanote.subject
se tem o direitosubject_type
(nada de contrário), mas achei que no Rails deveria haver uma maneira melhor?