Verein für polymorphe belongs_to von einem bestimmten Typ
Ich bin relativ neu auf Schienen. Ich möchte hinzufügen, eine Assoziation zu einem Modell, das die polymorphe Assoziation, gibt aber nur Modelle eines bestimmten Typs, z.B.:
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
Habe ich versucht, eine Vielzahl von Kombinationen aus der Lektüre über die Verbände auf ApiDock aber nichts scheint genau das zu tun, was ich will. Hier ist die beste, die ich bisher:
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
Und ich will es um diesen test zu bestehen:
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
Den ersten test bestanden, aber Sie können nicht rufen Sie den Bezug direkt:
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)>'
Warum?
Dem Grund will ich es so machen, weil ich das konstruieren einen Bereich basierend auf der Analyse einer query-string inklusive Eintritt zu den verschiedenen Modellen/etc; der code verwendet, um zu konstruieren, der Umfang ist wesentlich komplexer als das oben es nutzt collection.reflections
usw. Meine aktuelle Lösung funktioniert für das, aber es verletzt mich, ich kann nicht rufen Sie die Beziehungen, die direkt aus einer Instanz der Hinweis.
Konnte ich es lösen, indem man es in zwei Ausgaben: mit scopes direkt
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}) }
und dann nur mit einem getter für note.volunteer
/note.participation
dass nur die Renditen note.subject
wenn es die richtige subject_type
(null sonst), aber ich dachte, in Schienen es muss einen besseren Weg?
- Der etwas kryptischen
{notes: {subject_type: "Volunteer"}}
Klauselnotes:
denn sonst würde die Abfrage auf nicht-existierendevolunteers.subject_type
Spalte, das macht es die Abfrage aufnotes.subjects_type
. Ich sich entschieden haben, entspricht{'notes.subject_type': "Volunteer"}
syntax; es erinnert mich auch an dienotes
sollte ein plural-Tabelle name, nicht die (manchmal singular) Verein name...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich hatte zufällig das gleiche problem haben. und ich habe endlich gebügelt Sie heraus die beste und robusteste Lösung mit einem self-Referenz Vereinigung wie unten.
Clean & simple, nur getestet on Rails 4.1, aber ich denke, es sollte funktionieren, die für Vorherige Versionen.
Volunteer
undParticipation
Sie benutzen wollen würde"Volunteer"
oder:Volunteer
und"Participation"
oder:Participation
. Diskussion hier: github.com/rails/rails/pull/8357Note.create(volunteer: Volunteer.create)
dannsubject_id
undsubject_type
wird null, obwohl, können Sie immer noch abrufen, die freiwillige aus der read-Methode.War ich festgefahren auf diese Art von reverse-Verein und in
Rails 4.2.1
ich endlich entdeckt. Hoffentlich hilft jemand, wenn Sie mit einer neueren version von Rails. Ihre Frage war in der Nähe alles, was ich gefunden in Bezug auf die Frage, die ich hatte.joins
nicht wirklich beschränken, diesubject_type
[Rails 5.0]subject_type
für die nicht-Aufnahme auf join, habe ich diesen workaround:belongs_to :volunteer, -> { where(notes: { subject_type: :Volunteer }) }, foreign_key: :subject_id, foreign_type: :Volunteer, optional: true
ich hoffe, es kann nützlich seinIch habe ein hackish Weg, um dieses Problem. Ich habe einen ähnlichen Anwendungsfall in ein Projekt von mir, und ich fand dieses, um zu arbeiten. In Ihrer Notiz-Modell, das Sie hinzufügen können Assoziationen wie diese:
Müssen Sie eins von diesen für jedes Modell, die Sie möchten, befestigen Sie Notizen, um. Um diesen Prozess Trockner würde ich empfehlen, ein Modul in etwa so:
Dann auch in Ihre Freiwilligen-und beteiligungsmodelle.
[BEARBEITEN]
Einen etwas besseren lambda wäre:
Aus irgendeinem Grund ersetzen die 'wo' mit 'alle' scheint nicht zu funktionieren. Auch beachten Sie, dass 'none' ist nur verfügbar in Rails 4.
[MOAR BEARBEITEN]
Ich bin kein rails 3.2 atm so kann ich das nicht testen, aber ich denke, man kann ein ähnliches Ergebnis erzielen, indem Sie mit einem Proc von Bedingungen, so etwas wie:
Könnte sich lohnen, ein Schuss
undefined method subject_type
), ich denke, es ist da: > Innerhalb der proc, self ist das Objekt, das der Eigentümer des Vereins, es sei denn, Sie sind begierig, laden der Verein, in dem Fall selbst ist die Klasse, die der Verein innerhalb. -- guides.rubyonrails.org/3_1_release_notes.htmlKönnen Sie tun, etwa so:
Den
includes
tun, dieleft join
so haben Sie dienotes
Beziehung mit allenvolunteer
undparticipation
. Und Sie gehen durchsubject_id
zu finden Ihren Eintrag.