Rails nested_form for a recursive model association - ruby-on-rails

It's based on two models: Formula and Operand. A formula can have many Operands. Each operand can have many or none operands.
I've established a polymorphic association in which Operand belongs to operation and also have many operands as operator. Formula have many operands as operation itself.
I'm trying to build a nested_form that can handle this, but i'm stuck. I'm using simple_nested_form. Any help would be appreciated.
class Formula < ActiveRecord::Base
validates_presence_of :name, :client_id
belongs_to :client
has_many :operands, as: :operation, dependent: :destroy
accepts_nested_attributes_for :operands, allow_destroy: true
end
class Operand < ActiveRecord::Base
validates_presence_of :operator
belongs_to :operation, polymorphic: true
has_many :operands, as: :operation, dependent: :destroy
accepts_nested_attributes_for :operands, allow_destroy: true
OPERATOR_TYPES = ["+", "-", "*", "/"]
end
UPDATE
Based in models above, I add in formulas_controller:
def new
#formula.operands.build
end
In the formula _form, I add fields_for :operands
= simple_nested_form_for #formula, :html => { :class => 'form-vertical' } do |f|
= f.error_notification
.form-inputs
.row
.span3= f.input :name
.well
= f.fields_for :operands
= f.link_to_add t('helpers.links.add'), :operands
And then, the partial _operands_fields, also rendering fields_for :operands
.well
.row
.span2= f.input :operator, collection: Operand::OPERATOR_TYPES
.span3= f.input :numeric_operand
.span3= f.link_to_remove t('helpers.links.remove')
.well
= f.fields_for :operands
= f.link_to_add t('helpers.links.add'), :operands
I think this should work, but it seems that when the formula form is rendered, it includes the fields_for the formula operand. Then, inside this partial, instead of including "Add", it renders another operand fields, and so on, ending in an infinite loop such this:
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
Client Load (0.2ms) SELECT "clients".* FROM "clients" WHERE "clients"."id" = ? LIMIT 1 [["id", 1]]
Rendered formulas/_operand_fields.html.haml (48.4ms)
Rendered formulas/_operand_fields.html.haml (9.0ms)
Rendered formulas/_operand_fields.html.haml (7.5ms)
Rendered formulas/_operand_fields.html.haml (9.1ms)
Rendered formulas/_operand_fields.html.haml (8.8ms)
Rendered formulas/_operand_fields.html.haml (9.2ms)
Rendered formulas/_operand_fields.html.haml (9.3ms)
Rendered formulas/_operand_fields.html.haml (9.5ms)
Rendered formulas/_operand_fields.html.haml (17.7ms)
Rendered formulas/_operand_fields.html.haml (52.4ms)
Rendered formulas/_operand_fields.html.haml (10.4ms)
Rendered formulas/_operand_fields.html.haml (11.3ms)
Any ideas in how to solve this problem?

Related

ActiveAdmin: has_many associative records are commiting changes before parent object on patch failure

I'm having an issue with Rails ActiveAdmin (quite a novice myself still) where a has_many association (CampaignCountry) is deleting/adding records incorrectly when its select input field :countries is modified and when the parent object (Campaign) fails due to any validation errors:
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
CampaignCountry Destroy (0.3ms) DELETE FROM "campaign_countries" WHERE "campaign_countries"."campaign_id" = $1 AND "campaign_countries"."country_id" = $2 [["campaign_id", 53], ["country_id", 1]]
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
(0.6ms) COMMIT
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
(0.1ms) BEGIN
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
Campaign Exists (0.3ms) SELECT 1 AS one FROM "campaigns" WHERE "campaigns"."vanity_url" = $1 AND "campaigns"."id" != $2 LIMIT $3 [["vanity_url", "dummycampaign"], ["id", 53], ["LIMIT", 1]]
↳ /Users/JAlva/.rbenv/versions/2.5.8/lib/ruby/gems/2.5.0/gems/activerecord-5.2.5/lib/active_record/log_subscriber.rb:98
(0.1ms) ROLLBACK
as you can see, CampaignCountry delete is still occuring despite the parent objects eventual ROLLBACK in this example. Ideally, I want the CampaignCountry records to only update after the parent Campaign validations are run and it is found to be valid. Below are the association rules between them:
# Campaign model:
has_many :campaign_countries, dependent: :destroy
has_many :countries, through: :campaign_countries
# CampaignCountry model:
belongs_to :campaign, touch: true
belongs_to :country
# Campaign ActiveAdmin countries field:
permit_params ... country_ids: []
...
f.input :countries, as: :select, multiple: true, collection: Country.pluck(:alpha_code, :id)
Any help is appreciated in understanding why this is occuring as well as a potential fix. Let me know if supplemental information is needed.

stack too deep from callback method

I have four models in Sport, Gold, Silver, and Bronze with a one to one relationship between sport and the other three. Every instance of sport must have an instance of Gold, Silver and Bronze; and three are to be created using a callback function in the Sports model. These callback function is one throwing up the error that i have been able to trace as the rails error stack produced is just a single line.
My Code
MODELS
Sport
class Sport < ActiveRecord::Base
validates :sportname, presence: true,
uniqueness: { case_sensitive: false }
has_one :gold, inverse_of: :sport, :dependent => :destroy
has_one :silver, inverse_of: :sport, :dependent => :destroy
has_one :bronze, inverse_of: :sport, :dependent => :destroy
accepts_nested_attributes_for :gold
accepts_nested_attributes_for :silver
accepts_nested_attributes_for :bronze
after_validation :build_default_medals, on: :create
def build_default_medals
self.build_gold
self.build_silver
self.build_bronze
end
end
Gold
class Gold < ActiveRecord::Base
belongs_to :sport #, inverse_of: :gold
validates_associated :sport, presence: true
belongs_to :team, inverse_of: :golds, counter_cache: true
validates_associated :team, :if => :create, allow_nil: true
accepts_nested_attributes_for :team
accepts_nested_attributes_for :sport
end
Silver
class Silver < ActiveRecord::Base
belongs_to :sport #, inverse_of: :silver
validates_associated :sport, presence: true
belongs_to :team, inverse_of: :silvers, counter_cache: true
validates_associated :team, :if => :create, allow_nil: true
accepts_nested_attributes_for :team
accepts_nested_attributes_for :sport
end
Bronze
class Bronze < ActiveRecord::Base
belongs_to :sport #, inverse_of: :bronze
validates_associated :sport, presence: true
belongs_to :team, inverse_of: :bronzes, counter_cache: true
validates_associated :team, :if => :create, allow_nil: true
accepts_nested_attributes_for :team
accepts_nested_attributes_for :sport
end
The callback after_validation :build_default_medals, on: :create is where the error is, producing this in my log
Started POST "/admin/sports" for 127.0.0.1 at 2016-07-14 14:52:22 -0400
Processing by Admin::SportsController#create as HTML
Parameters:{"utf8"=>"√",authenticity_token"=>"jss1O4bSJd3hcqxuSpu/KAxaowB7d
g5pLZw55oGDf1M=", "sport"=>{"sportname"=>"boxing"}, "commit"=>"Create Sport"}
User Load (1.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 (0.0ms) BEGIN
Sport Exists (1.0ms) SELECT 1 AS one FROM `sports` WHERE `sports`.`sportname` = 'boxing' LIMIT 1
SQL (1.0ms) INSERT INTO `sports` (`created_at`, `sportname`, `updated_at`) VALUES ('2016-07-14 18:52:22', 'boxing', '2016-07-14 18:52:22')
SQL (15.1ms) UPDATE `sports` SET `created_at` = '2016-07-14 18:52:22', `id` = 13, `sportname` = 'boxing', `updated_at` = '2016-07-14 18:52:22' WHERE `sports`.`id` = 13
SQL (1.0ms) INSERT INTO `golds` (`created_at`, `sport_id`, `updated_at`) VALUES ('2016-07-14 18:52:22', 13, '2016-07-14 18:52:22')
SQL (1.0ms) UPDATE `sports` SET `created_at` = '2016-07-14 18:52:22', `id` =13, `sportname` = 'boxing', `updated_at` = '2016-07-14 18:52:22' WHERE `sports`.`id` = 13
SQL (1.0ms) INSERT INTO `silvers` (`created_at`, `sport_id`, `updated_at`) VALUES ('2016-07-14 18:52:22', 13, '2016-07-14 18:52:22')
SQL (1.0ms) UPDATE `sports` SET `created_at` = '2016-07-14 18:52:22', `id` =13, `sportname` = 'boxing', `updated_at` = '2016-07-14 18:52:22' WHERE `sports`.`id` = 13
SQL (1.0ms) INSERT INTO `bronzes` (`created_at`, `sport_id`, `updated_at`) VALUES ('2016-07-14 18:52:22', 13, '2016-07-14 18:52:22') (72.2ms)
ROLLBACK
Completed 500 Internal Server Error in 213ms
SystemStackError (stack level too deep): actionpack (4.1.8) lib/action_dispatch/middleware/reloader.rb:79
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8/lib/action_dispatch/middleware/templates/rescues/_source.erb (2.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (4.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (7.0ms)
Rendered C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/actionpack-4.1.8/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (145.1ms)
I am curious as to why the callback triggers update after inserting and what exactly is triggering the ROLLBACK and infinite loop for Stack Level Too Deep. Any insight is appreciated. Thanks.
EDIT
Using after_create :build_default_medals in place of after_validation :build_default_medals, on: :creation produces
Started POST "/admin/sports" for 127.0.0.1 at 2016-07-14 16:19:24 -0400
Processing by Admin::SportsController#create as HTML
Parameters: {"utf8"=>"√","authenticity_token"=>"3LrwB6+nD9PJ9EwxEgIGhN3rVHP3UPLOHUz9MXWRJ4Y=", "sport"=>{"sportname"=>"dancing"}, "commit"=>"Create Sport"}
User Load (1.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 (0.0ms) BEGIN
Sport Exists (1.0ms) SELECT 1 AS one FROM `sports` WHERE `sports`.`sportname` = 'dancing' LIMIT 1
SQL (1.0ms) INSERT INTO `sports` (`created_at`, `sportname`,`updated_at`) VALUES ('2016-07-14 20:19:24', 'dancing', '2016-07-14 20:19:24')
Gold Load (2.0ms) SELECT `golds`.* FROM `golds` WHERE `golds`.`sport_id` =8 LIMIT 1
Silver Load (1.0ms) SELECT `silvers`.* FROM `silvers` WHERE `silvers`.`sport_id` = 8 LIMIT 1
Bronze Load (1.0ms) SELECT `bronzes`.* FROM `bronzes` WHERE `bronzes`.`sport_id` = 8 LIMIT 1
(45.5ms) COMMIT
EDIT FOR undefined method 'create' for #<Gold:0x63bf820>
def build_default_medals
#Gold.create(sport: self, team: nil)
#Silver.create(sport: self, team: nil)
#Bronze.create(sport: self, team: nil)
self.create_gold(team: nil)
self.create_silver(team: nil)
self.create_bronze(team: nil)
end
replace
after_validation :build_default_medals, on: :create
with
after_create :build_default_medals
You are trying to build associated models while sport is still a new record and not a persisted one. validates_associated :sport, presence: true causes a circular dependency and will give error.
I reproduced and tested with this change. It will work fine.
For saved object, use self.create_gold instead of self.build_gold, like this
def build_default_medals
self.create_gold
self.create_silver
self.create_bronze
end
Also remove accepts_nested_attributes_for :sport from Gold, Silver and Bronze model. Refer - https://github.com/rails/rails/issues/7809

Rails not displaying saving Model property using update_attributes

I have a Project model. I recently added project_name to that model. When I enter a name into the field and create a new Project however, it doesn't save the new attribute.
Projects Controller:
def create
if project.update_attributes(project_params)
flash[:success] = 'Project was successfully created'
redirect_to project_url(project)
else
render :new
end
end
def project_params
params.require(:project).permit(:description, :project_name, :customer_number, :sales_number, :sales_id, :customer_id, :discount, :date_wanted, :price,
items_attributes: [:id, :name, :unit, :quantity, :price, :_destroy])
end
Project Model:
include AASM
STEP_FORWARD_STATE = {
'quotes' => :proposal,
'proposals' => :ordered,
'orders' => :active,
'in_process' => :close
}
STEP_BACK_STATE = {
'proposals' => :quoted,
'orders' => :proposal,
'in_process' => :ordered,
'closed' => :active
}
belongs_to :customer
belongs_to :sales, class_name: 'User'
has_many :curtains, dependent: :destroy
has_many :versions, class_name: 'ProjectVersion', dependent: :destroy
has_many :tasks, dependent: :destroy
has_many :drape_tasks, class_name: 'DrapeTask', dependent: :destroy
has_many :trough_tasks, class_name: 'TroughTask', dependent: :destroy
has_many :items, dependent: :destroy
accepts_nested_attributes_for :items, :allow_destroy => true
delegate :email, :first_name, :full_name, :city_address, :name_for_select,
:contry_address, :address_for_email, :phone_o,
to: :customer, prefix: true, :allow_nil => true
delegate :name_for_select, :address_for_email, :email, :city_address, :full_name, :rep_number, :first_title,
to: :sales, prefix: true, :allow_nil => true
validates :customer, :sales, presence: true
attr_accessor :update_event
Terminal Log During the action:
Started POST "/projects" for ::1 at 2016-06-08 15:54:57 -0400
Processing by ProjectsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+vpCG6JPLLSBtntKhudPTopzq/MH0YlnjlfnFSsOVYnMfRY888LafcReDF41GIxb14q6DLdkbz/AwXEQi+iF4w==", "commit"=>"Create Project", "name"=>"TEST DAMMIT", "customer_autocomplete"=>"test#test.com", "project"=>{"customer_id"=>"1", "sales_id"=>"1", "discount"=>""}, "sales_autocomplete"=>"dzk#dzk", "description_autocomplete"=>"DID IT WORK"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Profile Load (1.8ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."person_id" = $1 AND "profiles"."person_type" = $2 LIMIT 1 [["person_id", 1], ["person_type", "User"]]
Project Load (1.5ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" IS NULL ORDER BY "projects"."id" ASC LIMIT 1
(0.2ms) BEGIN
Customer Load (0.5ms) SELECT "customers".* FROM "customers" WHERE "customers"."id" = $1 LIMIT 1 [["id", 1]]
Profile Load (1.2ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."person_id" = $1 AND "profiles"."person_type" = $2 LIMIT 1 [["person_id", 1], ["person_type", "Customer"]]
User Load (1.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
CACHE (0.0ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."person_id" = $1 AND "profiles"."person_type" = $2 LIMIT 1 [["person_id", 1], ["person_type", "User"]]
SQL (0.8ms) INSERT INTO "projects" ("state", "sales_id", "customer_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["state", "quotes"], ["sales_id", 1], ["customer_id", 1], ["created_at", "2016-06-08 19:54:57.746357"], ["updated_at", "2016-06-08 19:54:57.746357"]]
Item Load (0.2ms) SELECT "items".* FROM "items" WHERE "items"."project_id" = $1 [["project_id", 13]]
Curtain Load (0.2ms) SELECT "curtains".* FROM "curtains" WHERE "curtains"."project_id" = $1 [["project_id", 13]]
(1.2ms) COMMIT
Redirected to http://localhost:3000/projects/13
EDIT (June 9th):
Thanks for the help so far, #taryn-east .
(Also added my project_params above for clarification)
Here's the form I used for project_name:
.form-group
= label_tag 'name', 'Project Name', class: 'control-label'
.controls
= text_field_tag 'name', project.project_name, class: 'form-control', autocomplete: :off
For comparison, here's a form used for a working parameter of the model:
.form-group
= label_tag 'customer_autocomplete', 'Customer', class: 'control-label'
.controls
= text_field_tag 'customer_autocomplete', project.customer_name_for_select, class: 'form-control customer-autocomplete', autocomplete: :off
= f.hidden_field :customer_id
Clearly, f.hidden_field :[parameter] was missing. I added it to my project_name field, leaving it looking like so:
.form-group
= label_tag 'name', 'Project Name', class: 'control-label'
.controls
= text_field_tag 'name', project.project_name, class: 'form-control', autocomplete: :off
= f.hidden_field :project_name
Which now leaves us with a Terminal log of:
Started POST "/projects" for ::1 at 2016-06-09 12:11:42 -0400
Processing by ProjectsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"BqEZsyQK+uLoeWewYC6smeC6CX0EGLcZgUQlmAyjtLqOmAb3v+2pw6VJ0hoBnISkGegz3fCAMT4+UPMVh28l8Q==", "commit"=>"Create Project", "project_name"=>"TEST PLS DEALER", "project"=>{"project_name"=>"", "customer_id"=>"1", "sales_id"=>"1", "discount"=>"", "description"=>""}, "customer_autocomplete"=>"test#test.com", "sales_autocomplete"=>"dzk#dzk", "description_autocomplete"=>"WORK PLS ONE TIME EHHHH"}
This is promising, as adding
f.hidden_field :project_name
Add project_name to the Project hash passed in the Create action like so:
"project"=>{"project_name"=>"", "customer_id"=>"1", "sales_id"=>"1", "discount"=>"", "description"=>""}
However, you can see that the actual content of the parameter remains outside the field it is supposed to reside in. I did some digging and noticed the difference between my field and the previous working fields: the working ones had class: "[:parameter]-autocomplete" in them. I added the appropriate class to mine, and it still didn't work. Elsewhere in the app, there's a Coffeescript:
$(document).on 'focus', '.customer-autocomplete', ->
$(#).autocomplete
source: "/customers",
dataType: 'JSON',
minLength: 2,
select: (event, ui) ->
$('#project_customer_id').val ui.item.customer_id
$('#sales_autocomplete').val ui.item.sales_data
$('#project_sales_id').val ui.item.sales_id
$(document).on 'focus', '.sales-autocomplete', ->
$(#).autocomplete
source: "/sales",
dataType: 'JSON',
minLength: 2,
select: (event, ui) ->
$('#project_sales_id').val ui.item.sales_id
I'm not familiar with Coffeescript, but it seems like this is what's auto-completing the hidden fields with class: "[:parameter]-autocomplete". How do I replicate this for my added fields?
EDIT: (June 10th)
I took your advice and simplified the migrations (definitely a good call :) ) but it still seems to be uncooperative.
New Params:
def project_params
params.require(:project).permit(:description, :name, :customer_number, :sales_number, :sales_id, :customer_id, :discount, :date_wanted, :price,
items_attributes: [:id, :name, :unit, :quantity, :price, :_destroy])
end
New Form:
.form-group
= label_tag 'name', 'Project Name', class: 'control-label'
.controls
= text_field_tag 'name', project.name, class: 'form-control name-autocomplete', autocomplete: :off
= f.hidden_field :name
New Show:
Project Name:
= project.name
Terminal Log from attempt to make a Project with the above:
Started POST "/projects" for ::1 at 2016-06-10 10:30:30 -0400
Processing by ProjectsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"iMt4rowEbO1UGzjwfl1XkraTbktC+5+/MiNzYvU/uxAA8mfqF+M/zBkrjVof73+vT8FU67ZjGZiNN6XvfvMqWw==", "commit"=>"Create Project", "name"=>"TEST PROJECT NAME", "project"=>{"name"=>"", "customer_id"=>"1", "sales_id"=>"1", "discount"=>"", "description"=>""}, "customer_autocomplete"=>"test#test.com", "sales_autocomplete"=>"dzk#dzk", "description_autocomplete"=>"TEST PROJECT DESCRIPTION"}
I'm having the same issue with project.description as well, as you can see; is it possible the 'project' variable is defined somewhere else in the app that doesn't include these new params? It's not throwing a red screen at any point in the process, either. Thanks again for all the help so far #taryn :)
If you look at the output in the server window it shows you the problem:
"name"=>"TEST DAMMIT", "customer_autocomplete"=>"test#test.com",
"project"=>{"customer_id"=>"1", "sales_id"=>"1", "discount"=>""}
That name field isn't part of the project and thus won't get picked up as an attribute of project.
What you'd expect to see coming through from params would be this:
"customer_autocomplete"=>"test#test.com",
"project"=>{"name"=>"TEST DAMMIT", "customer_id"=>"1", "sales_id"=>"1", "discount"=>""}
This means that something is slightly wrong with your form. Check that your name fields is done the same way as the other project fields
Edit:
In your form, the attributes is named name:
text_field_tag 'name',
but in the rest of your code it's expecting the attribute to be named project_name
params.require(:project).permit(:description, :project_name
You have to actually name the field the same as what you use int he code :) eg
text_field_tag 'project_name',
though TBH I'd seriously recommend just calling it name (and rewriting the migrations etc) if you can because project.project_name is a bit redundant ;)

Parent model Object ID nil when updating child model in form

I am trying to save parent models ID and it child attributes using a join table council_history, for some reason it seems to be not saving the parent models ID in the join table but saves child attributes ID, when I try to do something similar with a many to many association everything works fine and parent object is saved, I am not sure if its my associations that are wrong, have been wrestling with multiple combinations, any help would be appreciated.
Started PUT "/properties/34/build/council" for 127.0.0.1 at 2013-08-21 00:44:37 +0100
Processing by Properties::BuildController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+nfsCrRvTUrgYrdHix0v2QGaDJj25j4X1sh8oHVpkgs=", "property"=> {"council_history_attributes"=>{"property_id"=>"34", "council_id"=>"1"}}, "commit"=>"Create Council history", "property_id"=>"34", "id"=>"council"}
Started GET "/properties/34/build/confirmed" for 127.0.0.1 at 2013-08-21 00:44:37 +0100
Processing by Properties::BuildController#show as HTML
Parameters: {"property_id"=>"34", "id"=>"confirmed"}
Property Load (0.2ms) SELECT "properties".* FROM "properties" WHERE "properties"."id" = ? LIMIT 1 [["id", "34"]]
CouncilHistory Load (0.3ms) SELECT "council_histories".* FROM "council_histories" WHERE "council_histories"."property_id" = 34 LIMIT 1
(0.1ms) begin transaction
**(0.5ms) UPDATE "council_histories" SET "property_id" = NULL, "updated_at" = '2013-08-20 23:44:37.220619' WHERE "council_histories"."id" = 18**
(1.5ms) commit transaction
Tenant Load (0.3ms) SELECT "tenants".* FROM "tenants" WHERE "tenants"."property_id" = 34
Rendered properties/build/confirmed.html.erb within layouts/application (2.1ms)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
(0.4ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = 3 AND (((roles.name = 'admin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))
Property.rb
class Property < ActiveRecord::Base
attr_accessible :name, :council_history_attributes, :council,
:property_id, :council_id, :status, :council_history_id
belongs_to :user
has_one :council_history
accepts_nested_attributes_for :council_history
has_one :council, through: :council_history, :foreign_key => :council_id
accepts_nested_attributes_for :council
Council.rb
class Council < ActiveRecord::Base
attr_accessible :CouncilEmail, :name, :CouncilTel, :council_histories_attributes
has_many :council_histories
has_many :properties, :through => :council_histories, :foreign_key => :property_id
end
Council_history.rb -- Join Table
class CouncilHistory < ActiveRecord::Base
attr_accessible :council_id, :property_id, :vacant
belongs_to :council
belongs_to :property
end
Nested Controller
class Properties::BuildController < ApplicationController
include Wicked::Wizard
steps :council, :confirmed
def show
#property = Property.find(params[:property_id])
#property.build_council_history do |council_history|
#council = council_history.build_council
end
render_wizard
end
View Form
<%= simple_form_for #property, url: wizard_path, :method => 'put' do |f| %>
<%= f.simple_fields_for :council_history do |builder| %>
<%= builder.input :property_id, as: :hidden, input_html: { value: #property.id } %>
<%= builder.input :council_id, :collection => Council.all %>
<%= builder.submit %>
<% end %>
<% end %>
If you will ask another questions on the future, make sure to AVOID putting authenticity_token in the question ...

Nested forms in Rails using has_many :through

I am having trouble figuring out how to make a nested form using a has_many :through relationship. I used this Railscast and I took at look at this tutorial and lots of the questions on Stack Overflow and elsewhere around Google.
I'm trying to make a way to create tags via the articles form. My code has gone through lots of iterations based on information from lots of different sources and none of them have worked, but right now I have
A class for articles
class Article < ActiveRecord::Base
attr_accessible :content, :heading, :image, :tag_ids, :tags, :tag_name, :tag_attributes
belongs_to :user
has_many :comments, :dependent => :destroy
has_many :article_tags
has_many :tags, :through => :article_tags
accepts_nested_attributes_for :tags, :reject_if => proc { |attributes| attributes['tag_name'].blank? }
...
end
A class for tags
class Tag < ActiveRecord::Base
attr_accessible :tag_name
has_many :article_tags
has_many :articles, :through => :article_tags
end
A class for article_tags
class ArticleTag < ActiveRecord::Base
belongs_to :article
belongs_to :tag
end
The New in my articles_controller.rb is like this:
def new
#article = Article.new
#tags = Tag.find(:all)
article_tag = #article.article_tags.build()
#article_tags = #article.tags.all
#article.article_tags.build.build_tag
3.times do
article_tag = #article.article_tags.build()
end
end
And my form for articles is currently like this (I have gone back and forth between nesting the fields_for :tags inside the fields_for :article_tags or just letting them be on their own):
<%= form_for #article , :html => { :multipart => true } do |f| %>
...excerpted...
<%= f.fields_for :article_tags do |t| %>
<%= t.fields_for :tags do |ta| %>
<%= ta.label :tag_name, "Tag name" %>
<%= ta.text_field :tag_name %>
<% end %>
<% end %>
I realize this is probably messy; I'm pretty new at this and I'm trying to figure it out. Do I have to add anything to the articles_controller create? Is it something to do with the attr_accessible? Or should I do something completely different?
EDIT:
Here are the request parameters after making the change suggested by Hck and creating a new article, selecting an existing tag with tag_id 3 and trying to also create a new tag at the same time:
Started POST "/articles" for 127.0.0.1 at 2011-08-10 19:05:46 +1000
Processing by ArticlesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"5CQuV4RWfFZD1uDjv1DrZbIe+GB/sDQ6yiAETZutmZ4=", "article"=>{"heading"=>"Test heading", "content"=>"Test Content", "tag_ids"=>["3"], "article_tags"=>{"tags"=>{"tag_name"=>"Test tag"}}}, "commit"=>"Submit"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
WARNING: Can't mass-assign protected attributes: article_tags
Tag Load (0.4ms) SELECT "tags".* FROM "tags" WHERE "tags"."id" = 3 LIMIT 1
AREL (0.4ms) INSERT INTO "articles" ("content", "user_id", "created_at", "updated_at", "heading", "image_file_name", "image_content_type", "image_file_size") VALUES ('Test Content', 1, '2011-08-10 09:05:46.228951', '2011-08-10 09:05:46.228951', 'Test heading', NULL, NULL, NULL)
AREL (0.2ms) INSERT INTO "article_tags" ("article_id", "tag_id", "created_at", "updated_at") VALUES (88, 3, '2011-08-10 09:05:46.243076', '2011-08-10 09:05:46.243076')
[paperclip] Saving attachments.
Redirected to [localhost]
Completed 302 Found in 212ms
And if I add :article_tags to the attr_accessible for Article and try again, I get:
Started POST "/articles" for 127.0.0.1 at 2011-08-10 19:11:49 +1000
Processing by ArticlesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"5CQuV4RWfFZD1uDjv1DrZbIe+GB/sDQ6yiAETZutmZ4=", "article"=>{"heading"=>"Test heading", "content"=>"Test content", "tag_ids"=>["3"], "article_tags"=>{"tags"=>{"tag_name"=>"Test tag "}}}, "commit"=>"Submit"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Tag Load (0.4ms) SELECT "tags".* FROM "tags" WHERE "tags"."id" = 3 LIMIT 1
Completed in 119ms
ActiveRecord::AssociationTypeMismatch (ArticleTag(#2165285820) expected, got Array(#2151973780)):
app/controllers/articles_controller.rb:32:in `create'
Try to replace #article.article_tags.build.build_tag with #article.tags.build in your controller`s action.
I don't think you have to nest the article tags in it too. Article tags is just an association between the articles and tags. You can simply create the new tag within the articles because you already associated with them. I believe it is from the magic of the "accepts_nested_attributes". try this.
<%= form_for #article , :html => { :multipart => true } do |f| %>
...excerpted...
<%= f.fields_for :tags, Tag.new do |t| %>
<%= t.label :tag_name, "Tag name" %>
<%= t.text_field :name %>
<% end %>
<% end %>
Also, you should try to mass assign it instead of saving every attribute piece by piece by using private params. I had a nested forms problem before too, so you can take a look at how I wrote my code:
Cannot save record to database RAILS nested forms
The only thing I left out there was the private params section, which I recommended you to do.
private
def venue_params
params.require(:venue).permit(:name, :address, :discount, :latitude, :longitude, :tags_attributes =>[:name],:tag_ids => [])
end
I also wrote a blog post about nested forms, so you can take a look at it too
http://minling.github.io/

Resources