2009-12-04 6 views
11

Estoy aprendiendo RoR y tratando de usar accepts_nested_attributes_for y has_and_belongs_to_ many para enviar información que tradicionalmente sería de dos formas. He leído en algunos sitios que son compatibles, algunos sitios no son compatibles y algunos sitios no lo saben. Como referencia, estoy usando Rails 2.3.4. He intentado modelar mi solución del tutorial Recortes del Ryan en nested modelsIntentando usar accepts_nested_attributes_for y has_and_belongs_to_many pero la tabla de unión no se está completando

Por lo que he tratado de depuración, parece que tengo dos problemas, pero no estoy seguro de por qué.

  1. Cuando envío un formulario con modelos anidados, solo se publica una parte de la información del modelo anidado. Solo obtengo el primer campo, no la "n" que el usuario puede haber seleccionado
  2. Del único campo que se publica, no hay filas insertadas en la tabla de unión que creé para la relación HABTM.

Aquí es una pieza de código y los registros correspondientes para mi intento de inserción:

Fiscal Modelo:

class Attorney < ActiveRecord::Base 
    has_and_belongs_to_many :associations 
    accepts_nested_attributes_for :associations, :reject_if => proc { |a| a['name'].blank? } 
end 

Asociación Modelo:

class Association < ActiveRecord::Base 
    has_and_belongs_to_many :attorneys 
    accepts_nested_attributes_for :attorneys 
    validates_presence_of :name, :message => "Please enter an association name." 
end 

Controlador de abogados:

def new 
    @attorney = Attorney.new 
    @attorney.associations.build 

    respond_to do |format| 
    format.html # new.html.erb 
    format.xml { render :xml => @attorney } 
    end 
end 

def create 
    @attorney = Attorney.new(params[:attorney]) 

    respond_to do |format| 
    if @attorney.save 
     flash[:notice] = 'Attorney was successfully created.' 
     format.html { redirect_to(@attorney) } 
     format.xml { render :xml => @attorney, :status => :created, :location => @attorney } 
    else 
     format.html { render :action => "new" } 
     format.xml { render :xml => @attorney.errors, :status => :unprocessable_entity } 
    end 
    end 
end 

del Procurador Vista:

<% form_for(@attorney, :html => {:multipart => true}) do |f| %> 
    <%= f.error_messages %> 
<%= f.label :"First name" %> 
<%= f.text_field :firstname %><br> 

<%= f.label :"Last Name" %> 
<%= f.text_field :lastname %><br> 

<%= f.label :"Attorney Type" %> 
<%= f.collection_select :member_type_id, MemberType.all, :id, :name %><br> 

<%= f.text_area :bio, :cols => 70, :rows => 20 %><br><br> 

<%= f.label :"Attorney Location" %> 
<%= f.collection_select :office_location_id, OfficeLocation.all, :id, :location %><br> 

<div id="associations"> 
     <%= render :partial => 'shared/membership' %> 
</div> 
<%= add_association_link "Add Association" %> 
    <%= f.submit 'Create' %> 
<% end %> 

afiliación parcial:

<div class="association"> 
    <% fields_for :associations do |assoc_form| %> 
    <%= assoc_form.collection_select(:association_id, Association.find(:all), :id, :name, :include_blank => true) %> 

<% = link_to_function "eliminar", "$ (this) .up ('asociación'.) Eliminar()". %> <% = link_to 'Nueva Asociación', new_association_path%> extremo <% Hel%>

Fiscal por Link:

def add_association_link(name) 
    link_to_function name do |page| 
    page.insert_html :bottom, :associations, :partial => 'shared/membership', :object => AssociationsAttorneys.new 
    end 
end 

Únete tabla de migración: la captura

class CreateAssociationsAttorneys < ActiveRecord::Migration 
    def self.up 
    create_table :associations_attorneys do |t| 
     t.references :attorney, :null => false 
     t.references :association, :null => false 
     t.timestamps 
    end 
    end 

    def self.down 
    drop_table :associations_attorneys 
    end 
end 

Log:

Processing AttorneysController#new (for 127.0.0.1 at 2009-12-04 08:16:19) [GET] 
Rendering template within layouts/default 
Rendering attorneys/new 
    [4;35;1mMemberType Load (0.4ms)[0m [0mSELECT * FROM "member_types" [0m 
    [4;36;1mOfficeLocation Load (18.6ms)[0m [0;1mSELECT * FROM "office_locations" [0m 
    [4;35;1mAssociation Load (0.6ms)[0m [0mSELECT * FROM "associations" [0m 
Rendered shared/_membership (3.5ms) 
    [4;36;1mCACHE (0.0ms)[0m [0;1mSELECT * FROM "associations" [0m 
Rendered shared/_membership (1.5ms) 
Rendered shared/_nav (0.6ms) 
Rendered shared/_footer (0.1ms) 
Completed in 149ms (View: 114, DB: 20) | 200 OK [http://localhost/attorneys/new] 

Processing ApplicationController#index (for 127.0.0.1 at 2009-12-04 08:16:19) [GET] 

Processing AttorneysController#create (for 127.0.0.1 at 2009-12-04 08:16:57) [POST] 
    Parameters: {"commit"=>"Create", "authenticity_token"=>"Jh7aMCcOY7jUu/D1YtiCswg2n6iwqnS98VnVn46psp0=", "associations"=>{"association_id"=>"3"}, "attorney"=>{"birthstate"=>"Alabama", "office_location_id"=>"1", "birthdate"=>"December 3, 2009", "birthcity"=>"Test", "middlename"=>"Test", "lastname"=>"Testing", "image_temp"=>"", "member_type_id"=>"2", "firstname"=>"Test", "bio"=>"testing testing testing", "suffix"=>"", "email"=>"[email protected]"}} 
    [4;35;1mAttorney Load (15.6ms)[0m [0mSELECT "attorneys".id FROM "attorneys" WHERE ("attorneys"."email" = '[email protected]') LIMIT 1[0m 
    [4;36;1mAttorney Create (0.8ms)[0m [0;1mINSERT INTO "attorneys" ("birthstate", "created_at", "birthdate", "office_location_id", "birthcity", "updated_at", "middlename", "lastname", "firstname", "member_type_id", "suffix", "bio", "image", "email") VALUES('Alabama', '2009-12-04 15:16:57', 'December 3, 2009', 1, 'Test', '2009-12-04 15:16:57', 'Test', 'Testing', 'Test', 2, '', 'testing testing testing', NULL, '[email protected]')[0m 
Redirected to http://localhost:3000/attorneys/11 
Completed in 150ms (DB: 16) | 302 Found [http://localhost/attorneys] 

puedo ver que las asociaciones "=> {" association_id "=>" 3" } se solo está obteniendo la última de las múltiples asociaciones que tuve para la persona en particular y no está creando ninguna entrada en la tabla de unión. ¿Dónde podría haber salido mal mi código?

Respuesta

27

Ustedes dos tienen problemas aquí, desafortunadamente uno de ellos está enmascarado por el otro.

Ambos problemas se derivan de esta parte de la vista:

<div class="association"> 
    <% fields_for :associations do |assoc_form| %> 
    <%= assoc_form.collection_select(:association_id, Association.find(:all), 
     :id, :name, :include_blank => true) %> 

Problema 1: Usted ha entendido mal lo que hace accept_nested_fields_for.

accepts_nested_fields_for se utiliza para crear y modificar objetos relacionados en un formulario. Se puede usar para completar la tabla de unión, que es una especie de lo que estás tratando de hacer. Sin embargo, usar accepts_nested_fields_for para completar la tabla de unión es imposible con una relación HABTM. Un buen uso de accepts_nested_fields_for sería si quisiera crear una nueva asociación que se vinculará con el nuevo abogado. O si tenía un modelo de unión enriquecida que requería información adicional para cada registro.

Problema 2: No está enlazando los campos de esta forma con el formulario del abogado. Lo cual es necesario para usar accepts_nested_fields_for.

Ya hemos establecido que accept_nested_fields_for no es lo que necesita para lograr esto, pero aún no está asociando el campo select association_id con el formulario. Por eso se estableció params [associations] [association_id] y no params [attorney] [associations] [association_id].

Problema 3: La estructura del formulario está mal para lo que parece que estás tratando de lograr.

Hay demasiadas cosas que deben corregirse para darme un buen descanso. Es mejor que revises el complex-forms-example repository. Es un ejemplo práctico de accept_nested_attributes_for, no trata con ninguna relación HABTM, pero debe enseñarle todo lo que necesita saber. El código corregido a continuación es el 90% de lo que necesita. Los complejos-formularios-ejemplos vinculados anteriormente le enseñarán lo que necesita saber para completar los espacios en blanco que son add_association_link y create_association_link.

La corrección implica los siguientes pasos:

  1. crear un modelo de combinación, y cambiar la relación de a tiene muchos a través de uno, aceptando atributos anidados en el modelo unirse.
  2. Haga un pequeño ajuste en el controlador, en términos de cosas.
  3. Pase el objeto del generador de formularios al parcial.
  4. Vuelva a escribir el formulario en el parcial para que se centre en el modelo de unión recién creado.

Puede lograrlo con los siguientes cambios.

class Attorney < ActiveRecord::Base 
    has_many :attorney_associations 
    has_many :associations, :through => :attorney_associations 

    accepts_nested_attributes_for :attorney_associations, :reject_if => proc { |a| 
    a['association_id'].blank? } 
    accepts_nested_attributes_for :associations, :reject_if => proc {|a| 
    a['name'].blank?} 
end 

class AttorneyAssociations < ActiveRecord::Base 
    belongs_to :attorney 
    belongs_to :association 
end 

Controlador Fiscal:

def new 
    @attorney = Attorney.new 
    @attorney.associations.build 
    @attorney.attorney_associations.build 


    respond_to do |format| 
    format.html # new.html.erb 
    format.xml { render :xml => @attorney } 
    end 
end 

nuevo Fiscal Vista:

<% form_for(@attorney, :html => {:multipart => true}) do |f| %> 
    <%= f.error_messages %> 
<%= f.label :"First name" %> 
<%= f.text_field :firstname %><br> 

<%= f.label :"Last Name" %> 
<%= f.text_field :lastname %><br> 

<%= f.label :"Attorney Type" %> 
<%= f.collection_select :member_type_id, MemberType.all, :id, :name %><br> 

<%= f.text_area :bio, :cols => 70, :rows => 20 %><br><br> 

<%= f.label :"Attorney Location" %> 
<%= f.collection_select :office_location_id, OfficeLocation.all, :id, :location %><br> 

<div id="associations"> 
    <% f.fields_for :attorney_association do |aa_form| %> 
    <%= render :partial => 'attorney_association', :locals => {:f => aa_form} %> 
    <% end %> 
    <%= add_association_link "Add Another Existing Association" %> 
    <% f.fields_for :associations do |assoc_form| %> 
    <%= render :partial => 'attorney', :locals => {:f => assoc_form} %>  
    <%= create_association_link, "Create a New Association for this Attorney" %> 
</div> 



<%= f.submit 'Create' %> 
<% end %> 

Estoy asumiendo que add_association_link es un ayudante Javascript que crea un enlace para clonar un vacío ejemplo de lo que era la membresía parcial. create_association_link es un marcador de posición para un ayudante similar que agregará un parcial para una nueva asociación.

Fiscal Asociación parcial:

<div class="attorney_association"> 
    <%= f.collection_select(:association_id, Association.find(:all), 
     :id, :name, :include_blank => true) %> 
    <%= link_to_function "remove", "$(this).up('.attorney_association').remove()" %> 
    </div> 

Asociación parcial:

<div class="association"> 
    <%= f.label_for :name %> 
    <%= f.text_field :name %> 
    <%= link_to_function "remove", "$(this).up('.attorney_association').remove()" %> 
    </div> 
+0

Cambiando el fin de pasar en forma 'f', ahora estoy recibiendo el error: no definido método 'association_id 'para # en esta línea en el parcial: <% = assoc_form.collection_select (: association_id, Association.find (: all),: id ,: nombre,: include_blank => verdadero)%>. ¿Algunas ideas? – davidstites

+0

Parece que pasé por alto una cantidad de otros problemas que estaba teniendo. Actualicé la solución para abordar esos también. – EmFi

+0

, así que he trabajado en esto durante varias semanas y sigo teniendo problemas. cuando intenta convertir a la asociación de abogados en parcial, se queja de no saber qué es 'f' ... pensé que el objeto de formulario se había pasado con el hash de los locales: ¿hash? – davidstites

Cuestiones relacionadas