I have been trying to get one form to update two tables in Ruby on Rails. I have done a fairly exhaustive search on here and I'm finding the guides a little confusing. The closest answer I have found is this:
Ruby on Rails Saving in two tables from one form
I have mostly copied this answer but I still can't get it to work. Here are the relevant bits of code:
Models - I have two models, supplier and account. Each supplier should have one account.
class Supplier < ApplicationRecord
has_one :account
accepts_nested_attributes_for :account
end
class Account < ApplicationRecord
belongs_to :supplier
end
Form - not sure if this is entirely correct
<h2>Please enter a supplier</h2>
<%= form_for(@supplier) do |form| %>
<p>
<%= form.label :name %><br>
<%= form.text_field :name %>
</p>
<p>
<%= form.fields_for :account do |f| %>
<%= f.label :account_number %><br>
<%= f.text_field :account_number %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<% end %>
And finally, the SuppliersController
class SuppliersController < ApplicationController
def index
@suppliers = Supplier.all
end
def new
@supplier = Supplier.new
@supplier = build_account
end
def create
@supplier = suppliers.build(supplier_params)
if @supplier.save
redirect_to suppliers_path
else
redirect_to root_path
end
end
private
def supplier_params
params.require(:supplier).permit(:name, account_attributes:
[:account_number])
end
end
I am getting an undefined method error on the second line of the new action of the SuppliersController
and I don't know why.
Debugging printout for create action in SupplierController
:
Started POST "/suppliers" for 127.0.0.1 at 2017-11-03 08:25:25 -0600 Processing by SuppliersController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"KEqvPgjrYmuBux3qWCQJLAkSLQ4z1ns4HsK2P9sWlhVegpjhik/JoWT3vlL/kP42XpU8adllOaqSA16izYJ0SA==", "supplier"=>{"name"=>"", "account_attributes"=>{"account_number"=>""}}, "commit"=>"Create Supplier"}
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "suppliers" ("created_at", "updated_at", "name") VALUES (?, ?, ?) [["created_at", "2017-11-03 14:25:25.388933"], ["updated_at", "2017-11-03 14:25:25.388933"], ["name", "Bob"]]
(0.6ms) commit transaction (0.0ms) begin transaction
Supplier Load (1.0ms) SELECT "suppliers".* FROM "suppliers" WHERE "suppliers"."id" = ? LIMIT ? [["id", 6], ["LIMIT", 1]] SQL (0.3ms) INSERT INTO "accounts" ("created_at", "updated_at", "account_number", "supplier_id") VALUES (?, ?, ?, ?) [["created_at", "2017-11-03 14:25:25.424367"], ["updated_at", "2017-11-03 14:25:25.424367"], ["account_number", "456456456"], ["supplier_id", 6]]
(0.6ms) commit transaction
Supplier: #Supplier id: 6, created_at: "2017-11-03 14:25:25", updated_at: "2017-11-03 14:25:25", name: "Bob"
Account: #Account id: 1, created_at: "2017-11-03 14:25:25", updated_at: "2017-11-03 14:25:25", account_number: "456456456", supplier_id: 6
(0.0ms) begin transaction (0.0ms) commit transaction
Redirected to http://localhost:3000/suppliers Completed 302 Found in 51ms (ActiveRecord: 4.6ms)
Your issue is that build_account
is a method on a supplier object. As you have your code, it is currently trying to run build_account as if it was a method on the SuppliersController
So, you should try updating your controller method for new to:
def new
@supplier = Supplier.new
@account = @supplier.build_account
end
This calls the build_account
against the supplier object you have just instantiated, returning the result as an instantiated account object, associated with the supplier instance it belongs to.
The build_account method is generated through the association you have defined in your supplier model, where you say has_one :account
. The docs here http://guides.rubyonrails.org/association_basics.html#has-one-association-reference describe all the methods that are created in this way.
EDIT
Following up on the question about the create action, the controller should be updated. In the comments we have debugged the models and they appear to work as we would hope, so once updated the controller should work as:
def create
# Created a Supplier, but not yet persisted.
@supplier = Supplier.new(supplier_params)
# Persists the supplier, giving him an id, which can then be used
# through implicit autosave functionality to take the nested params
# to build the associated account. All of these operations are wrapped
# in a single DB transaction, ensuring nothing is left dangling if
# there is a validation failure
if @supplier.save
redirect_to suppliers_path
else
redirect_to root_path
end
end
Based on the association autosave functionality, new associated records (account) should be automatically created based on the nested parameters being passed from the form, at the point the supplier is saved, without having to add any additional code to explicitly handle this. More autosave options are documented here: http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html
from the guides, this section
4.1.1 Methods Added by belongs_to
each instance of the Book model will have these methods:
So as my friend points below, I assume
@supplier = Supplier.build_account // wrong
@account = @supplier.build_account
would be correct
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.