2010-05-09 12 views
5

Edición de mi pregunta de concisión y actualizar lo que he hecho:¿Cómo uso accept_nested_attributes_for?

¿Cómo modelo que haya varias direcciones para una empresa y asignar una única dirección a un contacto, y ser capaz de asignarlos al crear o editar un contacto?

Quiero usar atributos anidados para poder agregar una dirección al momento de crear un nuevo contacto. Esa dirección existe como su propio modelo porque es posible que desee la opción desplegable de direcciones existentes en lugar de ingresar desde cero.

Parece que no puedo hacer que funcione. Consigo un método no definido `build' para nada: error NilClass

Aquí está mi modelo para lentes de contacto:

class Contact < ActiveRecord::Base 
    attr_accessible :first_name, :last_name, :title, :phone, :fax, :email, :company, 
        :date_entered, :campaign_id, :company_name, :address_id, :address_attributes 

    belongs_to :company 
    belongs_to :address 
    accepts_nested_attributes_for :address 
end 

Aquí es mi modelo de Dirección:

class Address < ActiveRecord::Base 
    attr_accessible :street1, :street2, :city, :state, :zip 

    has_many :contacts 
end 

me gustaría, cuando creando un nuevo contacto, accede a todas las Direcciones que pertenecen a los otros Contactos que pertenecen a la Compañía. Así que aquí es cómo represento Empresa:

class Company < ActiveRecord::Base 
    attr_accessible :name, :phone, :addresses 

    has_many :contacts 

    has_many :addresses, :through => :contacts 

end 

Aquí es como yo estoy tratando de crear un campo en la vista para _form de contacto de manera que, cuando alguien crea un nuevo contacto, que pasan a la dirección con el modelo de Dirección y asociar esa dirección al Contacto:

<% f.fields_for :address, @contact.address do |builder| %> 
    <p> 
    <%= builder.label :street1, "Street 1" %> </br> 
    <%= builder.text_field :street1 %> 
    <p> 
    <% end %> 

Cuando intento Editar, el campo para Calle 1 está en blanco. Y no sé cómo mostrar el valor de show.html.erb.

En la parte inferior es mi consola de errores - parece que no puede crear valores en la tabla de direcciones:

Mi controlador de contactos es como sigue:

def new 
    @contact = Contact.new 
    @contact.address.build # Iundefined method `build' for nil:NilClass 

    @contact.date_entered = Date.today 
    @campaigns = Campaign.find(:all, :order => "name") 
    if params[:campaign_id].blank? 

    else 
     @campaign = Campaign.find(params[:campaign_id]) 
     @contact.campaign_id = @campaign.id 
    end 

    if params[:company_id].blank? 

    else 
     @company = Company.find(params[:company_id]) 
     @contact.company_name = @company.name 
    end 

    end 

    def create 
    @contact = Contact.new(params[:contact]) 
    if @contact.save 
     flash[:notice] = "Successfully created contact." 
     redirect_to @contact 
    else 
     render :action => 'new' 
    end 
    end 

    def edit 
    @contact = Contact.find(params[:id]) 
    @campaigns = Campaign.find(:all, :order => "name") 
    end 

Aquí hay un fragmento de mi consola de errores: estoy fijando el atributo, pero no está creando en la tabla de direcciones ....

Procesamiento ContactsController # cr eate (por 127.0.0.1 en 2010-05-12 21:16:17)

[post] Parámetros: { "cometer" => "Enviar", "authenticity_token" => "d8/gx0zy0Vgg6ghfcbAYL0YtGjYIUC2b1aG + dDKjuSs = ", " contacto "=> {" company_name "=>" Allyforce ", " title "=>" "," campaign_id "=>" 2 ", " atributos_de_dirección "=> {" street1 "=> "abc"}, "fax" => "", "phone" => "", "last_name" => "", "date_entered" => "2010-05-12", "email" = > "", "first_name" => "abc"}}

Empresa Carga (0.0ms) [0m [0mSELECCIONAR * FROM "companies" WHERE ("companies". "Name" = "Allyforce") LIMIT 1 [0m

Dirección Create (16.0 ms) [0m
[0; "direcciones" 1mINSERT en ("ciudad", , "street1" "zip", "created_at", , "estado" "updated_at", "street2") VALUES (NULL, NULL, '2010-05-13 04:16:18 ', NULL, '2010-05-13 04:16:18 ', NULL, NULL) [0m

Contacto Crear (0.0ms) [0m
[ "contactos" 0mINSERT en ("compañía", , "título" "created_at", "updated_at", , "address_id" "campaign_id", "apellidos", "teléfono", "fax", " company_id "," date_entered ", " first_name "," email ") VALUES (NULL, '2010-05-13 04:16:18', '', '2010-05-13 04:16:18', 2, 2, '', '', '', 5, '2010- 05-12' , 'abc', '') [0m

+0

Hmm creo que crea valor añadido en la tabla de direcciones, aquí es donde Dirección Crear (16.0ms) [0m [0; 1mINSERTAR EN "direcciones" ("ciudad", "zip", "created_at", "street1 "," updated_at "," street2 "," state ") VALUES (NULL, NULL, '2010-05-13 04:16:18', NULL, '2010-05-13 04:16:18', NULL, NULL) [0m – dombesz

+0

Actualicé mi publicación, incluí el código en las vistas. – dombesz

+0

Puede ver que en Dirección Crear los valores para street1 están en blanco ... no "abc" .... – Angela

Respuesta

3

¿Simplemente una pregunta banal, si usa esto en su formulario de contacto no debe ser la dirección en singular?

<% f.fields_for :address, @contact.address do |builder| %> 
    <p> 
    <%= builder.label :street1, "Street 1" %> </br> 
    <%= builder.text_field :street1 %> 
    <p> 
<% end %> 

En su acción tiene que ver también

@ycontact.build_address 

Si nos fijamos en el código fuente de su campo de entrada debe ser similar.

< input type = "text" size = "30" name = "contacto [address_attributes] [street1]" id = "contact_address_attributes_street1">

creo que también tiene problemas con sus asociaciones, si almacena address_id en contacto a continuación

class Contact < ActiveRecord::Base 
    belongs_to :address 
    accepts_nested_attributes_for :address 
end 

class Address < ActiveRecord::Base 
    attr_accessible :street1 
    has_many :contacts 
end 

y creo que este es su caso, ya que desea asignar una dirección a varios contactos.

También se puede hacer en inversa, si almacenar su contact_id en su modelo de dirección:

class Contact < ActiveRecord::Base 
    has_one :address 
    accepts_nested_attributes_for :address 
end 

class Address < ActiveRecord::Base 
    attr_accessible :street1 
    belongs_to :contact 
end 

En este caso no se puede tener usuarios con la misma dirección.

actualización

En su usuario show.html.eb puede utilizar

<%= @contact.address.street1 %> 

En show.html.erb de su empresa puede visualizar los datos como:

<% @company.addresses.each do |address| %> 
    <%= address.street1 %> 
    ... 
<% end %> 

En su página de edición de usuario, los campos del formulario deben ser los mismos que en new.html.erb, simplemente debe verse la etiqueta del formulario:

<% form_for @contact do |form| %> 
    <%=render :partial=>'fields' %> 
<% end %> 

Los campos parciales contiene todos sus fieds togheter con el:

<% f.fields_for :address, @contact.address do |builder| %> 
    <p> 
    <%= builder.label :street1, "Street 1" %> </br> 
    <%= builder.text_field :street1 %> 
    <p> 
<% end %> 

Esto debería funcionar.

Actualización 2

Puede acceder a todas las direcciones que pertenecen a una empresa con:

@company.addresses 

Si tiene

class Company<Activerecord::Base 
    has_many :contacts 
    has_many :addresses, :through=>:contacts 
end 

Acerca de las corrientes de aire, o direcciones predeterminadas. Puede tener diferentes opciones según sus necesidades. Yo quiero la opción de que un contacto pueda seleccionar una de las direcciones existentes de la base de datos. Usted puede hacer una caja de selección en su formulario de contacto como:

<%= form.select :address_id, options_from_collection_for_select(@company.addresses, 'id', 'street1')%> 

También se puede usar algo de JavaScript de lujo para añadir o quitar el formulario de dirección, en ese caso, si el usuario elige una de las direcciones existentes. En este caso, debe eliminar los atributos de dirección del formulario. Sugiero crear un enlace con el código de JavaScript que agrega los atributos de dirección y quita (estilo de alternar).

+0

Bueno, quiero tener usuarios que tengan la misma dirección. Pero hay varias direcciones posibles ... imagine una compañía grande y distribuida con diferentes empleados. Entonces, esto funcionaría ... esto está usando: a través =>: contactos, ¿verdad? – Angela

+0

también ... does accepts_nested_attributes_for: address work when Contact belongs_to: address? Aparece Obtengo un error ... Creo que solo funciona para has_many o has_one .... Actualicé mi pregunta para simplificar la pregunta – Angela

+0

El accept_nested_attributes_for funciona también con belongs_to, lo estoy usando en este momento. El has_many: through no importa hasta que desee crear las direcciones a través del modelo de la compañía. También doesnt realmente tiene sentido. – dombesz

1

Si cada dirección pertenece a un solo contacto, (como una casa), que podría hacer:

class Company < ActiveRecord::Base 
    has_many :contacts 
end 

class Contact < ActiveRecord::Base 
    belongs_to :company 
    has_one :address 
end 

class Address < ActiveRecord::Base 
    belongs_to :contact 
end 

Por supuesto, puede mover las columnas de dirección a la tabla de contactos y deshacerse del modelo de direcciones por completo.

Si cada dirección tiene varios contactos (es decir, las direcciones son instalaciones de la empresa donde trabajan los contactos):

class Company < ActiveRecord::Base 
    has_many :contacts 
end 

class Contact < ActiveRecord::Base 
    belongs_to :company 
    belongs_to :address 
end 

class Address < ActiveRecord::Base 
    has_many :contacts 
end 

Usted puede agregar una asociación de has_many :through para obtener company.addresses:

class Company < ActiveRecord::Base 
    has_many :contacts 
    has_many :addresses, :through => :contacts 
end 

En tanto situaciones los componentes de la dirección (calle, ciudad, estado, etc.) son columnas en la tabla de direcciones.

La última parte de su pregunta realmente depende de la distribución de datos; si tiene solo unas pocas direcciones, simplemente puede pegarlas en un elemento seleccionado con la dirección principal predeterminada. Con más direcciones puede necesitar un formulario AJAXified con autocompletar que busque en la tabla de direcciones.

Para obtener los nuevos contactos/direcciones en sus tablas, es posible que desee ver los formularios anidados. Railscast 196 es una buena introducción.

EDITAR - más sobre los atributos anidados

accepts_nested_attributes_for debería funcionar ya sea con un assocation has_many o has_one, y debería funcionar con múltiples niveles de anidamiento. Parece que usted quiere tener un contacto pertenecer a una empresa, y una dirección de pertenecer a un contacto, en cuyo caso:

class Company < ActiveRecord::Base 
    has_many :contacts 
    accepts_nested_attributes_for :contact 
end 

class Contact < ActiveRecord::Base 
    belongs_to :company 
    has_one :address # or has_many, if you prefer 
end 

class Address < ActiveRecord::Base 
    belongs_to :contact 
end 

A continuación, en la vista de uso form_for y fields_for como se describe en el Railscast para obtener el elementos de forma anidados. Esto puede ser un poco complicado para hacerlo bien si su forma es inusual. Hay excelentes ejemplos de ryanb here que cubren varias situaciones típicas.

+0

¡Hola! ¿Cómo sería esto diferente del uso de polimórficos, como se describe en Rails Recipes? De hecho, cada dirección tiene múltiples contactos potencialmente; y entonces cada compañía tiene múltiples direcciones ... gracias. – Angela

+0

Hola, no parece que pueda usar un accept + _nested_attribute_for: en el modelo Contacts como accept_nested_attributes_for: addresses porque es una asociación belongs_to. Miré a la API y estoy intentando has_one ... ¿pero suena bien? – Angela

+0

Las asociaciones polimórficas son útiles cuando desea compartir un modelo (como Dirección) con otros modelos (como Compañía y Persona, por ejemplo). Una empresa puede tener una dirección y una persona puede tener una dirección, por lo que tiene sentido reutilizar el modelo si el formato de la dirección es el mismo. Sin polimorfismo, necesitaría modelos separados de dirección de la empresa y dirección de la persona. Editaré la respuesta para responder a su otro comentario. – zetetic

0

Recomendaría dejar que el Contacto tenga muchas direcciones. Sucede de todos modos en la naturaleza.Si decidido por una sola, que sería has_one :address

Ah, y que debe downcase ellos: has_many :Contacts debe haber has_many :contacts

En el modelo de dirección se hace referencia con (base de datos necesita un campo addressable_id y addressable_type)

belongs_to :addressable, :polymorphic => true 
+0

¿Cómo se agregaría la dirección a un nuevo contacto para que esté disponible para los nuevos contactos futuros que formen parte de esa empresa? – Angela

1

Dado que desea que una dirección pertenezca tanto a una empresa como a un contacto, tiene dos opciones. Si no va a necesitar varias direcciones (lo que podría recomendar que no se asuma), puede colocar una "belongs_to: address" en Company and Contact. La dirección tendría entonces un "has_many: companies" y "has_many: contacts".

La mejor opción que tiene más sentido es usar asociaciones polimórficas, donde la tabla de direcciones tendría: columnas de tipo direccionable y: columnas direccionables para vincular de nuevo a cualquier modelo.

class Company < ActiveRecord::Base 
    attr_accessible :name, :phone, :addresses 

    has_many :contacts 
    accepts_nested_attributes_for :contacts 

    has_many :addresses, :as => :addressable 
    accepts_nested_attributes_for :addresses 

end 

class Contact < ActiveRecord::Base 
    attr_accessible :first_name, :last_name, :title, :phone, :fax, :email, :company, 
        :date_entered, :campaign_id, :company_name 

    belongs_to :company 
    has_many :addresses, :as => :addressable 
    accepts_nested_attributes_for :addresses 
end 

class Address < ActiveRecord::Base 
    attr_accessible :street1 

    belongs_to :addressable, :polymorphic => true 
end 

Ahora su problema viene con el deseo de tener una asociación sencillo "company.addresses" para conseguir tanto su dirección y los de sus contactos. Es posible que recomiende mantenerse alejado de esto y usar un enfoque diferente.

Vas a querer asignar las direcciones de la vista en el registro correcto, por lo que sería mejor para separarlos:

<% form_for @company do |company_form| %> 
    <% company_form.fields_for :addresses do |address_form| %> 
    <%= address_form.text_field :street1 %> 
    <% end %> 
    <% company_form.fields_for :contacts do |contact_form| %> 
    <% contact_form.fields_for :addresses do |contact_address_form| %> 
     <%= contact_address_form.text_field :street1 %> 
    <% end %> 
    <% end %> 
<% end %> 

su tabla de direcciones tiene las columnas polimórficos como este:

class CreateAddresses < ActiveRecord::Migration 
    def self.up 
    create_table :addresses do |t| 
     t.string :street1 
     t.string :addressable_type 
     t.integer :addressable_id 
     t.timestamps 
    end 
    end 

    def self.down 
    drop_table :addresses 
    end 
end 

No estoy seguro de cómo puede mantener todos los enlaces en línea recta si no anida los registros individualmente de esta manera.

Si necesita obtener todas las direcciones de una empresa para fines de visualización, puede utilizar las manipulaciones enumerables ruby ​​estándar como recopilar para reunirlas.

¡Buena suerte!

+0

Hola, todavía estoy tratando de decidir si usar polimórficos ...conceptualmente, la empresa tiene direcciones: a través de contactos ... en otras palabras, la única razón por la que tendría una dirección asociada a una empresa es porque un contacto pertenece a ella. ¿Qué piensa usted de eso? – Angela

+0

Sí, necesito ver todas las direcciones de la compañía ... permitirá al usuario seleccionar una dirección potencial al agregar un nuevo contacto ... ¿Eso significaría no usar polimorfismo? – Angela

+0

El polimorfismo tiene sentido ya que, a medida que su aplicación crezca, es posible que desee asociar direcciones con otros objetos. Si no prevés esto, no es necesario. Si no usa el polimorfismo, puede usar has_many: through, pero solo con fines de visualización. Aún necesitará usar los formularios anidados para acceder a un contacto específico para agregar/modificar direcciones para los contactos de una empresa. P.S. ¿Esto fue lo suficientemente útil para votar? – aceofspades

Cuestiones relacionadas