Rails 3.2.1 вложенная форма и acceptpts_nested_attributes_for возвращает:: клиент (xxxxx) ожидается, получил массив (xxxxx) " - PullRequest
2 голосов
/ 02 марта 2012

Пожалуйста, помогите мне понять, где я ошибаюсь, чтобы я получал это сообщение при попытке сохранить форму ввода данных (сложную форму) после ввода всех данных?

У меня есть пять моделей следующим образом:

class Contract < AR::Base
  has_many :clientlines
  has_many :codelines
  has_many :clients, :through => :clientlines
  has_many :codes, :through => :codelines

  accepts_nested_attributes_for :clientlines
end

class Clientline < AR::Base
  belongs_to :contract
  belongs_to :client

  accepts_nested_attributes_for :contract
end

class Client < AR::Base
  has_many :clientlines
  has_many :contracts, :through => :clientlines
end

class Codeline < AR::Base
  belongs_to :contract
  belongs_to :code
  units_alloc

  accepts_nested_attributes_for :code
end

class Code < AR::Base
  has_many :codelines
  has_many :contracts, :through => :codelines
end

В качестве источника дизайна я использовал следующую статью:

http://rubysource.com/complex-rails-forms-with-nested-attributes/

В моем app / controller / contract_controller.rb у меня есть следующее:

def new
  @contract = Contract.new
  4.times { @contract.codes.build }
  4.times { @contract.codelines.build }
end

def create
  @contract = Contract.new(params[:contract])
  if @contract.save
    flash[:success] = "New Contract has been saved"
    redirect_to @contract # this redirects to the contract show page
  else
    @title = "You have some errors"
    render 'new'
  end
end
.
.
.
end

Я сложил сложную форму следующим образом:

- provide(:title, 'Add Contract')
%h2 New Contract
=form_for(@contract) do |f|
  =render 'shared/contract_error_messages', object: f.object
  =render 'fields', f:  f
  .actions
    = f.submit "Save", class: 'save strong round'

частичные поля _ *: 1015 *

<fieldset><legend>Enter Contract Details</legend>
.field
  = f.label :name, "AuthNum"
  %br/
  = f.text_field  :authnum, :size => 10, :class => "ui-state-default"
.field
  = f.label :name, "Start Date"
  %br/
  = f.text_field  :st_date, :size => 12, :class => "ui-state-default"
.field
  = f.label :name, "End Date"
  %br/
  = f.text_field  :end_date, :size => 12, :class => "ui-state-default"
</fieldset>
<fieldset><legend>Enter Client Details</legend>
= f.fields_for :clients do |ff|
  .field
    = ff.label :name, "First Name"
    %br/
    = ff.text_field :f_name, :size => 15, :class => "ui-state-default"
  .field
    = ff.label :name, "MI"
    %br/
    = ff.text_field :mi, :size => 3, :class => "ui-state-default"
  .field
     = ff.label :name, "Last Name"
     %br/
     = ff.text_field :l_name, :size => 15, :class => "ui-state-default"
  .field
     = ff.label :name, "Birth Date"
     %br/
     = ff.text_field :birth_date, :size => 12, :class => "ui-state-default"
  .field
     = ff.label :name, "Address1"
     %br/
     = ff.text_field :address1, :size => 25, :class => "ui-state-default"
  .field
     = ff.label :name, "Address2"
     %br/
     = ff.text_field :address2, :size => 25, :class => "ui-state-default"
  .field
     = ff.label :name, "City"
     %br/
     = ff.text_field :city, :size => 15, :class => "ui-state-default"
  .field
     = ff.label :name, "ZipCode"
     %br/
     = ff.text_field :zip_code, :size => 10, :class => "ui-state-default"
  .field
     = ff.label :name, "State"
     %br/
     = ff.text_field :state, :size => 15, :class => "ui-state-default"
  .field
     = ff.label :name, "MedicareNum"
     %br/
     = ff.text_field :medicarenum, :size => 20, :class => "ui-state-default"
  .field
     = ff.label :name, "MedicaidNum"
     %br/
     = ff.text_field :medicaidnum, :size => 20, :class => "ui-state-default"
  .field
     = ff.label :name, "MemberNum"
     %br/
     = ff.text_field :membernum, :size => 20, :class => "ui-state-default"
  .field
     = ff.label :name, "SocSerCareMgr"
     %br/
     = ff.text_field :socsercaremgr, :size => 20, :class => "ui-state-default"
  .field
     = ff.label :name, "SSCM_Ph"
     %br/
     = ff.text_field :sscm_ph, :size => 15, :class => "ui-state-default"
  .field
     = ff.label :name, "NurseCareMgr"
     %br/
     = ff.text_field :nursecaremgr, :size => 20, :class => "ui-state-default"
  .field
     = ff.label :name, "NCM_Ph"
     %br/
     = ff.text_field :ncm_ph, :size => 15, :class => "ui-state-default"
  .field
     = ff.label :name, "EmergencyContact"
     %br/
     = ff.text_field :emergencycontact, :size => 20, :class => "ui-state-default"
  .field
     = ff.label :name, "EC_Ph"
     %br/
     = ff.text_field :ec_ph, :size => 15, :class => "ui-state-default"
  .field
     = ff.label :name, "PrimaryCarePhy"
     %br/
     = ff.text_field :primarycarephy, :size => 20, :class => "ui-state-default"
  .field
     = ff.label :name, "PCPhy_Ph"
     %br/
     = ff.text_field :pcphy_ph, :size => 15, :class => "ui-state-default"
</fieldset>
<fieldset><legend>Enter Billing Code Details</legend>
= f.fields_for :codes do |ff|
  .field
    = ff.label :name, "Code Name"
    %br/
    = ff.text_field :code_name, :size => 15, :class => "ui-state-default"
  .field
    = ff.label :name, "Status"
    %br/
    = ff.text_field :status, :size => 10, :class => "ui-state-default"
  .field
    = ff.label :name, "Description"
    %br/
    = ff.text_field :description, :size => 25, :class => "ui-state-default"
= f.fields_for :codelines do |ff|
  .field
    = ff.label :name, "Units Alloc"
    %br/
    = ff.text_field :units_alloc, :precision => 6, :scale => 2, :size => 10, :class => 
    "ui-state-default"
</fieldset>

Моя первая и непосредственная проблема заключается в том, что, как только я ввел все данные в форму, а затем нажал кнопку «Сохранить», я получил следующее:

ActiveRecord :: Ассоциация TypeMisMatch в ContractsController # Создать. Ожидается, что клиент (#xxxxxx) получил массив (#xxxxxx).

Другая проблема состоит в том, что, если я включу 'accepts_nested_attributes_for: codelines' в мою модель контракта, атрибут 'units_alloc' исчезнет из моей формы.

Любая помощь или руководство будут наиболее ценными по этим двум вопросам. Я потратил некоторое время на чтение сложных форм, наблюдал за трансляциями «сложных форм», прочитал и перечитал Руководства Rails по ассоциациям, а также документацию API по методу acceptpts_nested_attributes_for. Очевидно, что мое понимание этих концепций не дошло до полного понимания, необходимого для решения этих проблем, поэтому я обратился за помощью.

Обновление приложение / контроллеры / contracts_controller.rb

class ContractsController < ApplicationController

def index
  @contracts = Contract.paginate(page: params[:page])
end

def show
  @contract = Contract.find(params[:id])
end

def new
  @contract = Contract.new
  @contract.codes.build
  @contract.codelines.build
  @contract.clients.build
end

def create
  raise params[:contract].to_s ------ **this is line #19**
  @contract = Contract.new(params[:contract])
  if @contract.save
    flash[:success] = "New Contract has been saved"
    redirect_to @contract # this redirects to the contract show page
  else
    @title = "You have some errors"
    render 'new'
  end
end

def edit
  @contract = Contract.find(param[:id])
end

def update
  if @contract.update_attributes(params[:contract])
    flash[:success] = "Contract Profile updated"
    redirect_to @contract
  else
    render 'edit'
  end
end
end

Я добавил «Raise Params [: contract] .to_s» в качестве первой строки в моем действии create в contract_controller.rb, и получился следующий вывод:

RuntimeError in ContractsController#create

{"authnum"=>"700900", "st_date"=>"04/03/2012", "end_date"=>"06/29/2012", "clients"=> 
{"f_name"=>"Lefty", "mi"=>"L", "l_name"=>"Right", "birth_date"=>"07/18/1979", 
"address1"=>"54 Frosty Lane", "address2"=>"", "city"=>"Frave", "zip_code"=>"54806",
"state"=>"WI", "medicarenum"=>"789987456", "medicaidnum"=>"931579135", 
"membernum"=>"890333-3", "socsercaremgr"=>"Caring Serving",
"sscm_ph"=>"1-444-444-4444", "nursecaremgr"=>"Caring Nurse", 
"ncm_ph"=>"1-555-555-5555", "emergencycontact"=>"Quick Response", 
"ec_ph"=>"1-666-666-6666", "primarycarephy"=>"This One", "pcphy_ph"=>"1-777-777-7777"},
"codes"=>{"code_name"=>"S-5463", "status"=>"Active", "description"=>"Transition from
sch to mkt"}, "codelines"=>{"units_alloc"=>"80.00"}}

Rails.root: /home/tom/rails_projects/tracking
Application Trace | Framework Trace | Full Trace

app/controllers/contracts_controller.rb:19:in `create'

Request

Parameters:

{"utf8"=>"✓",
"authenticity_token"=>"/i21h2vwzuDPjIrCXzYEIAg41FnMxfGdCQQggjqcZjY=",
"contract"=>{"authnum"=>"700900",
"st_date"=>"04/03/2012",
"end_date"=>"06/29/2012",
"clients"=>{"f_name"=>"Lefty",
"mi"=>"L",
"l_name"=>"Right",
"birth_date"=>"07/18/1979",
"address1"=>"54 Frosty Lane",
"address2"=>"",
"city"=>"Frave",
"zip_code"=>"54806",
"state"=>"WI",
"medicarenum"=>"789987456",
"medicaidnum"=>"931579135",
"membernum"=>"890333-3",
"socsercaremgr"=>"Caring Serving",
"sscm_ph"=>"1-444-444-4444",
"nursecaremgr"=>"Caring Nurse",
"ncm_ph"=>"1-555-555-5555",
"emergencycontact"=>"Quick Response",
"ec_ph"=>"1-666-666-6666",
"primarycarephy"=>"This One",
"pcphy_ph"=>"1-777-777-7777"},
"codes"=>{"code_name"=>"S-5463",
"status"=>"Active",
"description"=>"Transition from sch to mkt"},
"codelines"=>{"units_alloc"=>"80.00"}},
"commit"=>"Save"}

Обновление 1

Я изменил свое новое действие contract_controller на:

def new
  @contract = Contract.new

Создание объекта кодовых линий по контракту, затем создание кодов через объект кодовых линий

  codelines = @contract.codelines.build
  codelines.codes.build

Создание объекта clientlines с помощью контракта, затем создание клиентов с помощью объекта clientlines

  clientlines = @contract.clientlines.build
  clientlines.clients.build
end

Я также изменил свою модель контракта, добавив accepts_nested_attributes_for: clientlines,: codelines, а также добавив строку attr_accessor.

class Contract < ActiveRecord::Base
  has_many :clientlines
  has_many :codelines
  has_many :clients, :through => :clientlines
  has_many :codes, :through => :codelines

  accepts_nested_attributes_for :clients
  accepts_nested_attributes_for :codes
  accepts_nested_attributes_for :clientlines
  accepts_nested_attributes_for :codelines

  attr_accessor :codes, :clients, :clientlines, :codelines
end

Теперь у меня есть nested_attribute_writers с именами ассоциаций в моих параметрах, но теперь моя ошибка изменилась на:

NoMethodError in ContractsController#new

undefined method `build' for nil:NilClass

Теперь возникает вопрос: "Правильно ли в attr_accessor ссылаться на все ассоциации?" Другой вопрос, который у меня возникает: «Нужно ли использовать form_helper для создания записей для клиента, кода и строки кода?» Причина, по которой я спрашиваю об этом, заключается в том, что, похоже, что несмотря на действие по сборке, которое я имею в моем contract_controller, кажется, что оно все еще равно нулю. Если ответ на второй вопрос положительный, можете ли вы направить меня к какому-нибудь ресурсу, который поможет мне в создании form_helper? Я проверяю RailsGuides.

Спасибо.

Обновление 2

Я ходил кругами, я изменил новое действие contract_controller на:

def new
  @contract = Contract.new

  @contract.codes.build
  @contract.clients.build
end

Я также изменил свою модель контракта обратно на:

class Contract < ActiveRecord::Base
  has_many :clientlines
  has_many :codelines
  has_many :clients, :through => :clientlines
  has_many :codes, :through => :codelines

  accepts_nested_attributes_for :clients
  accepts_nested_attributes_for :codes

  attr_accessible :clients_attributes, :codes_attributes etc
end

Наконец, я удалил раздел в моем представлении, _fields.html.haml, который относится к кодовым строкам, а именно:

= f.fields_for :codelines do |ff| and the next four lines

и теперь в моих параметрах есть необходимые "clients_attributes" и "codes_attributes", и в результате форма сохраняется в соответствующих таблицах. У меня все еще есть некоторые проблемы, а именно дополнительный атрибут в кодовой строке, 'units_alloc' и некоторые другие, но все выглядит лучше.

Ответы [ 2 ]

2 голосов
/ 02 марта 2012

Итак, ваша проблема в способе настройки ваших форм.

Ваша модель contract может принимать вложенные атрибуты для clientlines, однако вы пытаетесь принять вложенные атрибуты для clients, code.

Для этого вам необходимо настроить модель Contract следующим образом:

class Contract < AR::Base
  has_many :clientlines
  has_many :codelines
  has_many :clients, :through => :clientlines
  has_many :codes, :through => :codelines

  accepts_nested_attributes_for :codes
  accepts_nested_attributes_for :clients
end

Мне кажется, я понимаю ваше замешательство, поскольку ClientLines является владельцем codes и clients, то есть отношений, для которых вы должны принять вложенные атрибуты, но это неверно.

Как только вы используете has_many :codes, :through => :clientlines, вы устанавливаете ассоциацию первого класса между Contract и Code, поэтому, если вы хотите создать новый codes в форме contract, вам нужно сказать accepts_nested_attributes_for :codes в вашей Contract модели.

Надеюсь, это поможет.

Итак, что касается вашей последней публикации параметров, проблема заключается в :codes и :clients. Если у вас был код accepts_nested_attributes_for :codes и accepts_nested_attributes_for :clients в вашей модели Contract, это должно работать нормально.

Здесь проблема в том, что для правильной сборки клиентов или кодов вам также необходимо создать родительский объект. Итак, ваш контроллер должен выглядеть примерно так:

class ContractsController < ApplicationController
  def new
    @contract = Contract.new

    #Build the codelines object throught the contract, then build the codes through the codelines object
    codelines =  @contract.codelines.build
    codelines.build_code

    # Do the same for the clients
    clientlines = @contract.clientlines.build
    clientlines.build_client
  end

Теперь, если вы сделали это правильно, вместо того, чтобы видеть :clients или :codes в вашем params, вы должны увидеть :clients_attributes или :codes_attributes.

Я действительно думаю, что должен это сделать.

0 голосов
/ 07 апреля 2012

Первый ответ был очень полезен, но он не помог мне полностью найти решение. Я нашел решение, которое довело меня до конца на этом сайте, отладка nested_forms .

Это девятый пункт, где вы читаете, что если вы работаете с has_many: посредством ассоциации, то вам нужно основывать nested_form на модели соединения или чем-то близком к этому.

Я быстро выполнил тест путем рефакторинга некоторого кода тут и там, и теперь у меня есть работоспособная вложенная форма, которая передает правильные параметры контроллеру, который, в свою очередь, обрабатывает его правильно, и моя таблица кодовых строк теперь имеет только одну запись.

...