text_field rails form_with form_tag form collection ruby-on-rails ruby-on-rails-3 forms nested-forms has-many-through

ruby on rails - form_with - Rails has_many a través del formulario con casillas de verificación y campo adicional en el modelo de unión



rails form select (3)

¡Parece que lo descubrí! Esto es lo que tengo:

Mis modelos:

class Product < ActiveRecord::Base has_many :categorizations, dependent: :destroy has_many :categories, through: :categorizations accepts_nested_attributes_for :categorizations, allow_destroy: true validates :name, presence: true def initialized_categorizations # this is the key method [].tap do |o| Category.all.each do |category| if c = categorizations.find { |c| c.category_id == category.id } o << c.tap { |c| c.enable ||= true } else o << Categorization.new(category: category) end end end end end class Category < ActiveRecord::Base has_many :categorizations, dependent: :destroy has_many :products, through: :categorizations validates :name, presence: true end class Categorization < ActiveRecord::Base belongs_to :product belongs_to :category validates :description, presence: true attr_accessor :enable # nice little thingy here end

La forma:

<%= form_for(@product) do |f| %> ... <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name %> </div> <%= f.fields_for :categorizations, @product.initialized_categorizations do |builder| %> <% category = builder.object.category %> <%= builder.hidden_field :category_id %> <div class="field"> <%= builder.label :enable, category.name %> <%= builder.check_box :enable %> </div> <div class="field"> <%= builder.label :description %><br /> <%= builder.text_field :description %> </div> <% end %> <div class="actions"> <%= f.submit %> </div> <% end %>

Y el controlador:

class ProductsController < ApplicationController # use `before_action` instead of `before_filter` if you are using rails 5+ and above, because `before_filter` has been deprecated/removed in those versions of rails. before_filter :process_categorizations_attrs, only: [:create, :update] def process_categorizations_attrs params[:product][:categorizations_attributes].values.each do |cat_attr| cat_attr[:_destroy] = true if cat_attr[:enable] != ''1'' end end ... # all the rest is a standard scaffolded code end

Desde el primer vistazo funciona bien. Espero que no se rompa de alguna manera ... :)

Gracias a todos. Un agradecimiento especial a Sandip Ransing por participar en la discusión. Espero que sea de utilidad para alguien como yo.

Estoy tratando de resolver una tarea bastante común (como pensé).

Hay tres modelos:

class Product < ActiveRecord::Base validates :name, presence: true has_many :categorizations has_many :categories, :through => :categorizations accepts_nested_attributes_for :categorizations end class Categorization < ActiveRecord::Base belongs_to :product belongs_to :category validates :description, presence: true # note the additional field here end class Category < ActiveRecord::Base validates :name, presence: true end

Mis problemas comienzan cuando se trata del formulario Nuevo producto / edición.

Al crear un producto, necesito verificar las categorías (a través de las casillas de verificación) a las que pertenece. Sé que se puede hacer al crear casillas de verificación con un nombre como ''producto [category_ids] []''. Pero también necesito ingresar una descripción para cada una de las relaciones marcadas que se almacenarán en el modelo de unión (Categorización).

Vi esos hermosos Railscasts en formas complejas, habtm checkboxes, etc. He estado buscando StackOverflow apenas. Pero no he tenido éxito.

Encontré un post que describe casi exactamente el mismo problema que el mío. Y la última respuesta tiene sentido para mí (parece que es el camino correcto a seguir). Pero en realidad no está funcionando bien (es decir, si falla la validación). Quiero que las categorías se muestren siempre en el mismo orden (en formularios nuevos / de edición; antes / después de la validación) y que las casillas de verificación permanezcan donde estaban si falla la validación, etc.

Cualquier cosa apreciada. Soy nuevo en Rails (cambiando de CakePHP), así que sea paciente y escriba lo más detallado posible. Por favor, apúntame de la manera correcta!

Gracias. :)


Acabo de hacer lo siguiente. Funcionó para mí ..

<%= f.label :category, "Category" %> <%= f.select :category_ids, Category.order(''name ASC'').all.collect {|c| [c.name, c.id]}, {} %>


use accepts_nested_attributes_for para insertar en la intermediate table es decir, el formulario de vista de categorizations se verá como

# make sure to build product categorizations at controller level if not already class ProductsController < ApplicationController before_filter :build_product, :only => [:new] before_filter :load_product, :only => [:edit] before_filter :build_or_load_categorization, :only => [:new, :edit] def create @product.attributes = params[:product] if @product.save flash[:success] = I18n.t(''product.create.success'') redirect_to :action => :index else render_with_categorization(:new) end end def update @product.attributes = params[:product] if @product.save flash[:success] = I18n.t(''product.update.success'') redirect_to :action => :index else render_with_categorization(:edit) end end private def build_product @product = Product.new end def load_product @product = Product.find_by_id(params[:id]) @product || invalid_url end def build_or_load_categorization Category.where(''id not in (?)'', @product.categories).each do |c| @product.categorizations.new(:category => c) end end def render_with_categorization(template) build_or_load_categorization render :action => template end end

Vista interior

= form_for @product do |f| = f.fields_for :categorizations do |c| %label= c.object.category.name = c.check_box :category_id, {}, c.object.category_id, nil %label Description = c.text_field :description