Rails not displaying saving Model property using update_attributes - ruby-on-rails

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 ;)

Related

Add Existing Tag to Article in Rails 6

I'm trying to add existing Tags to Articles.
My models:
# app/models/user.rb
class User < ApplicationRecord
has_many :articles, dependent: :destroy
has_many :tags, dependent: :destroy
end
# app/models/article.rb
class Article < ApplicationRecord
belongs_to :user
has_many :tags
end
# app/models/tag.rb
class Tag < ApplicationRecord
belongs_to :user
belongs_to :article
has_many :articles
validates :name, presence: true, uniqueness: { scope: :user_id }
end
By using the following in the console, I'm able to add a tag to an article.
> a = Article.last
> a.tags.create(name: "unread", user_id: "1")
> a.tags
Tag Load (0.3ms) SELECT "tags".* FROM "tags" WHERE "tags"."article_id" = ? [["article_id", 29]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Tag id: 6, name: "unread", user_id: 1, article_id: 29, created_at: "2020-12-28 16:05:36", updated_at: "2020-12-28 16:05:36", permalink: "unread">]>
If I try to add this same tag to different article using the same .create, I get a rollback error.
> a = Article.first
> a.tags.create(name: "unread", user_id: "1")
(0.0ms) begin transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Tag Exists? (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = ? AND "tags"."user_id" = ? LIMIT ? [["name", "unread"], ["user_id", 1], ["LIMIT", 1]]
(0.0ms) rollback transaction
Thinking the problem is the .create since that tag already exists, I tried this first_or_create, but that also errored.
> a.tags.first_or_create(name: "unread", user_id: "1")
Tag Load (0.1ms) SELECT "tags".* FROM "tags" WHERE "tags"."article_id" = ? ORDER BY "tags"."id" ASC LIMIT ? [["article_id", 52], ["LIMIT", 1]]
(0.0ms) begin transaction
Tag Exists? (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = ? AND "tags"."user_id" = ? LIMIT ? [["name", "unread"], ["user_id", 1], ["LIMIT", 1]]
(0.3ms) rollback transaction
How do I add an existing tag to an article?
Edit:
I've added a join table as prescribed by Max. This has allowed me to save tags to articles via the console.
> t = Tag.where(name: "rails", user_id: "1").first_or_create
Tag Load (0.6ms) SELECT "tags".* FROM "tags" WHERE "tags"."name" = ? AND "tags"."user_id" = ? ORDER BY "tags"."id" ASC LIMIT ? [["name", "rails"], ["user_id", 1], ["LIMIT", 1]]
=> #<Tag id: 8, name: "rails", user_id: 1, created_at: "2020-12-28 21:21:25", updated_at: "2020-12-28 21:21:25", permalink: "rails">
> a.tags << t
(0.0ms) begin transaction
Article Tag Exists? (0.1ms) SELECT 1 AS one FROM "article_tags" WHERE "article_tags"."tag_id" = ? AND "article_tags"."article_id" = ? LIMIT ? [["tag_id", 8], ["article_id", 29], ["LIMIT", 1]]
Article Tag Create (0.2ms) INSERT INTO "article_tags" ("article_id", "tag_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["article_id", 29], ["tag_id", 8], ["created_at", "2020-12-28 21:22:13.567187"], ["updated_at", "2020-12-28 21:22:13.567187"]]
(17.9ms) commit transaction
Tag Load (0.1ms) SELECT "tags".* FROM "tags" INNER JOIN "article_tags" ON "tags"."id" = "article_tags"."tag_id" WHERE "article_tags"."article_id" = ? LIMIT ? [["article_id", 29], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Tag id: 6, name: "unread", user_id: 1, created_at: "2020-12-28 16:05:36", updated_at: "2020-12-28 16:05:36", permalink: "unread">, #<Tag id: 8, name: "rails", user_id: 1, created_at: "2020-12-28 21:21:25", updated_at: "2020-12-28 21:21:25", permalink: "rails">]>
I'm still unclear how to save this from the view.
I have a form in app/views/articles/edit.html.erb:
<%= form_with(model: #article, local: true) do |form| %>
<%= form.label :tag_list, "Tags, separated by comma" %>
<%= form.text_field :tag_list %>
<%= form.submit "Update Tags" %>
<% end %>
In the Articles Controller I assume I'd want the following.
def edit
#article.tags << Tag.where(name: "unread", user_id: "1").first_or_create
#article.save!
end
...or perhaps I need to loop those tags in the model? Something like this:
# app/models/article.rb
def tag_list
tags.map(&:name).join(", ")
end
def tag_list=(names)
self.tags = names.split(",").map do |name|
Tag.where(name: name.strip, user_id: current_user.id).first_or_create!
end
end
# app/controllers/articles_controller.rb
def edit
#article.tags << tag_list
#article.save!
end
But this isn't working. How do I update the tags from a view?
Edit:
I've decided not to update tags by form, so I've marked Max's answer as the solution since he pointed me to the Join Tables which got me on the right track.
If you wish to update this for posterity, I'm sure they will appreciate it.
If you want a tag to ever belong to more then a single article you need a join table:
class Article < ApplicationRecord
has_many :article_tags
has_many :tags, through: :article_tags
end
# rails g model article_tag article:references tag:references
# make sure to add a unique index on article_id and tag_id
class ArticleTag < ApplicationRecord
belongs_to :article
belongs_to :tag
validates_uniqueness_of :tag_id, scope: :article_id
end
class Tag < ApplicationRecord
has_many :article_tags
has_many :articles, through: :article_tags
end
This creates a many to many association between the two tables (an article can have multiple tags, and a tag can belong to multiple articles). This can also be done through has_and_belongs_to_many which does not have a join model but has limited flexibility.
If you want to add an existing tag(s) to an article you can use the shovel operator:
a = Article.last
a.tags << Tag.first
a.tags << Tag.second
But usually you use the tag_ids= setter to set the association from an array of ids:
class ArticlesController
def create
#article = Article.new(article_params)
# ...
end
private
def article_params
require(:article)
.permit(:title, :body, tag_ids: [])
end
end
<%= form_with(model: #article) do |f| %>
# ...
<%= f.collection_checkboxes :tag_ids, Tag.all, :id, :name %>
<% end %>
tag_ids: [] permits an array of ids.
This will automatically add/remove rows in the join table depending on what the user checks.

Rails 5, Rolify - Role Assignment Strategy

I am not very good at learning how to code. I've been trying for 4 years and still struggling to figure out basic concepts.
I haven't found the right starting point so, I'm constantly gap filling for things that might be foundations and I don't know them yet.
I have a Rails 5 app. It has models for user, roles, app_roles and assign_roles.
The associations are:
User
rolify strict: true # strict means you get true only on a role that you manually add
attr_accessor :current_role
belongs_to :organisation
Role
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource,
:polymorphic => true,
:optional => true
AppRole
[no associations]
Note: this is the resource I'm using to have a CRUD to define roles that the app can use to assign roles to users.
Assign Role
[no associations]
Note: this is the resource I'm using to allow some users to assign roles to some other users.
Organisation
has_many :users
I'm using rolify gem.
My user model has:
class User < ApplicationRecord
rolify strict: true # strict means you get true only on a role that you manually add
attr_accessor :current_role
My Roles table has:
class Role < ApplicationRecord
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource,
:polymorphic => true,
:optional => true
validates :resource_type,
:inclusion => { :in => Rolify.resource_types },
:allow_nil => true
scopify
end
In my assign_roles controller, I have:
class Users::AssignRolesController < ApplicationController
before_action :authenticate_user!
def index
# if #current_user.is_admin?
#app_roles = AppRole.all
# else
# #app_roles = AppRole.where(category: relevant_category)
# end
# if #current_user.is_admin?
#users = User.all
# else
# #users = current_user.organisation.users.select { |u| u.id != current_user.organisation.owner_id }
# end
end
def create
user = User.find(params[:users])
role = AppRole.find(params[:roles])
# organisation = Organisation.first
# #organisation = Organisation.find(#current_user.organisation)
# byebug
user.add_role role.display_name, #current_user.organisation
flash[:notice] = "Successfully created"
redirect_to action: :index
end
def show
# #users = User.joins(:profiles).where('profiles.organisation_id = ?' #current_user.organisation.id)
# #users = User.all
#current_user.organisation.users
end
def update
end
def destroy
user = User.find(params[:users])
# role = AppRole.find(params[:roles])
assigned_role = user.roles
# user_roles = user.roles
# organisation = Organisation.first
# byebug
user.remove_role assigned_role.name, #current_user.organisation
flash[:notice] = "Successfully created"
redirect_to action: :index
end
end
In my routes file, i have:
resources :users, shallow: true do
scope module: :users do
resources :assign_roles
resources :identities
end
end
In my views/users/assign_roles/index file, I have:
<%= form_tag(url: '/assign_roles', method: :post ) do |f| %>
<div class="row" style="margin-top:50px; margin-bottom: 150px">
<div class="col-md-3 col-md-offset-1">
<%= select_tag "users", options_from_collection_for_select(#users, "id", "full_name"), { class: "chosen-select form-control" } %>
</div>
<!-- # roles -->
<div class="col-md-3 col-md-offset-1">
<%= select_tag "roles", options_from_collection_for_select(#app_roles, "id", "display_name"), :class => 'chosen-select form-control' %>
</div>
<div class="col-md-3 col-md-offset-1">
<div class="form-actions">
<%= submit_tag(value = "Submit") %>
</div>
</div>
</div>
<% end %>
So far, that all looks OK. Im' getting stuck at the next part.
In my views/users/assign_roles/show.html.er
I'm trying to show each user's roles on a view. I want the user that assigned any existing roles to be able to delete them.
I have:
<% #users.each do |user| %>
<td><%= user.roles.count %></td>
<% user.roles.each do |role| %>
<td><%= role.name.titleize %></td>
<td><%= link_to 'Destroy', role, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
<% end %>
My controller for assign_roles is saved in app/controllers/users folder. My views folder for assign roles is saved inside app/views/users/assign_roles
When I try to use the app/users/4/assign_roles form, I get the form to render with the list of AppRoles that are available to be assigned. I don't get any error message. Instead, I get the notice on successful creation. However, when I try to check if a user has a role, I get false.
I can see the server log reads as follows:
Started POST "/users/4/assign_roles?method=post&url=%2Fassign_roles" for ::1 at 2016-10-23 11:50:11 +1100
Processing by Users::AssignRolesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"U80gMQd01SaTcHHnnSFxvRc2u9JvJMFB+5smS9SaN8ZRixQvJRTMbutG0KkoqXL+oMU1aOxX8AURBtuy2Rm5yA==", "users"=>"4", "roles"=>"3", "commit"=>"Submit", "method"=>"post", "url"=>"/assign_roles", "user_id"=>"4"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 4], ["LIMIT", 1]]
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
AppRole Load (0.2ms) SELECT "app_roles".* FROM "app_roles" WHERE "app_roles"."id" = $1 LIMIT $2 [["id", 3], ["LIMIT", 1]]
Organisation Load (0.4ms) SELECT "organisations".* FROM "organisations" ORDER BY "organisations"."id" ASC LIMIT $1 [["LIMIT", 1]]
Role Load (1.1ms) SELECT "roles".* FROM "roles" WHERE "roles"."name" = $1 AND "roles"."resource_type" = $2 AND "roles"."resource_id" = $3 ORDER BY "roles"."id" ASC LIMIT $4 [["name", "sdfddd"], ["resource_type", "Organisation"], ["resource_id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
(0.1ms) ROLLBACK
HABTM_Roles Load (0.4ms) SELECT "users_roles".* FROM "users_roles" WHERE "users_roles"."user_id" = $1 [["user_id", 4]]
Role Load (0.3ms) SELECT "roles".* FROM "roles" WHERE "roles"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.5ms) SELECT "roles".id FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 [["user_id", 4]]
Role Load (0.3ms) SELECT "roles".* FROM "roles" WHERE "roles"."id" = 1
Role Load (0.4ms) SELECT "roles".* FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 [["user_id", 4]]
Redirected to http://localhost:3000/users/4/assign_roles
Completed 302 Found in 33ms (ActiveRecord: 6.5ms)
I don't know what the ROLLBACK line means but I don't think that usually appears in these log messages when things are working correctly.
Can anyone see what I need to do to get the role assignment working correctly?
organisation model
class Organisation < ApplicationRecord
include LogoUploader[:logo]
include BannerUploader[:banner]
# --------------- associations
has_many :users
# --------------- scopes
# --------------- validations
# --------------- class methods
enum org_type: {
University: 1,
Publicly_Funded_Research_Organisation: 2,
Industry: 3,
Grantor: 4,
Investor: 5,
Policy: 6,
}
# --------------- callbacks
# --------------- instance methods
# --------------- private methods
end
add resourcify to your organisation model and then try.
Instead of developing our own RBAC (Role Based Access Control), there are already some open source gems available in rails community. we can make use of that.
Its a nice gem
https://github.com/nathanl/authority
Also there are many other gems available
https://www.ruby-toolbox.com/categories/rails_authorization
If none of the above gems fulfil your need, then atleast you can use that as an sample model for your development.

Rails nested_form for a recursive model association

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?

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