【问题标题】:Rails 4 with simple_form_for, has_many through and nested attributesRails 4 具有 simple_form_for、has_many through 和嵌套属性
【发布时间】:2016-02-03 07:39:24
【问题描述】:

我需要与此几乎相同的功能:

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

我一直在转来转去(转来转去)试图让它正常运行,但总是遇到障碍。我对 Ruby 和 Rails 还有些陌生,需要帮助才能继续前进。这是我目前存在的实现:

/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

所以,目前我的问题是:

  1. 表单视图中的这一行 - role = ta.object.account_transfer_role.titleize
    给我一个“undefined method `account_transfer_role' for nil:NilClass”错误,那我在哪里做错了吗?

  2. transfer_account 模型中的 accepts_nested_attributes_for 行为什么(或为什么会)起作用?我的印象是 accepts_nested_attributes_for 在关联的 belongs_to 端不起作用,因为它不是父级(或类似的东西)。

  3. 如果我注释掉与问题 #1 相关的代码以避免该错误,则表单会呈现,但我只会得到一组 account 嵌套属性的输入框。每个transfer 都有两个transfer_accounts 和两个accounts,并在transfers_controller 的“new”操作中与之关联(transfer_account:account_transfer_role 的值为'source' 和 'destination'),我不应该得到两组 account 嵌套属性输入框吗?

  4. 我的嵌套属性的单数/复数是否正确?基本上,我让它们保持一致,从它们的关联开始。例如,transferhas_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|  
    

    account 也是如此,除了单数。

  5. 如果我执行第 3 步,请使用提供的一组 account 属性填写表单,然后提交,我会收到“No route matches [POST] "/transfers/new'错误。所以,显然我的路由有问题。我不确定 transfersaccounts 应该如何出现在 routes.rb 文件中。作为

    resources  :transfers
    resources  :accounts
    

    或作为

    resources  :transfers do  
      resources  :accounts  
    end  
    

    或者还有其他方式。另外,我不知道路由文件中是否也需要transfer_accounts

如果您已经到达这条线,感谢您的耐心等待。:>) 如果您能提供任何帮助,我们将不胜感激。

干杯, 蒂姆

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 activerecord nested-attributes has-many-through


    【解决方案1】:

    解决方案非常简单,解决了我在问题 1、3、4 和 5 中提出的每个问题。

    在表格中,我更改了这一行:

    = simple_form_for :transfer do |t|
    

    到这里:

    = simple_form_for @transfer do |t|
    

    对于问题 1,account_transfer_role 不再为零。
    对于问题 3,它开始为 transfer_accounts 渲染输入框。
    对于第4题,strong参数中的单数和复数证明是正确的,如图所示。
    对于问题 5,这个版本的路线被证明是正确的:

    resources  :transfers
    resources  :accounts
    

    虽然我将其简化为:

    resources  :transfers, :accounts
    

    至于问题 2,我想出的最佳答案是我的理解似乎不正确,accepts_nested_attributes_for: 确实适用于关联的 belongs_to 方面,因为它对我有用。

    我希望这对任何处理具有 has_many, through 关联的嵌套属性的表单的人有所帮助。

    干杯!

    【讨论】:

      猜你喜欢
      • 2012-03-10
      • 1970-01-01
      • 2018-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多