Meine Rails Ansichten und Controller sind mit übersät redirect_to
, link_to
und form_for
Methodenaufrufe. Manchmal link_to
und redirect_to
explizit in den Pfaden, die sie verknüpfen (z. B. link_to 'New Person', new_person_path
), aber oft sind die Pfade implizit (z link_to 'Show', person
. B. ).
Ich füge meinem Modell eine einzelne Tabellenvererbung (STI) hinzu (sagen wir Employee < Person
), und alle diese Methoden brechen für eine Instanz der Unterklasse ab (sagen wir Employee
). Wenn Schienen ausgeführt werden link_to @person
, tritt ein Fehler auf undefined method employee_path' for #<#<Class:0x000001022bcd40>:0x0000010226d038>
. Rails sucht nach einer Route, die durch den Klassennamen des Objekts definiert ist, bei dem es sich um einen Mitarbeiter handelt. Diese Mitarbeiterrouten sind nicht definiert, und es gibt keinen Mitarbeiter-Controller, sodass die Aktionen auch nicht definiert sind.
Diese Frage wurde schon einmal gestellt:
- Bei StackOverflow besteht die Antwort darin, jede Instanz von link_to usw. in Ihrer gesamten Codebasis zu bearbeiten und den Pfad explizit anzugeben
- Bei StackOverflow schlagen zwei Personen erneut vor,
routes.rb
die Ressourcen der Unterklasse der übergeordneten Klasse (map.resources :employees, :controller => 'people'
) zuzuordnen . Die oberste Antwort in derselben SO-Frage schlägt vor, jedes Instanzobjekt in der Codebasis mit zu typisieren.becomes
- Eine weitere Antwort bei StackOverflow ist die Antwort im Do Do Repeat Yourself-Camp und schlägt vor, für jede Unterklasse ein doppeltes Gerüst zu erstellen.
- Hier ist wieder die gleiche Frage bei SO, wo die Top-Antwort einfach falsch zu sein scheint (Rails Magic Just Works!).
- An anderer Stelle im Web habe ich diesen Blog-Beitrag gefunden, in dem F2Andy empfiehlt, den Pfad überall im Code zu bearbeiten.
- In dem Blogbeitrag Single Table Inheritance und RESTful Routes bei Logical Reality Design wird empfohlen, die Ressourcen für die Unterklasse dem Superklassen-Controller zuzuordnen, wie in SO-Antwort Nummer 2 oben.
- Alex Reisner hat eine Post- Single-Table-Vererbung in Rails , in der er sich dafür einsetzt, die Ressourcen der untergeordneten Klassen nicht der übergeordneten Klasse in
routes.rb
zuzuordnen, da dadurch nur Routing-Brüche vonlink_to
undredirect_to
, aber nicht von abgefangen werdenform_for
. Daher empfiehlt er, der übergeordneten Klasse stattdessen eine Methode hinzuzufügen, damit die Unterklassen über ihre Klasse lügen. Hört sich gut an, aber seine Methode hat mir den Fehler gegebenundefined local variable or method `child' for #
.
So ist die Antwort , die eleganteste scheint und hat die meisten Konsens (aber es ist nicht alles , dass elegant, noch , dass viel Konsens), die die Ressourcen , um Ihre hinzufügen routes.rb
. Nur dass das nicht funktioniert form_for
. Ich brauche etwas Klarheit! Um die oben genannten Optionen zu destillieren, sind meine Optionen
- Ordnen Sie die Ressourcen der Unterklasse dem Controller der Oberklasse in zu
routes.rb
(und hoffen Sie, dass ich form_for für keine Unterklassen aufrufen muss). - Überschreiben Sie interne Methoden für Schienen, damit die Klassen sich gegenseitig belügen
- Bearbeiten Sie jede Instanz im Code, in der der Pfad zur Aktion eines Objekts implizit oder explizit aufgerufen wird, indem Sie entweder den Pfad ändern oder das Objekt typumwandeln.
Bei all diesen widersprüchlichen Antworten brauche ich eine Entscheidung. Mir scheint, es gibt keine gute Antwort. Ist dies ein Fehler im Design der Schienen? Wenn ja, ist es ein Fehler, der möglicherweise behoben wird? Oder wenn nicht, dann hoffe ich, dass mich jemand klarstellen kann, mich durch die Vor- und Nachteile jeder Option führt (oder erklärt, warum dies keine Option ist) und welche die richtige Antwort ist und warum. Oder gibt es eine richtige Antwort, die ich im Web nicht finde?
quelle
Antworten:
Dies ist die einfachste Lösung, die ich mit minimalen Nebenwirkungen finden konnte.
Jetzt
url_for @person
wirdcontact_path
wie erwartet zugeordnet.So funktioniert es: URL-Helfer verlassen sich darauf
YourModel.model_name
, über das Modell nachzudenken und (unter anderem) Singular- / Plural-Routenschlüssel zu generieren. HierPerson
heißt es im Grunde, ich bin wie einContact
Typ, frag ihn .quelle
build_x
/create_x
). Andererseits ist der Preis für das Spielen mit Magie, dass Sie nie 100% sicher sind, was sich ändern kann.Ich hatte das gleiche Problem. Nach der Verwendung von STI wurde die
form_for
Methode an die falsche untergeordnete URL gesendet.Am Ende fügte ich die zusätzlichen Routen für die untergeordneten Klassen hinzu und zeigte sie auf dieselben Controller
Zusätzlich:
In diesem Fall ist die Struktur tatsächlich ein Gebäude (Kinderklasse).
Es scheint für mich zu funktionieren, nachdem ich einen Submit mit gemacht habe
form_for
.quelle
Ich schlage vor, Sie werfen einen Blick auf: https://stackoverflow.com/a/605172/445908 . Mit dieser Methode können Sie "form_for" verwenden.
quelle
<%= form_for @child, :as => :child, url: @child.becomes(Parent)
<%= form_for @child.becomes(Parent)
Verwenden Sie Typ in den Routen:
http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-2/
quelle
Der Idee von @Prathan Thananart folgen, aber versuchen, nichts zu zerstören. (da es so viel Magie gibt)
Jetzt wird url_for @person wie erwartet contact_path zugeordnet.
quelle
Ich hatte auch Probleme mit diesem Problem und kam mit dieser Antwort auf eine ähnliche Frage wie wir. Es hat bei mir funktioniert.
Hier gezeigte Antwort: Verwenden des STI-Pfads mit demselben Controller
Die
.becomes
Methode wird hauptsächlich zur Lösung von STI-Problemen wie Ihrem verwendetform_for
..becomes
Infos hier: http://apidock.com/rails/ActiveRecord/Base/becomesSuper späte Antwort, aber dies ist die beste Antwort, die ich finden konnte und es hat gut für mich funktioniert. Hoffe das hilft jemandem. Prost!
quelle
Ok, ich war in diesem Bereich von Rails sehr frustriert und bin zu folgendem Ansatz gelangt. Vielleicht hilft dies anderen.
Beachten Sie zunächst, dass eine Reihe von Lösungen über und um das Netz die Verwendung der Konstante für vom Client bereitgestellte Parameter empfehlen. Dies ist ein bekannter DoS-Angriffsvektor, da Ruby keine Müllsammelsymbole sammelt, sodass ein Angreifer beliebige Symbole erstellen und verfügbaren Speicher belegen kann.
Ich habe den folgenden Ansatz implementiert, der die Instanziierung von Modellunterklassen unterstützt und gegenüber dem obigen Contantize-Problem SICHER ist. Es ist dem, was Rails 4 macht, sehr ähnlich, erlaubt aber auch mehr als eine Unterklassenebene (im Gegensatz zu Rails 4) und funktioniert in Rails 3.
Nachdem ich verschiedene Ansätze für das Problem des Ladens von Unterklassen in der Entwicklung ausprobiert hatte, die den oben vorgeschlagenen ähnlich waren, stellte ich fest, dass das einzige, was zuverlässig funktionierte, die Verwendung von "require_dependency" in meinen Modellklassen war. Dies stellt sicher, dass das Laden von Klassen in der Entwicklung ordnungsgemäß funktioniert und keine Probleme in der Produktion verursacht. In der Entwicklung kennt AR ohne 'require_dependency' nicht alle Unterklassen, was sich auf die SQL auswirkt, die für den Abgleich in der Typspalte ausgegeben wird. Außerdem können Sie ohne 'require_dependency' auch in eine Situation geraten, in der mehrere Versionen der Modellklassen gleichzeitig ausgeführt werden! (z. B. kann dies passieren, wenn Sie eine Basis- oder Zwischenklasse ändern. Die Unterklassen scheinen nicht immer neu geladen zu werden und werden von der alten Klasse in Unterklassen belassen.)
Ich überschreibe auch nicht model_name wie oben vorgeschlagen, da ich I18n verwende und unterschiedliche Zeichenfolgen für die Attribute verschiedener Unterklassen benötige, z. B.: Tax_identifier wird zu 'ABN' für Organisation und 'TFN' für Person (in Australien).
Ich verwende auch die Routenzuordnung, wie oben vorgeschlagen, um den Typ festzulegen:
Zusätzlich zur Routenzuordnung verwende ich InheritedResources und SimpleForm und verwende den folgenden generischen Formular-Wrapper für neue Aktionen:
... und für Bearbeitungsaktionen:
Damit dies funktioniert, mache ich in meinem Basis-ResourceContoller den Ressourcenanforderungsnamen von InheritedResource als Hilfsmethode für die Ansicht verfügbar:
Wenn Sie InheritedResources nicht verwenden, verwenden Sie in Ihrem 'ResourceController' Folgendes:
Immer froh, andere Erfahrungen und Verbesserungen zu hören.
quelle
Ich habe kürzlich meine Versuche dokumentiert , ein stabiles STI-Muster in einer Rails 3.0-App zum Laufen zu bringen. Hier ist die TL; DR-Version:
Dieser Ansatz umgeht die von Ihnen aufgelisteten Probleme sowie eine Reihe anderer Probleme, die andere mit STI-Ansätzen hatten.
quelle
Sie können dies versuchen, wenn Sie keine verschachtelten Routen haben:
Oder Sie gehen einen anderen Weg und verwenden OOP-Magie wie hier beschrieben: https://coderwall.com/p/yijmuq
Auf die zweite Weise können Sie ähnliche Helfer für alle Ihre verschachtelten Modelle erstellen.
quelle
Hier finden Sie eine sichere und saubere Möglichkeit, damit es in Formularen und in Ihrer gesamten Anwendung, die wir verwenden, funktioniert.
Dann habe ich in meiner Form. Das hinzugefügte Stück hierfür ist der Bezirk as ::.
Hoffe das hilft.
quelle
Die sauberste Lösung, die ich gefunden habe, besteht darin, der Basisklasse Folgendes hinzuzufügen:
Es funktioniert für alle Unterklassen und ist viel sicherer als das Überschreiben des gesamten Modellnamenobjekts. Indem wir nur auf die Routenschlüssel abzielen, lösen wir die Routing-Probleme, ohne I18n zu beschädigen oder mögliche Nebenwirkungen zu riskieren, die durch das Überschreiben des von Rails definierten Modellnamens verursacht werden.
quelle
Wenn ich eine STI-Vererbung wie diese betrachte:
in 'app / models / a_model.rb' füge ich hinzu:
Und dann in der AModel-Klasse:
Daher kann ich sogar einfach auswählen, welches Modell ich als Standardmodell verwenden möchte, ohne die Definition der Unterklasse zu berühren. Sehr trocken.
quelle
Dieser Weg funktioniert bei mir ok (definieren Sie diese Methode in der Basisklasse):
quelle
Sie können eine Methode erstellen, die ein übergeordnetes Dummy-Objekt für den Routing-Zweck zurückgibt
und rufen Sie dann einfach form_for @ employee.routing_object auf, das ohne Typ das Personenklassenobjekt zurückgibt
quelle
Nach der Antwort von @ prathan-thananart und für die mehreren STI-Klassen können Sie dem übergeordneten Modell Folgendes hinzufügen ->
Dadurch wird jedes Formular mit Kontaktdaten zum Senden von Parametern
params[:contact]
anstelle vonparams[:contact_person]
,params[:contact_whatever]
.quelle
hackisch, aber nur ein weiterer auf der Liste der Lösungen.
funktioniert auf Schienen 2.x und 3.x.
quelle
Child.new
wird eineParent
Klasse anstelle der Unterklasse zurückgegeben. Dies bedeutet, dass Sie die Unterklassen nicht über die Massenzuweisung über einen Controller erstellen können (da diestype
standardmäßig ein geschütztes Attribut ist), es sei denn, Sie legen das Typattribut auch explizit fest.