Как я могу использовать accepts_nested_attributes_for?
Редактирую мой вопрос для краткости и обновления того, что я сделал:
Как мне смоделировать наличие нескольких адресов для компании и назначить один адрес контакту, и иметь возможность назначать их при создании или редактировании контакта?
Я хочу использовать вложенные атрибуты, чтобы иметь возможность добавить адрес во время создания нового контакта. Этот адрес существует как его собственная модель, потому что я могу захотеть, чтобы опция выпадала из существующих адресов, а не входила с нуля.
Я не могу заставить его работать. Я получаю неопределенный метод `build'для nil: ошибка NilClass
Вот моя модель для контактов:
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
Вот моя модель для адреса:
class Address < ActiveRecord::Base
attr_accessible :street1, :street2, :city, :state, :zip
has_many :contacts
end
Я хотел бы, чтобы при создании нового контакта доступ ко всем адресам, которые принадлежат другим контактам, принадлежащим компании. Вот как я представляю компанию:
class Company < ActiveRecord::Base
attr_accessible :name, :phone, :addresses
has_many :contacts
has_many :addresses, :through => :contacts
end
Вот как я пытаюсь создать поле в представлении для _form для контакта, чтобы, когда кто-то создает новый контакт, он передавал адрес в модель адреса и связывал этот адрес с контактом:
<% f.fields_for :address, @contact.address do |builder| %>
<p>
<%= builder.label :street1, "Street 1" %> </br>
<%= builder.text_field :street1 %>
<p>
<% end %>
Когда я пытаюсь редактировать, поле для улицы 1 остается пустым. И я не знаю, как отобразить значение из show.html.erb.
Внизу моя консоль ошибок - не могу создать значения в таблице адресов:
Контроллер Мои контакты выглядит следующим образом:
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
Вот фрагмент моей консоли ошибок: я помещаю атрибут, но он не создается в таблице адресов....
Обработка ContactsController#create (для 127.0.0.1 в 2010-05-12 21:16:17)
[POST] Параметры: {"commit"=>"Submit", "authenticity_token"=>"d8/gx0zy0Vgg6ghfcbAYL0YtGjYIUC2b1aG+dDKjuSs=", "contact"=>{"company_name"=>"Allyforce", "title"=>"", "campaign_id"=>"2", "address_attributes"=>{"street1"=>"abc"}, "fax"=>"", "phone"=>"", "last_name"=>"", "date_entered"=>"2010-05-12", "email"=>"", "first_name"=>"abc"}}
Нагрузка компании (0,0 мс)[0 м [0 м ВЫБРАТЬ * ОТ "компаний" ГДЕ ("компании"."Имя" = 'Allyforce') LIMIT 1[0 м
Создание адреса (16,0 мс)[0 мс
[0;1mINSERT INTO "address" ("city", "zip", "creation_at", "street1", "updated_at", "street2", "state") ЗНАЧЕНИЯ (NULL, NULL, '2010-05-13 04:16:18', NULL,' 2010-05-13 04:16:18 ', NULL, NULL)[0 мес.Контакт Создать (0.0ms)[0m
[0mINSERT INTO "контакты" ("компания", "созданный_кат", "заголовок", "обновленный_ат", "идентификатор_п кампании", "адрес_идентификатора", "фамилия_имя", "телефон", "факс", "идентификатор_компании", "дата_включен"), "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
4 ответа
Просто банальный вопрос, если вы используете это в своей контактной форме, не следует обращаться в единственном числе?
<% f.fields_for :address, @contact.address do |builder| %>
<p>
<%= builder.label :street1, "Street 1" %> </br>
<%= builder.text_field :street1 %>
<p>
<% end %>
В вашем действии вы также должны сделать
@ycontact.build_address
Если вы посмотрите на исходный код, ваше поле ввода должно выглядеть следующим образом.
Я думаю, что у вас также есть проблемы с вашими ассоциациями, если вы храните address_id в контакте, то
class Contact < ActiveRecord::Base
belongs_to :address
accepts_nested_attributes_for :address
end
class Address < ActiveRecord::Base
attr_accessible :street1
has_many :contacts
end
И я думаю, что это ваш случай, потому что вы хотите назначить адрес нескольким контактам.
Вы также можете сделать наоборот, если вы храните ваш contact_id в вашей модели адреса:
class Contact < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
end
class Address < ActiveRecord::Base
attr_accessible :street1
belongs_to :contact
end
В этом случае вы не можете иметь пользователей с одинаковым адресом.
Обновить
В вашем пользователе show.html.eb вы можете использовать
<%= @contact.address.street1 %>
В вашей компании show.html.erb вы можете отобразить адреса, такие как:
<% @company.addresses.each do |address| %>
<%= address.street1 %>
...
<% end %>
На вашей странице редактирования пользователя поля формы должны быть такими же, как в new.html.erb, только тег формы должен выглядеть так:
<% form_for @contact do |form| %>
<%=render :partial=>'fields' %>
<% end %>
Частичное поле содержит все ваши друзья вместе с:
<% f.fields_for :address, @contact.address do |builder| %>
<p>
<%= builder.label :street1, "Street 1" %> </br>
<%= builder.text_field :street1 %>
<p>
<% end %>
Это должно работать.
Обновление 2
Вы можете получить доступ ко всем адресам, которые принадлежат компании с:
@company.addresses
Если у вас есть
class Company<Activerecord::Base
has_many :contacts
has_many :addresses, :through=>:contacts
end
О черновиках или адресах по умолчанию. Вы можете иметь различные варианты в зависимости от ваших потребностей. Если вы хотите, чтобы контакт мог выбрать один из существующих адресов в базе данных. Вы можете сделать поле выбора в вашей контактной форме, как:
<%= form.select :address_id, options_from_collection_for_select(@company.addresses, 'id', 'street1')%>
Также вы можете использовать какой-нибудь необычный javascript для добавления или удаления формы адреса, в том случае, если пользователь выбирает один из существующих адресов. В этом случае вы должны удалить address_attributes из формы. Я предлагаю создать ссылку с кодом javascript, который добавляет и удаляет address_attributes (стиль переключения).
Если каждый адрес принадлежит только одному контакту (как домашний адрес), вы можете сделать:
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
Конечно, вы можете просто переместить столбцы адресов в таблицу контактов и вообще избавиться от модели адресов.
Если у каждого адреса есть несколько контактов (то есть, адреса являются объектами компании, где работают контакты):
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
Вы можете добавить has_many :through
ассоциация, чтобы получить company.addresses
:
class Company < ActiveRecord::Base
has_many :contacts
has_many :addresses, :through => :contacts
end
В обеих ситуациях компоненты адреса (улица, город, штат и т. Д.) Являются столбцами в таблице адресов.
Последняя часть вашего вопроса действительно зависит от распределения данных - если у вас есть только несколько адресов, вы можете просто вставить их в элемент select с основным адресом по умолчанию. При наличии большего количества адресов вам может понадобиться форма AJAXified с автозаполнением для поиска в таблице адресов.
Чтобы получить новые контакты / адреса в ваших таблицах, вы можете посмотреть на вложенные формы. Railscast 196 - хорошее введение.
РЕДАКТИРОВАТЬ - больше о вложенных атрибутах
accepts_nested_attributes_for
должен работать либо с has_many
или же has_one
ассоциация, и должна работать с несколькими уровнями вложенности. Кажется, вы хотите, чтобы Контакт принадлежал Компании, а Адрес - Контакту, и в этом случае:
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
Тогда в представлении использовать form_for
а также fields_for
как описано в Railscast, чтобы получить вложенные элементы формы. Это может быть немного сложно сделать правильно, если ваша форма необычна. Здесь есть отличные примеры от ryanb, которые охватывают несколько типичных ситуаций.
Поскольку вы хотите, чтобы адрес принадлежал как компании, так и контакту, у вас есть два варианта. Если вам никогда не понадобится несколько адресов (что я могу рекомендовать против предположения), тогда вы можете указать "own_to:address" как в Company, так и в Contact. Адрес будет иметь как has_many: companies, так и has_many: контакты.
Лучшим вариантом, который имеет больше смысла, является использование полиморфных ассоциаций, где таблица адресов будет иметь столбцы: addressable_type и: addressable_id для ссылки на любую модель.
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
Теперь ваша проблема связана с желанием иметь единую ассоциацию "company.addresses", чтобы получить и ее адрес, и адреса своих контактов. Я мог бы рекомендовать держаться подальше от этого и использовать другой подход.
Вы захотите сопоставить эти адреса в представлении с правильной записью, поэтому лучше их разделить:
<% 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 %>
Ваша адресная таблица имеет полиморфные столбцы, подобные этому:
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
Я не уверен, как вы могли бы сохранить все ссылки прямыми, если вы не вкладываете записи по отдельности, как это.
Если вам нужно получить все адреса компании для демонстрации, вы можете использовать стандартные перечисляемые манипуляции ruby, например, сбор, чтобы собрать их вместе.
Удачи!
Я бы рекомендовал контакту иметь много адресов. Это происходит в любом случае в дикой природе. Если бы решился только на один, это было бы has_one :address
Ох, и вы должны прекратить их has_many :Contacts
должно быть has_many :contacts
В модели адреса, на которую вы ссылаетесь (база данных нуждается в addressable_id
а также addressable_type
поле)
belongs_to :addressable, :polymorphic => true