简体   繁体   English

Rails 4具有simple_form_for,has_many和嵌套属性

[英]Rails 4 with simple_form_for, has_many through and nested attributes

I'm in need of functionality virtually identical to this: 我实际上需要与此功能相同的功能:

https://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through https://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through

and I've been going around and around (and around) trying to get it to function properly, but keep running into obstacles. 而且我一直在四处寻找(使它正常运行),但一直遇到障碍。 I'm still somewhat new to Ruby and Rails and am in need of assistance in moving forward. 我还是Ruby和Rails的新手,在前进方面需要帮助。 Here is my implementation as it currently exists: 这是我目前的实现:

/models/transfer.rb

class Transfer < ActiveRecord::Base

  validates :name, presence:   true,
                   uniqueness: { case_sensitive: false }

  has_many  :transfer_accounts, inverse_of: :transfer
  has_many  :accounts,          through:    :transfer_accounts

  accepts_nested_attributes_for :transfer_accounts

end


/models/transfer_account.rb

class TransferAccount < ActiveRecord::Base

  validates :account_transfer_role, presence: true

  belongs_to :account,  inverse_of: :transfer_accounts
  belongs_to :transfer, inverse_of: :transfer_accounts

  validates :account,  presence: true
  validates :transfer, presence: true

  accepts_nested_attributes_for :account

end


/models/account.rb

class Account < ActiveRecord::Base

  validates :name,           presence:   true,
                             uniqueness: { case_sensitive: false }
  validates :user_name,      presence:   true
  validates :password,       presence:   true
  validates :account_number, presence:   true,
                             uniqueness: { case_sensitive: false }
  validates :routing_number, presence:   true

  has_many :transfer_accounts, inverse_of: :account
  has_many :transfers,         through:    :transfer_accounts

  belongs_to :bank, inverse_of: :accounts

end


/models/bank.rb

class Bank < ActiveRecord::Base

  validates :name,        presence:   true,
                          uniqueness: { case_sensitive: false }
  validates :connect_uri, presence:   true

  has_many :accounts

end


/controllers/transfers_controller.rb

class TransfersController < ApplicationController

  def new
    @transfer = Transfer.new
    @transfer.transfer_accounts.build(account_transfer_role: 'source').build_account
    @transfer.transfer_accounts.build(account_transfer_role: 'destination').build_account
    @valid_banks = Bank.all.collect {|c| [c.name, c.id]}  # available banks seeded in database
  end

  def index
    @transfers = Transfer.all
  end

  def show
    @transfer = resource
  end

  def create
    @transfer = Transfer.new(transfer_params)
    if @transfer.save
      redirect_to transfers_path, notice: "Transfer Created"
    else
      redirect_to transfers_path, alert:  "Transfer Not Created"
    end
  end

  def edit
    resource
  end

  def update
    if resource.update_attributes(transfer_params)
      redirect_to transfers_path(resource),     notice: "Transfer Updated"
    else
      redirect_to edit_transfer_path(resource), alert:  "Transfer Not Updated"
    end
  end

  def destroy
    resource.destroy
  end


  private

  def resource
    @transfer ||= transfer.find(params[:id])
  end

  def transfer_params
    params.require(:transfer).
      permit(:name, :description,
             transfer_accounts_attributes:
               [:account_transfer_role,
                account_attributes:
                  [:name, :description, :user_name, :password,
                   :routing_number, :account_number
                  ]
               ])
  end

end


/controllers/banks_controller.rb

class BanksController < ApplicationController

  def index
    @bank = Bank.new
    @banks = Bank.by_last_updated_at
  end

  def show
    @bank = resource
  end

  def create
    @bank = Bank.new(bank_params)
    if @bank.save
      redirect_to banks_path, notice: "Bank Created"
    else
      redirect_to banks_path, alert: "Bank Not Created"
    end
  end

  def edit
    resource
  end

  def update
    if resource.update_attributes(bank_params)
      redirect_to banks_path(resource), notice: "Bank Updated"
    else
      redirect_to edit_bank_path(resource), alert: "Bank Not Updated"
    end
  end

  def destroy
    resource.destroy
  end


  private

  def resource
    @bank ||= Bank.find(params[:id])
  end

  def bank_params
    params.require(:bank).
      permit(:name, :description, :connection_uri)
  end

end


/views/transfers/_form.html.haml

= simple_form_for :transfer do |t|
  .form-inputs

    = t.input :name, label: "Transfer Name"
    = t.input :description, required: false, label: "Transfer Description"

    = t.simple_fields_for :transfer_accounts do |ta|

      - role = ta.object.account_transfer_role.titleize

      = ta.input :account_transfer_role, as: :hidden

      = ta.simple_fields_for :account do |a|

        = a.input :bank_id, collection:    @valid_banks,
                            include_blank: 'Select bank...',
                            id:            'bank',
                            class:         'bank_selector',
                            label:         '#{role} Bank',
                            error:         '#{role} bank selection is required.'

        = a.input :name, label: "#{role} Account Name"
        = a.input :description, required: false, label: "#{role} Account Description"
        = a.input :user_name, label: "#{role} Account User Name"
        = a.input :password, label: "#{role} Account Password"
        = a.input :account_number, label: "#{role} Account Number"
        = a.input :routing_number, label: "#{role} Account Routing Number"

  = t.submit


/db/migrate/20151102001000_create_transfers.rb

class CreateTransfers < ActiveRecord::Migration
  def change
    create_table :transfers do |t|

      t.string :name, null: false, default: ''
      t.text   :description

      t.timestamps

    end
  end
end


/db/migrate/20151102002000_create_transfer_accounts.rb

class CreateTransferAccounts < ActiveRecord::Migration
  def change
    create_table :transfer_accounts do |t|

      t.string :account_transfer_role, null: false, default: ''

      t.references :transfer, index: true
      t.references :account,  index: true

      t.timestamps null: false

    end
  end
end


/db/migrate/20151102003000_create_accounts.rb

class CreateAccounts < ActiveRecord::Migration
  def change
    create_table :accounts do |t|

      t.string  :name,           null: false, default: ''
      t.string  :description
      t.string  :user_name,      null: false, default: ''
      t.string  :password,       null: false, default: ''
      t.string  :account_number, null: false, default: ''
      t.string  :routing_number, null: false, default: ''

      t.references :bank, index: true

      t.timestamps

    end
  end
end


/db/migrate/20151102004000_create_banks.rb

class CreateBanks < ActiveRecord::Migration
  def change
    create_table :banks do |t|

      t.string :name,           null: false, default: ''
      t.string :description
      t.string :connection_uri, null: false, default: ''

      t.timestamps

    end
  end
end


/db/migrate/20151102005000_add_foreign_keys_to_transfer_accounts.rb

class AddForeignKeysToTransferAccounts < ActiveRecord::Migration
  def change

    add_foreign_key :transfer_accounts, :accounts
    add_foreign_key :transfer_accounts, :transfers

  end
end


/db/migrate/20151102006000_add_foreign_keys_to_accounts.rb

class AddForeignKeysToAccounts < ActiveRecord::Migration
  def change

    add_foreign_key :accounts, :banks

  end
end


/db/seeds.rb

Bank.create(name:           'Acme Savings and Loan',
            description:    'The number one bank in the northeast',
            connection_uri: 'https://www.acmesavings.com')
Bank.create(name:           'First Bank of Anytown',
            description:    'The first and only bank in Anytown',
            connection_uri: 'https://www.firstbankanytown.com')
Bank.create(name:           'Generibank',
            description:    'The most generic bank in the country',
            connection_uri: 'https://www.generibank.com')


/config/routes.rb

Rails.application.routes.draw do

  resources  :transfers
  resources  :accounts
  resources  :banks
  root to:   'dashboard#index'

end

So, currently my questions are: 所以,目前我的问题是:

  1. This line in the form view - role = ta.object.account_transfer_role.titleize 表单视图中的这一行- role = ta.object.account_transfer_role.titleize
    is giving me an " undefined method `account_transfer_role' for nil:NilClass " error, so what am I doing wrong there? 给我一个nil:NilClass的“ undefined method`account_transfer_role '错误”,那我在做什么错呢?

  2. Why does (or why would) the accepts_nested_attributes_for line in the transfer_account model work? 为什么transfer_account模型中的accepts_nested_attributes_for行有效(或为什么有效)? I was under the impression that accepts_nested_attributes_for doesn't work on the belongs_to side of an association, due to the fact that it is not the parent (or something to that effect). 我的印象是accepts_nested_attributes_for在关联的belongs_to端不起作用,因为它不是父代(或具有某种作用的)。

  3. If I comment out code related to question #1 to avoid that error, the form renders, but I'm only getting one set of input boxes for the account nested attributes. 如果我注释掉与问题#1有关的代码以避免该错误,则会呈现该表单,但我只会得到一组account嵌套属性的输入框。 With each transfer having two transfer_accounts and two accounts built and associated with it in the " new " action of the transfers_controller (with transfer_account :account_transfer_role values of ' source ' and ' destination '), shouldn't I be getting two sets of the account nested attributes input boxes? 随着每个transfer有两个transfer_accounts和两个accounts建立和与之相关的“ ”行动transfers_controller (与transfer_account :account_transfer_role ”和“ 目的地 ”的值),我不应该会得到两套的account嵌套属性输入框?

  4. Are the singulars/plurals correct for my nested attributes? 单数/复数对我的嵌套属性是否正确? Basically, I've kept them consistent, starting from their associations. 从他们的关联开始,基本上让他们保持一致。 For example, transfer has_many :transfer_accounts , so transfer_accounts is plural in all of the following: 例如, transfer has_many :transfer_accounts ,因此transfer_accounts在以下所有方面都是复数的:

     /models/transfer.rb accepts_nested_attributes_for :transfer_accounts /controllers/transfers_controller.rb @transfer.transfer_accounts.build(account_transfer_role: 'source').build_account @transfer.transfer_accounts.build(account_transfer_role: 'destination').build_account . . . def transfer_params params.require(:transfer). permit(:name, :description, transfer_accounts_attributes: [:account_transfer_role, account_attributes: [:name, :description, :user_name, :password, :routing_number, :account_number ] ]) end /views/transfers/_form.html.haml = m.simple_fields_for :transfer_accounts do |ma| 

    The same holds true for account , except as singular. 除奇数外, account也是如此。

  5. If I do step 3, fill out the form with the single set of account attributes provided, then submit, I get a ' No route matches [POST] "/transfers/new ' error. So, obviously something is wrong with my routing. I wasn't sure how transfers and accounts should appear in the routes.rb file. As 如果执行第3步,则使用提供的一组account属性填写表单,然后提交,我将收到“ No route match [POST]“ / transfers / new ”错误。因此,显然我的路由有问题。我不确定transfersaccounts应如何显示在route.rb文件中。

     resources :transfers resources :accounts 

    or as 或作为

     resources :transfers do resources :accounts end 

    or as still some other way. 还是其他方式 Also, I didn't know if transfer_accounts was needed in the routes file as well. 另外,我也不知道route文件中是否也需要transfer_accounts

If you've gotten to this line, thank you for your patience.:>) And any help you can provide would be greatly appreciated. 如果您已到达这一行,谢谢您的耐心。:>)并且,您所能提供的任何帮助将不胜感激。

Cheers, Tim 蒂姆,干杯

The solution turned out to be incredibly simple, resolving each of the issues raised in my questions 1, 3, 4 and 5. 事实证明,该解决方案非常简单,可以解决我在问题1、3、4和5中提出的每个问题。

In the form, I changed this line: 在表单中,我更改了这一行:

= simple_form_for :transfer do |t|

to this: 对此:

= simple_form_for @transfer do |t|

For question 1, account_transfer_role was no longer nil. 对于问题1, account_transfer_role不再为零。
For question 3, it began rendering the input boxes for both transfer_accounts . 对于问题3,它开始为两个transfer_accounts渲染输入框。
For question 4, the singulars and plurals in the strong parameters proved to be correct as shown. 如图所示,对于问题4,强参数中的单数和复数证明是正确的。
For question 5, this version of the routes proved to be correct: 对于问题5,此路线的版本证明是正确的:

resources  :transfers
resources  :accounts

although I simplified it to: 尽管我将其简化为:

resources  :transfers, :accounts

As for question 2, the best answer I come up with is that my understanding appears to have been incorrect and that accepts_nested_attributes_for: does indeed work on the belongs_to side of an association because it is working for me. 至于问题2,我想出的最佳答案是我的理解似乎不正确,并且accepts_nested_attributes_for:确实在关联的belongs_to方面起作用,因为它对我belongs_to

I hope this helpful to anyone struggling with forms that have nested attributes for a has_many, through association. 我希望这对has_many, through关联为has_many, through具有嵌套属性的表格而苦苦挣扎的人有所帮助。

Cheers! 干杯!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM