Couldn't find ArticlesSkill with ID=1 for Article with ID= - ruby-on-rails

I am trying to create an article.
class Article < ActiveRecord::Base
belongs_to :article_skill
attr_accessible :articles_skill_attributes
accepts_nested_attributes_for :articles_skill
end
class ArticlesSkill < ActiveRecord::Base
attr_accessible :description, :name
has_many :articles
end
This is my form in the article/new.html.erb
<%= article_form.fields_for :articles_skill, #article.articles_skill do |b|%>
<label class="medium"><span class="red">*</span> Skill</label>
<%= b.select :id, options_for_select(ArticlesSkill.all.collect{|m| [m.name, m.id]}) %>
<%end%>
Here the article_form is the builder for the #article form object.
If I try to save the #article object its showing this error.
Couldn't find ArticlesSkill with ID=1 for Article with ID=

I've been struggling with this problem for a few days. Did a lot of searching.. it took going to the rails console and searching by the exception being thrown instead to make any progress with this.
Check out this answer on this question for why it's happening, and possible workarounds.
Use rails nested model to *create* outer object and simultaneously *edit* existing nested object?
Be aware that using the first option presented here creates a security hole as described in http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2010-3933

The second parameter in your fields_for call seems unnecessary. ActiveRecord is performing a lookup on the association articles_skill for #article when it reaches that param, but since the #article is new and has yet to be saved, it has no ID and triggers an error.
<%= article_form.fields_for :articles_skill do |b|%>
<label class="medium"><span class="red">*</span> Skill</label>
<%= b.select :id, options_for_select(ArticlesSkill.all.collect{|m| [m.name, m.id]}) %>
<% end %>

I can suggest only a workaround. It works, but I don't like it - I want some out-of-the-box solution.
I assume you have a function:
def articles_skill_params
params.require(:articles_skill).permit(:description, :name,
article_attributes: []) end
Add a function
def articles_skill_params2
params.require(:articles_skill).permit(:description, :name)
end
Add another function:
def set_article
article_id = articles_skill_params[:article_attributes][:id]
article = Article.find(article_id)
#articles_skill.articles << article
#articles_skill.save
end
Change your ArticlesSkillController#create:
def create
#articles_skill = ArticlesSkill.new(articles_skill_params2)
set_article
respond_to do |format|
if #articles_skill.save
format.html { redirect_to #articles_skill, notice: 'Article skill was successfully created.' }
format.json { render :show, status: :created, location: #articles_skill }
else
format.html { render :new }
format.json { render json: #articles_skill.errors, status: :unprocessable_entity }
end
end
end
As you can see, we simply exclude the nested attributes from the parent object creation (thus eliminating the error), then manually add them later.

If you just want people to be able to select an existing skill you don't need nested attributes at all (that's useful for when you might want people to be able to create an article skill from the same form that creates an article). You just want to set article_skill_id to an existing value, so you can just do
<%= form_for(#article) do |f| %>
...
<label class="medium"><span class="red">*</span> Skill</label>
<%= f.select :article_skill_id, ArticlesSkill.all.collect{|m| [m.name, m.id]}) %>
<% end %>

Related

Create new record for Join Table from a form.select input

First steps with RoR, trying to wrap my head around basic concepts. Following excercise: I have pupils and schoolclasses, both Active Record entities with a many to many (has_and_belongs_to_many) to each other. Now I have a form to create a new pupil. On this form there is also a form.select to pick the class for the pupil, but I can´t get this to work, I can´t get the controller to create a new record for the join table.
Schoolclass.rb
class Schoolclass < ApplicationRecord
has_and_belongs_to_many :pupils
end
Pupil.rb
class Pupil < ApplicationRecord
has_and_belongs_to_many :schoolclasses
end
Relevant part of the _form.html.erb
<div class="field">
<%= form.label :schoolclass %>
<%= form.select(schoolclass.id, schoolclasses_for_select) %>
</div>
schoolclasses_for_select is just a helper for populating the select box
def schoolclasses_for_select
Schoolclass.all.collect{ |s| [s.name, s.schoolyear] }
end
Everything I have tried on the controller has failed miserably. Somehow, I mostly end up with the controller trying to pass the schoolclass (as a String) as an attribute to the new Pupil, or with a MethodNotFound error. In my understanding it should work something like this :
#klass = params[:schoolclass]
pupil.schoolclasses << #klass
but it doesn´t.
Thanks in advance for any help.
Edit1: the create code
def create
#pupil = Pupil.new(pupil_params)
respond_to do |format|
if #pupil.save
format.html { redirect_to #pupil, notice: 'Pupil was successfully created.' }
format.json { render :show, status: :created, location: #pupil }
else
format.html { render :new }
format.json { render json: #pupil.errors, status: :unprocessable_entity }
end
end
end
def pupil_params
params.require(:pupil).permit(:nachname, :vorname, :schoolclass)
end
That is the part that works. What I haven't managed is to find the correct Schoolclass record and pass it to the pupil.
Issues
First argument to your form.select should be the field name i.e. :schoolclass_id. You can still keep the label Schoolclass.
I believe you want id of schoolclass to be passed in params when selected. For that to happen, change your options for select to Schoolclass.all.collect{ |s| [s.name, s.id] }
Biggest, Your association says a pupil can have multiple schoolclasses but your form doesn't support it. Have you handled it some other way?
Fixes
So, do something like (this does not support multiple schoolclasses selection):
<%= form.select :schoolclass_id, Schoolclass.all.collect{ |s| [s.name, s.id] } %>
And in your controller
def create
#pupil = Pupil.new(pupil_params)
# Find schoolclass from `schoolclass_id` and associate it to `#pupil`
schoolclass = Schoolclass.find(params[:pupil][:schoolclass_id]) # Handle case when schoolclass not selected in form
#pupil.schoolclasses |= [schoolclass]
respond_to do |format|
...
end
end
private
def pupil_params
params.require(:pupil).permit(:nachname, :vorname)
end

Nested Form not rendering or saving data

I'm new to Ruby and also Rails, and I'm trying to put together a nested form that ultimately creates a page but also allows the user to create individual parts inline before submitting the page. The inline form contains two buttons, one adds a part and the other one removes it. Here are my relevant files (please let me know if you need to see any other) and I'll list the problems I'm having after:
FYI, the gems I'm using are: Slim, Simple Form, cocoon and Bootstrap. On Rails 4.
_form.html.slim
= simple_form_for(#page, html: { class: 'form-horizontal' }) do |f|
= f.input :title, label: 'Title'
= f.input :description, label: 'Desc'
.form-group
.col-xs-10
label Parts
.form-inline
= f.simple_fields_for :parts do |part|
= render 'part_fields', f: part
.links
= link_to_add_association 'Add Part', f, :parts
= f.button :submit
_parts_fields.html.slim
= f.input :part_type, collection: ['String 1', 'String 2'], prompt: 'Part type', label: false
= f.input :part_title, placeholder: 'Part title', label: false
= f.input :part_desc, placeholder: 'Part description', label: false
= link_to_remove_association 'Remove Part', f
/models/page.rb
class Page < ActiveRecord::Base
belongs_to :project
has_many :parts
accepts_nested_attributes_for :parts
end
/models/part.rb
class Part < ActiveRecord::Base
belongs_to :page
end
/controllers/pages_controller.rb
class PagesController < ApplicationController
def index
#pages = Page.all
respond_to do |format|
format.html
format.json { render json: #pages }
end
end
def new
#page = Page.new
respond_to do |format|
format.html
format.json { render json: #page }
end
end
def edit
#page = Page.find(params['id'])
end
def create
#page = Page.new(page_params)
respond_to do |format|
if #page.save
format.html { redirect_to(#page) }
format.json { render json: #page, status: :created, location: #page }
else
format.html { render action: 'new' }
format.json { render json: #page.errors, status: :unprocessable_entity }
end
end
end
def update
#page = Page.find(params['id'])
respond_to do |format|
if #page.update_attributes(page_params)
format.html { redirect_to(action: 'index') }
format.json { head :ok }
else
format.html { render action: 'edit' }
format.json { render json: #page.errors, status: :unprocessable_entity }
end
end
end
def page_params
params.require(:page).permit(:title, :description, parts_attributes: [:id, :part_type, :part_title, :part_desc])
end
end # End Pages Controller
Routes
resources :projects do
resources :pages
end
resources :pages
resources :parts
Problems:
1) The form is not saving any data (can't edit/update current pages or create new ones). Update: fixed, see my own answer below.
2) On the partial I'm using a collection to have a dropdown menu, but those test values are hard-coded right now. How can I have a dropdown that populates each field with columns from the db?
3) The inline form elements which come from the partial are not being rendered back on the main form. All I see is the "Parts" label and no elements underneath it. Update: #pages.parts.build solved this.
Appreciate any help you guys can give me.
You have a #page object with no parts. So when it tries to render the fields_for, it does! Just, zero times. Add #page.parts.build in your controller to add one.
Not sure; too tired right now. Try changing save to save! and update_attributes to update! so Rails throws an error instead of failing silently. BTW, why do you have if params[:page] inside your page_params? That isn't needed; that's what require(:page) does.
ActiveRecord provides a column_names method, that returns an array of column names.
Actually, #page.parts.build will always build a new part, even if the user did not request this (it could be what you want). Now you only show the Add button for each nested part. I would assume you would want only one Add button, as follows:
.form-inline
= f.simple_fields_for :parts do |part|
= render 'parts_fields', f: part
= link_to_add_association 'Remove Part', f, :parts
= link_to_add_association 'Add Part', f, :parts
This will always show the Add Part link (and only once).
As to not saving, possible reasons are:
the data is not posted to the server (check your logfile, possible reasons are errors in your html, e.g. multiple identical ids)
the data is blocked by your strong-parameters command
the data is blocked by the reject_if condition of the accepts_nested_attributes_for
a failed validation
Now, if the code displayed is your actual code everything seems ok. So could you show us what is posted to the server? (check your logfile).
The form is now saving data as expected. In one of my partials I had two fields that were identical and thus creating a silent conflict for me. In addition, after I resolved this I started to get an Unpermitted parameter: _destroy error, and the fix was to add :_destroy to the list of required params for parts_attributes.

Ruby on Rails nested attributes not saving into database?

I'm trying to create a list of items within a "Todo list", however, I'm not sure if I'm doing this correctly with nested attributes. I think using a nested attribute is the right attempt because there's going to be a large list of items, and it will be associated with the correct "Todo list" based on ids.
Example of what the tables might look like when records are populated
Todo table
id list
1 grocery shopping
2 health insurance
Item table
id todo_id name
1 1 buy milk
2 1 buy cereal
3 2 Blue Shield
4 2 Healthnet
5 1 buy cherries
Although, with my attempt below, my application is not saving any of the data into the Item database.
Todo Controller
class TodoController < ApplicationController
def new
#todo = Todo.new
#todo.items.build
end
end
Todo Model
class Todo < ActiveRecord::Base
belongs_to :user
has_many :items
accepts_nested_attributes_for :items
end
Item Model
class Item < ActiveRecord::Base
belongs_to :todo
end
Todo View
<%= simple_form_for(#todo) do |f| %>
<%= f.input :list %>
<%= f.simple_fields_for :items do |g| %>
<%= g.input :name %>
<% end%>
<%= f.button :submit %>
<% end %>
I was able to have the name field show up in my view, but when I save it, it doesn't save into the database, however, I'm able to save the list into the database, and then when I try to edit the record, the name field doesn't show up anymore to be able to edit.
EDIT: to show create method
This is my current Create Method in Todo Controller
def create
#todo = Todo.new(todo_params)
respond_to do |format|
if #todo.save
format.html { redirect_to #todo, notice: 'Todo was successfully created.' }
format.json { render :show, status: :created, location: #todo }
else
format.html { render :new }
format.json { render json: #todo.errors, status: :unprocessable_entity }
end
end
end
Not sure if Edit needs to have something, but I only have this from generating a scaffold of Todo
def edit
end
EDIT 2 show todo_params
def todo_params
params.require(:todo).permit(:user_id, :list)
end
You must add the nested params to your strong params
def todo_params
params.require(:todo).permit(:user_id, :list, items_attributes: [:id, :text, ...])
end
Note about todo_id :
You don't need to add :todo_id in items_attributes list, because you already have the TODO as context.
#todo = Todo.new(todo_params)
In the above code, your todo_params will contain some item_attributes linked to #todo. ie, it's similar to doing
#todo.items.build
It will already create an item with a todo_id corresponding to #todo.id
You need to add the items to the list of whitelisted attributes
def todo_params
params.require(:todo).permit(
:user_id,
:list,
items_attributes: [ # you're missing this
:id,
:name
]
)
end

Inserting Multiple values from a single form Rails 4

Hello all i am trying to insert multiple records into the table using the same form so far i have achieved the following
class ProjectController < ApplicationController
def new
#project = Project.new
end
def create
#projec = Project.new(project_params)
respond_to do |format|
if #project.save
format.html { flash[:notice] = 'User successfully created.' and redirect_to action: "index"}
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
def project_params
params.require(:project).permit(:PROJECT_ID,:COMPANY_ID, :ASSESSMENT_ID, :PROJECT_SCORE , :CREATED_BY, :UPDATED_BY)
end
end
so in my view i have used like this since i want only the project level score to be saved into the database with all other values remaining the same
<% 10.times do %>
<%= f.range_field :PROJECT_SCORE[], :min=>0, :max=>10, :class=>"slide", :id=>"slider1", name: 'PROJECT_SCORE[of_values][]'%>
<% end %>
next in my model i have used like this
class Project< ActiveRecord::Base
serialize :PROJECT_SCORE ,Array
end
but i receive an error
Attribute was supposed to be a Array, but was a Fixnum. -- 0
SO is there any alternate ways to insert multiple records in the table at the same time ? or how do i solve this issue ?
There is an alternate way for inserting multiple records and I would highly recommend doing so. You could get this to work but it feels a bit hacky and not very flexible to me. Try using a has_many association and nested forms instead. If you're not familiar with has_many associations there is an introductory course at codeschool.com called Models Taste Like Chicken. There is also a great RailsCasts episode (#196) that goes into detail about nested forms with some cool AJAX features.
To do this you could create a Model called Score and tell it to belong the projects:
rails g model Score score:integer project:references
class Score < ActiveRecord::Base
belongs_to :project
end
And each project will have many scores:
class Project < ActiveRecord::Base
has_many :scores, dependent: :destroy
accepts_nested_attributes_for :scores, allow_destroy: true
end
The dependent destroy makes sure the associated scores get deleted if a Project is deleted. You also need to tell it to accept nested attributes for the scores model. Details here.
Next, set up the strong parameters with things_attributes:[:thing1, :thing2, :etc]
class ProjectController < ApplicationController
### other stuff
def project_params
params.require(:project).permit(:PROJECT_ID, :etc, scores_attributes: [:id, :score])
end
One thing I would like to mention here is the best practice for
naming conventions is to use snake_case for your
variables and database names. So instead of :PROJECT_ID name it
:project_id
ALL_CAPS is usually used for constants.
Now your view you can use the fields_for form helper to create another form block within the form block.
<%= form_for #project do |f| %>
<div class="field">
<!-- all normal form inputs -->
</div>
<!-- and now the nested form -->
<%= f.fields_for :scores do |ff| %>
<div class="field">
<%= ff.range_field :score %>
</div>
<% end %>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
This won't allow you to create new scores yet, but that starts to get really complicated and is explained in the RailsCasts I mentioned above (However he is using an older version of rails so make sure and set the strong parameters for the nested attributes).

How to change value in params array in Ruby on Rails?

I have an instance where I am recording prices for water from vendors. My vendor model has :price. However, I want to give users the option to input a price for different volumes, and do the simple division for them rather than having them to do it. In other words, users should be able to input $1.99 per liter or $3.99 for a gallon and so on. To do this, I need a virtual attribute in my form for :unit, since I don't want to be storing units in the table. Everything works well, except that I cannot seem to update vendor_params[:price] before I update the record or create a new record. This seems like it should be a cake walk, but I Googled most of the day and can't figure out how to make it work.
Here is what I have:
Model:
class Vendor < ActiveRecord::Base
attr_accessor :unit
...
end
Form:
<%= form_for(#vendor) do |f| %>
...
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
<%= select( "unit", "id", { "1 Liter" => "1", "Bottle (2 liters)" => "2", "Jerry Can (20 liters)" => "20"}) %>
</div>
...
<% end %>
Controller:
...
def update
vendor_params[:price] = vendor_params[:price].to_f/params[:unit][:id].to_f
respond_to do |format|
if #vendor.update(vendor_params)
format.html { redirect_to #vendor, notice: 'Vendor was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #vendor.errors, status: :unprocessable_entity }
end
end
...
end
I know that vendor_params[:price].to_f/params[:unit][:id].to_f returns the correct value. I just can't seem to assign that value to vendor_params[:price] before I update the record. I also tried the following which throws an error:
#vendor_params[:price] = vendor_params[:price].to_f/params[:unit][:id].to_f
It seems like this should be trivial! I guess I could use form_tag instead of form_for, but that seems odd when updating the full record. (The edit form has all fields for all the object attributes.) Anywho, I'm open to ideas and suggestions.
Thanks!!
If vendor_params is a strong_params method (which I'm assuming it is), it actually creates a new hash. So when you alter vendor_params... you're not actually changing your original params hash.
OK, why isn't vendor_params changing though... I dont care about params? WELL, vendor_params still points the original params hash assuming it looks something like:
def vendor_params
params.require(:vendor).permit(:price)
end
I think the link below is a similar issue and may present a useful solution. Hope I understood your problem correctly!
Modify ruby hash in place( rails strong params)

Resources