Mis Rieles vistas y controladores están llenas redirect_to
, link_to
y form_for
las llamadas a métodos. A veces link_to
y redirect_to
son explícitos en las rutas que están vinculando (por ejemplo link_to 'New Person', new_person_path
), pero muchas veces las rutas son implícitas (por ejemplo link_to 'Show', person
).
Agrego algo de herencia de tabla única (STI) a mi modelo (por ejemplo Employee < Person
), y todos estos métodos se rompen para una instancia de la subclase (por ejemplo Employee
); cuando se ejecuta rails link_to @person
, se equivoca con undefined method employee_path' for #<#<Class:0x000001022bcd40>:0x0000010226d038>
. Rails está buscando una ruta definida por el nombre de la clase del objeto, que es empleado. Estas rutas de empleados no están definidas y no hay un controlador de empleados, por lo que las acciones tampoco están definidas.
Esta pregunta se ha hecho antes:
- En StackOverflow , la respuesta es editar cada instancia de link_to, etc. en toda su base de código, y establecer explícitamente la ruta
- En StackOverflow nuevamente, dos personas sugieren usar
routes.rb
para asignar los recursos de la subclase a la clase padre (map.resources :employees, :controller => 'people'
). La respuesta principal en esa misma pregunta SO sugiere la conversión de tipos de cada objeto de instancia en la base de código usando.becomes
- Otro más en StackOverflow , la respuesta principal es en el campo Do Repeat Yourself, y sugiere crear andamios duplicados para cada subclase.
- Aquí está la misma pregunta nuevamente en SO, donde la respuesta principal parece estar mal (¡Rails magic Just Works!)
- En otra parte de la web, encontré esta publicación de blog donde F2Andy recomienda editar en la ruta en todas partes del código.
- En la publicación de blog Herencia de tabla única y rutas RESTful en Logical Reality Design, se recomienda asignar los recursos para la subclase al controlador de la superclase, como en la respuesta SO número 2 anterior.
- Alex Reisner tiene una publicación de herencia de tabla única en Rails , en la que defiende contra el mapeo de los recursos de las clases secundarias a la clase principal
routes.rb
, ya que eso solo detecta roturas de enrutamiento desdelink_to
yredirect_to
, pero no desdeform_for
. Por lo tanto, recomienda agregar un método a la clase principal para que las subclases mientan sobre su clase. Suena bien, pero su método me dio el errorundefined local variable or method `child' for #
.
Así que la respuesta que parece más elegante y tiene la mayor parte de consenso (pero no es todo lo que elegante, ni que tanto consenso), es el añadir los recursos a su routes.rb
. Excepto que esto no funciona form_for
. Necesito algo de claridad! Para destilar las opciones anteriores, mis opciones son
- asigne los recursos de la subclase al controlador de la superclase en
routes.rb
(y espero no tener que llamar a form_for en ninguna subclase) - Anular métodos internos de rieles para hacer que las clases se mientan entre sí
- Edite cada instancia en el código donde se invoca implícita o explícitamente la ruta a la acción de un objeto, ya sea cambiando la ruta o escribiendo el objeto.
Con todas estas respuestas en conflicto, necesito una decisión. Me parece que no hay una buena respuesta. ¿Es esto una falla en el diseño de los rieles? Si es así, ¿es un error que puede solucionarse? O si no, entonces espero que alguien pueda aclararme esto, guiarme a través de los pros y los contras de cada opción (o explicar por qué esa no es una opción), y cuál es la respuesta correcta y por qué. ¿O hay una respuesta correcta que no estoy encontrando en la web?
fuente
Respuestas:
Esta es la solución más simple que pude encontrar con un efecto secundario mínimo.
Ahora se
url_for @person
asignará acontact_path
lo esperado.Cómo funciona: los ayudantes de URL dependen
YourModel.model_name
para reflexionar sobre el modelo y generar (entre muchas cosas) claves de ruta en singular / plural. AquíPerson
básicamente está diciendo que soy comoContact
amigo, pregúntale .fuente
build_x
/create_x
). Por otro lado, el precio de jugar con magia es que nunca estás 100% seguro de lo que puede cambiar.Yo tuve el mismo problema. Después de usar STI, el
form_for
método se estaba publicando en la url secundaria incorrecta.Terminé agregando las rutas adicionales para las clases secundarias y señalándolas a los mismos controladores
Adicionalmente:
en este caso, la estructura es en realidad un edificio (clase secundaria)
Parece funcionar para mí después de hacer un envío con
form_for
.fuente
Le sugiero que eche un vistazo a: https://stackoverflow.com/a/605172/445908 , el uso de este método le permitirá usar "form_for".
fuente
<%= form_for @child, :as => :child, url: @child.becomes(Parent)
<%= form_for @child.becomes(Parent)
Utilizar tipo en las rutas:
http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-2/
fuente
Siguiendo la idea de @Prathan Thananart pero tratando de no destruir nada. (ya que hay tanta magia involucrada)
Ahora url_for @person se asignará a contact_path como se esperaba.
fuente
También estaba teniendo problemas con este problema y recibí esta respuesta en una pregunta similar a la nuestra. Funcionó para mi.
La respuesta se muestra aquí: Uso de la ruta STI con el mismo controlador
El
.becomes
método se define como el utilizado principalmente para resolver problemas de ITS como el suyoform_for
..becomes
información aquí: http://apidock.com/rails/ActiveRecord/Base/becomesRespuesta súper tardía, pero esta es la mejor respuesta que pude encontrar y funcionó bien para mí. Espero que esto ayude a alguien. ¡Salud!
fuente
Ok, he tenido un montón de frustración en esta área de Rails, y he llegado al siguiente enfoque, quizás esto ayude a otros.
En primer lugar, tenga en cuenta que varias soluciones por encima y alrededor de la red sugieren el uso de la constante en los parámetros proporcionados por el cliente. Este es un vector de ataque DoS conocido ya que Ruby no recolecta símbolos de recolección de basura, lo que permite a un atacante crear símbolos arbitrarios y consumir memoria disponible.
He implementado el siguiente enfoque que admite la creación de instancias de subclases de modelos, y es SEGURO del problema de contacto anterior. Es muy similar a lo que hace Rails 4, pero también permite más de un nivel de subclasificación (a diferencia de Rails 4) y funciona en Rails 3.
Después de probar varios enfoques para el 'problema de carga de subclase en desarrollo', muchos similares a los sugeridos anteriormente, descubrí que lo único que funcionaba de manera confiable era usar 'require_dependency' en mis clases de modelo. Esto garantiza que la carga de clases funcione correctamente en el desarrollo y no cause problemas en la producción. En desarrollo, sin AR 'require_dependency' AR no conocerá todas las subclases, lo que afecta el SQL emitido para la coincidencia en la columna de tipo. Además, sin 'require_dependency', también puede terminar en una situación con varias versiones de las clases de modelo al mismo tiempo. (por ejemplo, esto puede suceder cuando cambia una clase base o intermedia, las subclases no siempre parecen recargarse y quedan subclases de la clase anterior)
Tampoco anulo model_name como se sugirió anteriormente porque uso I18n y necesito diferentes cadenas para los atributos de diferentes subclases, por ejemplo: tax_identifier se convierte en 'ABN' para Organización y 'TFN' para Persona (en Australia).
También uso el mapeo de rutas, como se sugirió anteriormente, configurando el tipo:
Además del mapeo de rutas, estoy usando InheritedResources y SimpleForm y uso el siguiente contenedor de formulario genérico para nuevas acciones:
... y para editar acciones:
Y para que esto funcione, en mi ResourceContoller base expongo resource_request_name de InheritedResource como un método auxiliar para la vista:
Si no está utilizando InheritedResources, use algo como lo siguiente en su 'ResourceController':
Siempre contento de escuchar otras experiencias y mejoras.
fuente
Recientemente documenté mis intentos de lograr que un patrón STI estable funcione en una aplicación Rails 3.0. Aquí está la versión TL; DR:
Este enfoque evita los problemas que enumera, así como otros problemas que otros han tenido con los enfoques de ITS.
fuente
Puedes probar esto, si no tienes rutas anidadas:
O puede ir por otro lado y usar algo de magia OOP como se describe aquí: https://coderwall.com/p/yijmuq
De la segunda manera, puede crear ayudantes similares para todos sus modelos anidados.
fuente
Aquí hay una manera limpia y segura de que funcione en los formularios y en toda la aplicación que utilizamos.
Entonces tengo en mi forma. La pieza adicional para esto es el: distrito.
Espero que esto ayude.
fuente
La solución más limpia que encontré es agregar lo siguiente a la clase base:
Funciona para todas las subclases y es mucho más seguro que anular todo el objeto del nombre del modelo. Al apuntar solo a las teclas de ruta, resolvemos los problemas de enrutamiento sin romper I18n o arriesgar cualquier efecto secundario potencial causado por anular el nombre del modelo como lo define Rails.
fuente
Si considero una herencia de ITS como esta:
en 'app / models / a_model.rb' agrego:
Y luego en la clase AModel:
Por lo tanto, incluso puedo elegir fácilmente qué modelo quiero usar el predeterminado, y esto sin siquiera tocar la definición de subclase. Muy seco.
fuente
Esta manera me funciona bien (defina este método en la clase base):
fuente
Puede crear un método que devuelva un objeto padre ficticio para el propósito de enrutamiento
y luego simplemente llame a form_for @ employee.routing_object que sin tipo devolverá el objeto de clase Person
fuente
Siguiendo la respuesta @ prathan-thananart, y para las múltiples clases de ITS, puede agregar lo siguiente al modelo principal ->
Eso hará que cada formulario con los datos de contacto para enviar params como
params[:contact]
lugar deparams[:contact_person]
,params[:contact_whatever]
.fuente
hackear, pero solo otro más en la lista de soluciones.
funciona en rieles 2.xy 3.x
fuente
Child.new
, devuelve unaParent
clase en lugar de la subclase. Esto significa que no puede crear las subclases a través de la asignación masiva a través de un controlador (ya quetype
es un atributo protegido de forma predeterminada) a menos que también establezca el atributo type explícitamente.