Mes vues Rails et les contrôleurs sont jonchées redirect_to
, link_to
et les form_for
appels de méthode. Parfois link_to
et redirect_to
sont explicites dans les chemins qu'ils lient (par exemple link_to 'New Person', new_person_path
), mais souvent les chemins sont implicites (par exemple link_to 'Show', person
).
J'ajoute un héritage de table unique (STI) à mon modèle (par exemple Employee < Person
), et toutes ces méthodes sont interrompues pour une instance de la sous-classe (par exemple Employee
); lorsque les rails s'exécutent link_to @person
, il se trompe avec undefined method employee_path' for #<#<Class:0x000001022bcd40>:0x0000010226d038>
. Rails recherche un itinéraire défini par le nom de classe de l'objet, qui est employé. Ces itinéraires d'employés ne sont pas définis et il n'y a pas de contrôleur d'employés, donc les actions ne sont pas définies non plus.
Cette question a déjà été posée:
- Chez StackOverflow , la réponse est de modifier chaque instance de link_to etc dans l'ensemble de votre base de code, et d'indiquer le chemin explicitement
- Sur StackOverflow à nouveau, deux personnes suggèrent d'utiliser
routes.rb
pour mapper les ressources de la sous-classe à la classe parente (map.resources :employees, :controller => 'people'
). La première réponse à cette même question SO suggère de transtyper chaque objet d'instance dans la base de code en utilisant.becomes
- Encore un autre chez StackOverflow , la meilleure réponse est dans le camp Do Repeat Yourself, et suggère de créer des échafaudages en double pour chaque sous-classe.
- Voici à nouveau la même question chez SO, où la première réponse semble tout simplement fausse (Rails magic Just Works!)
- Ailleurs sur le web, j'ai trouvé ce billet de blog où F2Andy recommande de modifier le chemin partout dans le code.
- Dans le billet de blog Héritage de table unique et routes RESTful chez Logical Reality Design, il est recommandé de mapper les ressources de la sous-classe vers le contrôleur de la superclasse, comme dans la réponse SO 2 ci-dessus.
- Alex Reisner a publié un héritage de table unique dans les rails , dans lequel il déconseille de mapper les ressources des classes enfants à la classe parente dans
routes.rb
, car cela ne détecte que les ruptures de routage delink_to
etredirect_to
, mais pas deform_for
. Il recommande donc plutôt d'ajouter une méthode à la classe parente pour que les sous-classes mentent sur leur classe. Cela semble bien, mais sa méthode m'a donné l'erreurundefined local variable or method `child' for #
.
Donc , la réponse qui semble la plus élégante et a le plus consensus (mais ce n'est pas tout ce que élégant, ni que beaucoup de consensus), est l'ajouter les ressources à votreroutes.rb
. Sauf que cela ne fonctionne pas pour form_for
. J'ai besoin de clarté! Pour distiller les choix ci-dessus, mes options sont
- mapper les ressources de la sous-classe au contrôleur de la superclasse dans
routes.rb
(et j'espère que je n'ai pas besoin d'appeler form_for sur aucune sous-classe) - Remplacer les méthodes internes des rails pour que les classes se mentent
- Modifiez chaque instance du code dans laquelle le chemin d'accès à l'action d'un objet est appelé implicitement ou explicitement, en modifiant le chemin ou en convertissant le type de l'objet.
Avec toutes ces réponses contradictoires, j'ai besoin d'une décision. Il me semble qu'il n'y a pas de bonne réponse. Est-ce un échec dans la conception des rails? Si tel est le cas, est-ce un bogue qui pourrait être corrigé? Ou si ce n'est pas le cas, j'espère que quelqu'un pourra me donner raison, me présenter les avantages et les inconvénients de chaque option (ou expliquer pourquoi ce n'est pas une option), laquelle est la bonne réponse et pourquoi. Ou y a-t-il une bonne réponse que je ne trouve pas sur le Web?
la source
Réponses:
C'est la solution la plus simple que j'ai pu proposer avec un effet secondaire minimal.
Maintenant
url_for @person
mapperacontact_path
comme prévu.Comment ça marche: les assistants d'URL s'appuient
YourModel.model_name
pour réfléchir sur le modèle et générer (entre autres) des clés de route au singulier / au pluriel. VoiciPerson
essentiellement en disant que je suis juste commeContact
mec, demandez-lui .la source
build_x
/create_x
). D'un autre côté, le prix à jouer avec la magie est que vous n'êtes jamais sûr à 100% de ce qui peut changer.J'ai eu le même problème. Après avoir utilisé STI, la
form_for
méthode publiait sur la mauvaise URL de l'enfant.J'ai fini par ajouter les routes supplémentaires pour les classes enfants et les diriger vers les mêmes contrôleurs
Aditionellement:
dans ce cas, la structure est en fait un bâtiment (classe enfant)
Cela semble fonctionner pour moi après avoir fait une soumission avec
form_for
.la source
Je vous suggère de jeter un œil à: https://stackoverflow.com/a/605172/445908 , en utilisant cette méthode vous permettra d'utiliser "form_for".
la source
<%= form_for @child, :as => :child, url: @child.becomes(Parent)
<%= form_for @child.becomes(Parent)
Utilisez le type dans les itinéraires:
http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-2/
la source
Suivant l'idée de @Prathan Thananart mais en essayant de ne rien détruire. (car il y a tellement de magie impliquée)
Maintenant, url_for @person sera mappé à contact_path comme prévu.
la source
J'avais aussi des problèmes avec ce problème et j'ai trouvé cette réponse sur une question similaire à la nôtre. Cela a fonctionné pour moi.
Réponse affichée ici: Utilisation du chemin STI avec le même contrôleur
La
.becomes
méthode est définie comme principalement utilisée pour résoudre des problèmes d'IST comme votreform_for
..becomes
info ici: http://apidock.com/rails/ActiveRecord/Base/becomesRéponse super tardive, mais c'est la meilleure réponse que j'ai pu trouver et cela a bien fonctionné pour moi. J'espère que cela aide quelqu'un. À votre santé!
la source
Ok, j'ai eu une tonne de frustration dans ce domaine de Rails, et je suis arrivé à l'approche suivante, peut-être que cela aidera les autres.
Tout d'abord, sachez qu'un certain nombre de solutions au-dessus et autour du net suggèrent d'utiliser la constante sur les paramètres fournis par le client. Il s'agit d'un vecteur d'attaque DoS connu car Ruby ne récupère pas les symboles, permettant ainsi à un attaquant de créer des symboles arbitraires et de consommer la mémoire disponible.
J'ai implémenté l'approche ci-dessous qui prend en charge l'instanciation des sous-classes de modèle et qui est SÉCURISÉE par rapport au problème de contantize ci-dessus. Il est très similaire à ce que fait les rails 4, mais permet également plus d'un niveau de sous-classement (contrairement à Rails 4) et fonctionne dans Rails 3.
Après avoir essayé différentes approches pour le 'problème de chargement de sous-classes dans le développement', beaucoup similaires à ce qui est suggéré ci-dessus, j'ai trouvé que la seule chose qui fonctionnait de manière fiable était d'utiliser 'require_dependency' dans mes classes de modèle. Cela garantit que le chargement de classe fonctionne correctement dans le développement et ne pose aucun problème en production. En développement, sans "require_dependency", AR ne connaîtra pas toutes les sous-classes, ce qui a un impact sur le SQL émis pour la correspondance sur la colonne type. De plus, sans 'require_dependency', vous pouvez également vous retrouver dans une situation avec plusieurs versions des classes de modèle en même temps! (par exemple, cela peut arriver lorsque vous changez une classe de base ou intermédiaire, les sous-classes ne semblent pas toujours se recharger et sont laissées sous-classées par rapport à l'ancienne classe)
Je ne remplace pas non plus model_name comme suggéré ci-dessus car j'utilise I18n et j'ai besoin de chaînes différentes pour les attributs des différentes sous-classes, par exemple: tax_identifier devient 'ABN' pour Organization et 'TFN' pour Person (en Australie).
J'utilise également la cartographie d'itinéraire, comme suggéré ci-dessus, en définissant le type:
En plus du mappage d'itinéraire, j'utilise InheritedResources et SimpleForm et j'utilise le wrapper de formulaire générique suivant pour les nouvelles actions:
... et pour les actions d'édition:
Et pour que cela fonctionne, dans mon ResourceContoller de base, j'expose resource_request_name de InheritedResource comme méthode d'assistance pour la vue:
Si vous n'utilisez pas InheritedResources, utilisez quelque chose comme ce qui suit dans votre 'ResourceController':
Toujours heureux d'entendre les expériences et les améliorations des autres.
la source
J'ai récemment documenté mes tentatives pour obtenir un modèle STI stable dans une application Rails 3.0. Voici la version TL; DR:
Cette approche contourne les problèmes que vous énumérez ainsi qu'un certain nombre d'autres problèmes que d'autres ont rencontrés avec les approches IST.
la source
Vous pouvez essayer ceci, si vous n'avez pas d'itinéraires imbriqués:
Ou vous pouvez aller d'une autre manière et utiliser un peu de magie OOP comme décrit ici: https://coderwall.com/p/yijmuq
Dans un second temps, vous pouvez créer des helpers similaires pour tous vos modèles imbriqués.
la source
Voici un moyen sûr et propre de le faire fonctionner dans les formulaires et tout au long de votre application que nous utilisons.
Ensuite, j'ai dans ma forme. La pièce supplémentaire pour cela est le as:: district.
J'espère que cela t'aides.
la source
La solution la plus propre que j'ai trouvée est d'ajouter ce qui suit à la classe de base:
Cela fonctionne pour toutes les sous-classes et est beaucoup plus sûr que de remplacer tout l'objet de nom de modèle. En ciblant uniquement les clés de route, nous résolvons les problèmes de routage sans casser I18n ni risquer des effets secondaires potentiels causés par le remplacement du nom du modèle tel que défini par Rails.
la source
Si je considère un héritage IST comme ceci:
dans 'app / models / a_model.rb' j'ajoute:
Et puis dans la classe AModel:
Par conséquent, je peux même facilement choisir le modèle que je souhaite utiliser par défaut, et cela sans même toucher à la définition de la sous-classe. Très sec.
la source
Cette façon fonctionne pour moi ok (définissez cette méthode dans la classe de base):
la source
Vous pouvez créer une méthode qui renvoie un objet Parent factice pour le routage purpouse
puis appelez simplement form_for @ employee.routing_object qui sans type renverra l'objet de classe Person
la source
Suite à la réponse @ prathan-thananart, et pour les multiples classes STI, vous pouvez ajouter ce qui suit au modèle parent ->
Cela fera chaque formulaire avec des données de contact pour envoyer params comme
params[:contact]
lieu deparams[:contact_person]
,params[:contact_whatever]
.la source
hackish, mais juste un autre à la liste des solutions.
fonctionne sur les rails 2.x et 3.x
la source
Child.new
cela renvoie uneParent
classe plutôt que la sous-classe. Cela signifie que vous ne pouvez pas créer les sous-classes via une affectation en masse via un contrôleur (car iltype
s'agit d'un attribut protégé par défaut) à moins que vous ne définissiez également l'attribut type explicitement.