ruby on rails - update - Alternativa para accept_nested_attributes_for-quizás virtus
select form rails (4)
Entonces, la respuesta aceptada simplemente dice por qué accepts_nested_attributes_for
suele ser una buena solución, pero en realidad nunca ofrece una solución sobre cómo hacerlo. Y el ejemplo en el artículo vinculado tendrá problemas si desea que su formulario acepte un número dinámico de objetos anidados. Esta es la única solución que he encontrado.
https://coderwall.com/p/kvsbfa/nested-forms-with-activemodel-model-objects
Para la posteridad, esto es lo básico, pero hay un poco más en el sitio:
class ContactListForm
include ActiveModel::Model
attr_accessor :contacts
def contacts_attributes=(attributes)
@contacts ||= []
attributes.each do |i, contact_params|
@contacts.push(Contact.new(contact_params))
end
end
end
class ContactsController < ApplicationController
def new
@contact_list = ContactListForm.new(contacts: [Contact.new])
end
end
y f.fields_for :contacts
deben comportarse como una relación has_many
y fácilmente manejados por su objeto de formulario.
¿Si Contact
no es un modelo de AR
, tendrá que simular persisted?
también.
Soy relativamente nuevo en los rieles y, finalmente, encontré la forma correcta de utilizar accepts_nested_attributes_for
.
Sin embargo, hay algunos recursos serios en la web que dicen que utilizar accepts_nested_attributes_for
es generalmente una mala práctica (como esta).
¿Qué cambios son necesarios para evitar accepts_nested_attributes_for
y en qué carpeta colocaría el archivo de clase adicional (supongo que uno necesita una clase adicional)?
Leí que el virtus es apropiado para eso. ¿Está bien?
Aquí hay un ejemplo muy básico que todavía usa accepts_nested_attributes_for
(encuentre el ejemplo completo here ):
Modelos
class Person < ActiveRecord::Base
has_many :phones
accepts_nested_attributes_for :phones
end
class Phone < ActiveRecord::Base
belongs_to :person
end
Controlador
class PeopleController < ApplicationController
def new
@person = Person.new
@person.phones.new
end
def create
@person = Person.new(person_params)
@person.save
redirect_to people_path
end
def index
@people = Person.all
end
private
def person_params
params.require(:person).permit(:name, phones_attributes: [ :id, :number ])
end
end
Ver (personas / new.html.erb)
<%= form_for @person, do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :phones do |builder| %>
<p>
<%= builder.label :number %><br />
<%= builder.text_field :number %>
</p>
<% end %>
<%= f.submit %>
<% end %>
[editar]
¿Sería una buena idea usar un objeto de servicio?
Es mucho más fácil usar virtus en lugar de accepts_nested_attributes_for
de lo que pensé. El requisito más importante era atreverse a hacer cosas que no estaban cubiertas en ninguno de los tutoriales que leí todavía.
Paso a paso:
- Agregué
gem ''virtus''
al Gemfile y ejecuté elbundle install
. Escribí un archivo models / contact.rb y escribí el siguiente código:
class Contact include Virtus extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations attr_reader :name attr_reader :number attribute :name, String attribute :number, Integer def persisted? false end def save if valid? persist! true else false end end private def persist! @person = Person.create!(name: name) @phones = @person.phones.create!(number: number) end end
Luego corrí los
rails generate controller contacts
y llené * models / contacts_controller.rb * conclass ContactsController < ApplicationController def new @contact = Contact.new end def create @contact = Contact.new(contact_params) @contact.save redirect_to people_path end def contact_params params.require(:contact).permit(:name, :number) end end
El siguiente paso fue la vista. Creé views / contacts / new.html.erb y escribí este formulario básico
<%= form_for @contact do |f| %> <p> <%= f.label :name %><br /> <%= f.text_field :name %> </p> <p> <%= f.label :number %><br /> <%= f.text_field :number %> </p> <%= f.submit %> <% end %>
Por supuesto que también necesitaba agregar los
resources :contacts
rutaresources :contacts
Eso es. Tal vez podría hacerse más elegante. Tal vez también sería útil utilizar solo la clase Contactos, también para las otras acciones CRUD. No lo intenté todavía ...
Puede encontrar todos los cambios aquí: https://github.com/speendo/PhoneBook/tree/virtus/app/models
Railscasts también tiene un episodio en modelos de formularios anidados: http://railscasts.com/episodes/196-nested-model-form-revised?view=asciicast
Como se muestra en los comentarios, la gema de capullo se simplifica mucho: https://github.com/nathanvda/cocoon
Su pregunta implica que usted cree que la funcionalidad accept_nested_attributes es algo malo, lo cual no es el caso y funciona perfectamente bien.
Comenzaré diciendo que no necesita una alternativa a accept_nested_attributes_for, pero lo cubriré al final de esta publicación.
Con referencia al enlace que proporciona, no cita nada sobre por qué el cartel cree que accept_nested_attributes_for debería estar en desuso y simplemente se limita a
En mi humilde opinión, debería estar en desuso.
Los atributos anidados son un concepto extremadamente importante cuando se considera cómo capturar múltiples registros relacionados con un padre en una forma única que no es solo una cosa de Ruby on Rails, sino que se usa en la mayoría de las aplicaciones web complejas para enviar datos al servidor desde el navegador, independientemente de de las lenguas utilizadas para desarrollar el sitio.
No estoy criticando el artículo que señala en absoluto. Para mí, solo está señalando alternativas obvias para completar un modelo respaldado por una base de datos con una gran cantidad de código que no está necesariamente relacionado con la lógica empresarial. El ejemplo específico utilizado es simplemente una alternativa de preferencia de estilo de codificación.
Cuando el tiempo es dinero y la presión está activada y una línea de código hará el trabajo en comparación con las 22 líneas de código que se muestran en el ejemplo, mi preferencia en la mayoría de los casos (no en todos los casos) es usar una línea de código en un modelo (accept_nested_attributes_for ) para aceptar atributos anidados publicados desde un formulario.
No es una buena práctica responder correctamente a su pregunta, ya que no ha dicho realmente por qué USTED cree que accept_nested_attributes_for no es una buena práctica, sin embargo, la alternativa más simple es simplemente extraer los atributos de hash de parámetros en la acción de su controlador y manejar cada registro individualmente en una transacción.
Actualización - seguimiento en el comentario
Creo que el autor del artículo vinculado sostiene que, siguiendo los paradigmas de oop, cada objeto solo debe leer y escribir sus propios datos. Con accept_nested_attributes_for, un objeto, sin embargo, cambia algunos datos de otros objetos.
OK Vamos a aclarar eso. En primer lugar, los paradigmas OO no sugieren tal cosa. Las clases deben ser discretas, pero se les permite interactuar con otras clases. De hecho, no tendría sentido un enfoque OO en Ruby si este fuera el caso, ya que TODO en ruby es una clase, por lo tanto, nada podría hablar con otra cosa. Solo imagine lo que sucedería si un objeto que simplemente es una instancia de su controlador no pudiera interactuar con modelos u otros controladores.
Con accept_nested_attributes_for, un objeto, sin embargo, cambia algunos datos de otros objetos.
Un par de puntos en esa declaración, ya que es complejo, trataré de ser lo más breve posible.
1) Las instancias del modelo guardan los datos. En escenarios muy complejos que involucran cientos de tablas en cualquier / la mayoría de los otros idiomas (C, Delphi, VB, por nombrar algunas), un nivel medio en una solución de 3 niveles hace precisamente eso. En términos de Rails, un modelo es un lugar para la lógica de negocios y hace el trabajo del nivel medio en una solución de 3 niveles que normalmente está respaldada por procedimientos almacenados y vistas en el RDBMS. Las modelos con razón deberían poder hablar entre ellas.
2) accept_nested_attributes_for no rompe ningún principio OO en absoluto. simplemente simplifica la cantidad de código que necesitarías escribir si el método no existiera (como lo estás descubriendo). Si acepta los atributos que están anidados dentro de un hash de params para los modelos secundarios, todo lo que está haciendo es permitir que los modelos secundarios manejen esos datos de la misma manera que lo haría la acción de su controlador. Ninguna lógica de negocios se pasa por alto y usted obtiene beneficios adicionales.
Por último
Puedo permitirme preocuparme por la elegancia del código (más que por tiempo)
Les puedo asegurar que no hay nada elegante en escribir 20 o más líneas de código de lo que necesita y agregar cientos de líneas más de código desde una gema donde una línea de código hará el trabajo por usted. Como han dicho otros (incluyéndome a mí) accept_nested_attributes_for no siempre es un método de ActiveRecord apropiado para usar y es una buena cosa que esté haciendo al observar diferentes enfoques, ya que finalmente podrá hacer juicios mejor informados sobre cuándo usar En los métodos y cuándo escribir los tuyos. Sin embargo, sugeriría que para comprender completamente lo que está sucediendo (a medida que establezca que tiene el tiempo) sería mejor que escriba su propio código para manejar los objetos del formulario y acepte atributos de atributos anidados. De esa manera te encontrarías comprendiendo mucho más.
Espero que tenga sentido y buena suerte con tu aprendizaje.
ACTUALIZACIÓN 2
Para finalmente llegar a su punto y en referencia a su propia respuesta, además de tener en cuenta los excelentes comentarios que otros han hecho en su propia respuesta, los objetos respaldados por la gema de virtus son una solución perfectamente razonable, especialmente cuando se trata de cómo deben ser los datos. recogido. La combinación ayuda a separar la lógica de la interfaz de usuario de la lógica de negocios y, mientras que finalmente pase los datos a los modelos para que la lógica de negocios no se pase por alto (ya que demuestra que está haciendo exactamente esto), entonces tiene una gran solución. .
Simplemente no descartes los atributos_de_Atributos_ajidos de la mano.
También puede obtener algún beneficio al ver los railscasts de Ryan Bates en objetos de formas.