Inserting Multiple values from a single form Rails 4 - ruby-on-rails

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

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

Using collection select in rails

I am trying to create a simple blog application. In which each post can be associated with the tags. This is my view
_form.hmtl.erb
<%= form.collection_select(:tag_ids, #tags, :id, :name, {}, :multiple => true) %>
This is my controller
posts_controller.rb
def new
#post = #topic.posts.new
#tag = #post.tags.new
#tags = Tag.all
end
def create
#post = #topic.posts.new(post_params)
if #post.save
redirect_to topic_posts_path
else
render 'new'
end
end
def post_params
params.require(:post).permit(:title,:content,:post_image, tag_ids: [])
end
end
Model Post.rb
has_and_belongs_to_many :tags
I am getting an error while creating new post "NoMethodError in Posts#create", "undefined method `map' for nil:NilClass". I am not able to find where the error is.
You should have a join table with name posts_tags.rb
class Post_Tag < ApplicationRecord
belongs_to :tag
belongs_to :post
end
so you have already some tags which you want to be selected with post and join table should be updated with post_id and tag_id
change this in your form_partial: -
<%= select_tag "tag_ids", options_for_select(#tags,:id,:name),{}, multiple: true%> <br/><br/>
Note: - you should not use form.collection_select(:tag_ids...) Reason being form_for is used here for #post object and tag_ids is not attribute of #post object.
so on submitting form you should get array of tag's ids in params[:tag_ids]
def create
#post = #topic.posts.new(post_params)
if #post.save
#create join table for tags that are associated with post
#post.tags << Tag.find(params[:tag_ids])
redirect_to topic_posts_path
else
render 'new'
end
end
so here you can get
#post.tags => which will return you collection of tags associated with the post
if you are using this scenario you have to make MANY TO MANY relation between these two models.
has_and_belongs_to_many :tags
then in form you should use like
form.collection_select(:post, :tag_id, Tag.all, :id, :name_with_initial, prompt: true)
The #tags variable is missing from your create action in controller - you have declared it in new, but not in create. It may cause error in view if save was unsucessful and action tries to re-render the form.
Not sure if that's all, because I'm not sure where the error is thrown exactly. But as you said in one of the comments - it works when you use Tag.all instead of #tags.

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

Couldn't find ArticlesSkill with ID=1 for Article with ID=

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 %>

Ruby on rails 2 level model

I need some help creating a very simple forum in a existing model.
What I want in a Game page, have a mini forum, where is possible create some topics and some comments to this topics. In the beginning I'm only implement topics.
This is the error I have:
Mysql2::Error: Column 'user_id' cannot be null: INSERT INTO `topics` (`game_id`, `question`, `user_id`) VALUES (1, 'asd', NULL)
This is my main model:
game.rb
class Game < ActiveRecord::Base
attr_accessible :name
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 50 }
belongs_to :user
has_many :topics, dependent: :destroy
end
topic.rb
class Topic < ActiveRecord::Base
validates_presence_of :question
validates_presence_of :game_id
attr_accessible :question, :user_id
validates :question, length: {maximum: 50}, allow_blank: false
belongs_to :game
belongs_to :user
end
topic_controller.rb
def create
#game = Game.find(params[:game_id])
#topic = #game.topics.create(params[:topic])
#topic.user_id = current_user.id
respond_to do |format|
if #topic.save
format.html { redirect_to #game, notice: 'Topic was successfully created.' }
else
format.html { render action: "new" }
end
end
end
game/show.html.erb
<h2>Topics</h2>
<% #game.topics.each do |topic| %>
<p>
<b>Question:</b>
<%= topic.question %>
</p>
<% end %>
<h2>Add a topic:</h2>
<%= form_for([#game, #game.topics.build]) do |f| %>
<div class="field">
<%= f.label :question %><br />
<%= f.text_field :question %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Thanks ;)
I believe the issue you're experiencing is the difference between create and new in Rails.
Using new merely initializes the model, allowing you to save/validate later; using create will perform all of those steps in one command, causing the database row to be created.
So when you attempt to perform
#game.topics.create(params[:topic])
Rails attempts to create a Topic using params[:topic] and setting the game_id to #game.id, after which it immediately attempts to validate this new Topic that it created and save it to the database.
Potential options that you can consider:
1) Use #game.topics.new(params[:topic])
2) merge {:user_id => current_user.id} in: #game.topics.create(params[:topic].merge({:user_id => current_user.id})
I'd personally suggest option 1 (i.e. using new instead), but I have seen option 2 used before.
EDIT: Another issue that it looks as though you might be experiencing: should current_user be #current_user in your code?
Side note:
generally, if create fails to create database row, it will still work (returning the initialized model instead), but in your case it looks like this won't happen due to database-level restrictions on user_id being NOT NULL, causing an uncaught error.
You may want to consider reading the Rails Guide on nested resources. I've been where you are now , take a look at this discusion.
I guess you're accessing the site without being logged in, so user_id will not be set. You should ensure that there is a logged in user for all actions that are modifying or creating a topic. A simple approach can be found in this Railscast.
I think current_user.id is not setting properly, Do inspect these issue are almost all the other issues, ruby debugger is the beset way
in your GEM file add
GEM file
gem 'debugger'
run bundle install
then in your controller
def create
#game = Game.find(params[:game_id])
#topic = #game.topics.create(params[:topic])
#topic.user_id = current_user.id
debugger
respond_to do |format|
if #topic.save
format.html { redirect_to #game, notice: 'Topic was successfully created.' }
else
format.html { render action: "new" }
end
end
end
and this will stop you in the the debugger line, then from the console you could see if the values are set or not. check this for more

Resources