Create with multiple params issue - ruby-on-rails

I have a controller that looks like this:
def new
#columns = Column.where(:table_id => #table.id)
#row = Row.new(id: #table.id)
end
def create
row_params.each do |row_param|
#row = Row.new(row_param)
#row.column_id = params["column_id"]
if #row.save
redirect_to collection_rows_path, notice: 'item was successfully created.'
else
render action: 'new'
end
end
end
I have a form that looks like:
<%= form_for [#table, #row] do |f| %>
<% #columns.each do |column| %>
<%= column.id %>
<%= hidden_field_tag :column_id, column.id %>
<%= f.label :data %><br>
<%= f.text_field :data %>
<% end %>
<%= f.submit %>
<% end %>
Basically, I'm trying to send multiple params and have them inserted with the column. But I keep getting this error:
undefined methodstringify_keys' for ["data", "No"]:Array` when there is two columns which means there is two text fields and I insert "Hello" in the first one, and "No" in the second.
Two things: Why is it only reading the "No" on the second one instead of both the "Hello" and "No"? And also why am I getting this error?
Thanks for all help!

Answers to your questions:
It is only reading "No" which is your input in the last "Data" text_field since the two text_fields generated in your form_for save their input value in the same params key which is params[:row][:data]. What happens then is the latest value saved to the params[:row][:data] key overrides any previous value it had.
The error undefined method stringify_keys' for ["data", "No"]:Array happens because you create 2 text_fields with the same name which is :data. When you submit the form, an Array is being submitted instead of a String that Rails expects when using text_field.
Solution to your problem:
This seems like an ideal use case for using a nested model form. Basing on your code, it looks like Row belongs_to Table. So in your Table model you'll need to add this code:
#app/models/table.rb
accepts_nested_attributes_for :row
Then add the following code in your RowsController:
#app/controllers/rows_controller.rb
def new
#columns = Column.where(:table_id => #table.id)
#columns.each do |column|
#table.rows.build(column_id: column.id)
end
end
def create
#table = Table.new(table_params)
if #table.save
redirect_to collection_rows_path, notice: 'item was successfully created.'
else
render :new
end
end
private
def table_params
params.require(:table).permit(rows_attributes: [:data, :column_id])
end
Then in your 'rows#new' view:
#app/views/rows/new.html.erb
<%= form_for #table, url: rows_path ,method: :post do |f| %>
<%= f.fields_for :rows do |r| %>
<%= r.object.column.name %>
<%= r.hidden_field :column_id, value: r.object.column_id %>
<%= r.label :data %><br>
<%= r.text_field :data %>
<% end %>
<%= f.submit %>
<% end %>
What the above code will do is allow you to create multiple rows for a column according to the number of columns the table has. This won't work though if a #table has no #columns yet. This assumes that you've created #columns for the #table already. Basing on your code though, it seems like that's already what you're doing.

you want to store 'data' as array in Row
In Rails model Row add
serialize :data, Array
in view
text_field_tag 'row[data][]'
You are getting only 'No' because form for does not know its an array so , it picks the last one And you are getting this error because rails does not know you want to store it as array , it excepts a string but got an array instead.

Related

Redirecting to "show" view of a model object when searched by attributes (NOT id)

I have a form where users look for a particular bill by some attributes of that bill, namely the "Congress Number", "Bill Type", and "Bill Number", as in 114-H.R.-67 . I want to "show" the appropriate bill, but to do that I have get the appropriate bill model in a separate action which I've called "find_by_attributes". Inside this action I perform:
#bill = Bill.find_by( params ).first
which correctly acquires the appropriate bill's id.
Now I simply want to redirect to the "show" method of this bill, as in the url
".../bills/[#bill.id]"
As of right now, at the end of my "find_by_attributes" action I do
redirect_to bills_path(#bill)
which correctly loads the show.html.erb with #bill, but does not change the url (the url is still shows the "find_by_attributes" action followed by a long query-string, instead of the clean "/bills/[:bill_id]".
How can I restructure my code to achieve the neat redirect that I desire?
Full code below:
THE FORM
<%= form_tag("bills/find_or_create", :method => :get ) do |f| %>
<%# render 'shared/error_messages', object: f.object %>
<%= fields_for :bill do |ff| %>
<%= ff.label :congress, 'Congress (i.e. 114)' %>
<%= ff.number_field :congress, class: 'form-control' %>
<%= ff.select :bill_type, options_for_select(
[['House of Representatives', 'hr'],
['Senate', 's'],
['House Joint Resolution', 'hjres'],
['Senate Joint Resolution', 'sjres'],
['House Concurrent Resolution', 'hconres'],
['Senate Concurrent Resolution', 'sconres'],
['House Resolution', 'hres'],
['Senate Resolution', 'sres']]
)
%>
<%= ff.label :bill_number, 'Bill number (i.e. 67)' %>
<%= ff.number_field :bill_number, class: 'form-control' %>
<% end %>
<%= submit_tag "Submit", class: "btn btn-primary" %>
<% end %>
THE CONTROLLER ACTIONS
def find_by_attributes
#bill = Bill.where(bill_params).first_or_create(bill_attributes)
redirect_to bills_path(#bill)
end
def show
puts bill_params
if params[:bill]
#bill = Bill.where(bill_params).first_or_create do |bill|
bill.attributes = bill_attributes
end
else
#bill = Bill.find(params[:id])
end
#subjects = Subject.where("bill_id = ?", #bill[:id])
#bill_comments = Comment.where("target = ?", #bill[:id])
end
ROUTES FILE
...
resources :bills do
get :find_by_attributes
end
...
EDIT
I make use of the turbolinks gem in my rails application.
the thing I see here is that you are calling to
redirect_to bills_path(#bill)
that in theory is not the show path, you just need to remove the "s"
redirect_to bill_path(#bill)
and as a side comment, in this line, you don't need the first part, because find_b, finds the first record matching the specified conditions, you can remove that part.
#bill = Bill.find_by( params )

Rails - inserting multiple records/dynamic form

I'm working on a dynamic form in a Rails app, and I need to insert a variable number of records into a model in a single form submission. I've done this using PHP -> MySQL/Postgres before, but I have no idea how to do it in Rails.
Ultimately, users should be able to create any number of records to be inserted, but in my example below, I'm limiting it to 2... let me see if I can do that, first...
Here's the form - the ids all get a unique suffix because they are being populated dynamically from localStorage objects on submission.
new.html.erb
<%= form_for #entry, html: {id: :new_entry_form} do |f| %>
<% for i in 0..1 %>
<%= f.text_field :name, :id => 'name_#{i}' %>
<%= f.text_field :day, :id => 'day_#{i}' %>
<% end %>
<% end %>
Here's the associated controller - I'm sure that this is missing something, but I don't know what.
def new
#entry = Entry.new
end
def create
#entry = Entry.create(entry_params)
redirect_to "http://localhost:3000/entries"
end
private
def entry_params
params.require(:entry).permit(:name, :day)
end
Any help would be much appreciated.
Follow this link it shows how to create multiple object in one form submit:
http://vicfriedman.github.io/blog/2015/07/18/create-multiple-objects-from-single-form-in-rails/

New model insert overwriting others

I have a rails application where I have two models called Column and Row:
Column:
has_many :rows
accepts_nested_attributes_for :rows, :reject_if => lambda { |b| b[:data].blank? }
Row:
belongs_to :column
And the form:
<%= form_for [#table, #row] do |f| %>
<% #columns.each do |column| %>
<%= f.label :data %><br>
<%= f.text_area :data %>
<% end %>
<%= f.submit %>
<% end %>
and my create action in the controller:
#row = Row.new(row_params)
if #row.save
redirect_to table_rows_path, notice: 'row was successfully created.'
else
render action: 'new'
end
and my new action:
#columns = Column.where(:table_id => #table.id)
#row = Row.new(id: #table.id)
So I have two problems. The first is if I have say two columns, so there will be two textfields on the new row page, and I enter "Test" in the first text field and "Another Test" in the second. The only thing that is getting saved is the second. The first one saves "Another Test" instead of "Test".
Also, how can I get the row (which belongs to a column) to save a column_id inside each row?
Thanks for all help!
You are setting the new Row's id to the #table.id. Take that out; you never need to create a new .id. Then use column_id: params[:column_id] to link them up.

Create missing objects before form loads in rails

I am just trying to make my form display a blank field for each item that has not yet been created. I've done this successfully using the build method for more simple forms but I'm not sure how to do it for this case. Is the strategy I'm using to do this wrong or am I making a simple syntax mistake?
Here's the basic model setup:
A Comp has many Rounds and many Teams. A Round has many Items. Each Team has only 1 Item per Round
So when the form is loaded, if a team has not already created an item, I want there to be a blank item created for that team so that it shows up in the form and can be edited for that team.
I tried 2 different methods in my controller and neither has worked:
Method 1:
def edit_admin
#comp = Comp.find(params[:comp_id])
#round = #comp.rounds.find(params[:round_id])
team_ids = #round.items.all(:select => :team_id).collect(&:team_id)
#comp.teams.each do |team|
if team_ids.include? team.id == false
new_item = #round.items.new(:team_id => team.id, :round_id => #round.id)
new_item.save
end
end
end
def update_admin
#comp = Comp.find(params[:comp_id])
#round = #comp.rounds.find(params[:round_id])
if #round.update_attributes(params[:round])
redirect_to(edit_comp_path(#comp))
else
render 'edit_admin'
end
end
Method 2:
Essentially the same thing but I defined a method to run before the page loads:
before_filter :build_new_items, :only => :edit_admin
private
def build_new_items
#comp = Comp.find(params[:comp_id])
#round = #comp.rounds.find(params[:round_id])
team_ids = #round.items.all(:select => :team_id).collect(&:team_id)
#comp.teams.each do |team|
if team_ids.include? team.id == false
new_item = #round.items.new(:team_id => team.id, :round_id => #round.id)
new_item.save
end
end
end
The form looks like this (the view is called edit_admin.html.erb):
<%= form_tag update_admin_comp_round_items_path(#comp,#round), :method => :put do %>
<% for item in #round.items.all %>
<%= "Team: " + #comp.teams.find(item.team_id).team_name %> <br />
<%= fields_for 'round[items_attributes][]', item do |f| %>
<%= f.label :item_name %>
<%= f.text_field :item_name %> <br />
<%= f.hidden_field :id, :value => item.id %> <br />
<% end %>
<% end %>
<p><%= submit_tag "Submit" %></p>
<% end %>
Thanks.
Answering my own question since no one else did: I got method B to work with a very minor tweak. All it needed was parentheses around the value after "include?", like so:
if team_ids.include?(team.id) == false.
I figured this out by playing with each line of code in the rails console.
Method A may work as well but since I got method B to work, I haven't yet tried it.
UPDATE: method A also works

Rails linking form submission to object

In a rails project I have two entities, Users and Institutions, they have a many-to-many relationship.
The views for them are set up to create new users and institutions but I want to have another view for linking the two.
In rails console all I have to do is
myuser.institutions << the_institution_i_just_created
The controller can do some of the work but how do I handle the submissions and the forms? I want to use a selection box so that the input is limited to the Institutions already in existence.
<select id="institution_selection" name="institution_sel">
<% selections = []
Institution.all.each do |institution|
pair = [institution.name, institution.id]
selections.concat([pair])
end
%>
<%= options_for_select(selections) %>
</select>
So the question in summary is how do I map this submission to an object so that in the controller I can do add it to the relation?
The solution was:
Alright, so this is the solution I came up with, I'm sure there is a better way to go about it and I'll continue to look into it but at least I got something close to what I was aiming for
def test
if !session[:user]
redirect_to users_path, notice: "Please login first"
end
if params[:institution]
#user = User.find(session[:user])
#institution = Institution.find(params[:institution][:id])
#user.institutions << #institution
redirect_to #user, notice: "Institution was successfully added "
end
end
and for the view
<%= form_tag("/users/test", :method => "post") do %>
<%= collection_select :institution, :id, Institution.all, :id, :name %>
<%= submit_tag("Search") %>
<% end %>
Use collection_select
<% from for #instancevar do |form| %>
<%= form.collection_select :institution_id, Institution.all, :id, :name %>
# Do other stuff....
<% end %>

Resources