Rails: Create a survey with the data in a database table - ruby-on-rails-3

I'm currently trying to create a survey which will use questions that are stored in a table. I've read nested model form part 1 from rails casts however i'm not getting anywhere as the questions are not displaying in the survey.
I have three tables, one tables has the text of the questions, another table keeps the record of who entered the survey and a third table which keeps the answers from a user for the questions.
variable table:
name: varchar
id: integer
report table
employee name: varchar
date: date
id: integer
report_variable table
question_id
report_id
answer
Code i modified for reports/new:
# GET /reports/new
# GET /reports/new.json
def new
#report = Report.new
#variable = #report.variable.build #dont know what to do here, gives an error with report_id
respond_to do |format|
format.html # new.html.erb
format.json { render json: #report }
end
end
modified report/_form.html.erb
<div >
<%= f.fields_for :variable do |builder| %>
<%= render variable_fields, :f => builder %>
<% end %>
</div>
created report/_variable_fields.html.erb
<p>
<%= f.label :name %>
<%= f.text_field :name, :class => 'text_field' %>
<p>
model for report_variable
class ReportVariable < ActiveRecord::Base
attr_accessible :report_id, :value, :variable_id
has_and_belongs to many :reports
has_and_belongs to many :variables
end
model for report
class Report < ActiveRecord::Base
attr_accessible :employeeName
has_many :report_variable
has_many :variable
accepts_nested_attributes_for :report_variable
accepts_nested_attributes_for :variable
end
Sorry if it's a simple question, im pretty new to rails.

Welcome to Rails!
I think the simple answer is the fields aren't showing up because there aren't any nested records. You can probably get around that by uncommenting the variable line as you have it:
def new
#report = Report.new
#report.variables.build #this line creates 1 new empty variable, unsaved.
respond_to do |format|
format.html # new.html.erb
format.json { render json: #report }
end
end
If you want more than one variable, call something like:
3.times { #report.variables.build }
That way the #report object you're placing in the form helper will have three variables on it. This should get you moving again, the harder thing is going to be adding ajax addition / removal of variables, but if you know how many there are in advance you don't have to deal with that.
Good luck!

Related

Rails - Saving in a table and update/creating in another one

I am creating a small app to manage my own online portfolio but I can't solve an issue with update.
The main table of my database is works, then there's the tables authors and clients where I set the fields authorName and clientName as unique. Authors and clients can have multiple works, but a work can only have one of each.
In the form where I create works I have a field for the authorName and another one for clientName: if the author doesn't exist in the authors table it gets created, otherwise the existing one gets linked to the work. Same thing with the client. To achieve this I am using first_or_initialize and it works perfectly. Problems start when I try to use the same method inside the update action. These are my models:
Work Model
class Work < ApplicationRecord
has_one :description
belongs_to :author
belongs_to :client
accepts_nested_attributes_for :client
accepts_nested_attributes_for :author
scope :active, lambda {where(:isActive => true)}
scope :descOrder, lambda {order(:date => :desc)}
scope :cover, lambda {where(:isCover => true)}
end
Author Model
class Author < ApplicationRecord
has_many :works
end
Client Model
class Client < ApplicationRecord
has_many :works
end
Quick edit: the models as you see them are exactly as they are in my app. No code has been removed.
This is the form in my view:
<%= form_for(#work, :url => { :controller => "projects", :action => "update"} ) do |f| %>
<%= f.label("title") %>
<%= f.text_field(:title) %>
<%= f.fields_for(:author) do |author| %>
<%= author.label("author") %>
<%= author.text_field(:authorName) %>
<% end %>
<%= f.fields_for(:client) do |client| %>
<%= client.label("client") %>
<%= client.text_field(:clientName) %>
<% end %>
<%= f.label("date") %>
<%= f.date_field(:date) %>
<%= f.submit("update") %>
<% end %>
And this is how I am handling it in the controller:
def edit
#work = Work.find(params[:id])
if #work.client.nil?
#work.build_client
end
if #work.author.nil?
#work.build_author
end
end
def update
#work = Work.find(params[:id])
#work.client = Client.where(clientName: work_params["client_attributes"]["clientName"]).first_or_initialize
#work.author = Author.where(authorName: work_params["author_attributes"]["authorName"]).first_or_initialize
if #work.update(work_params)
flash[:notice] = "work: #{#work.title} updated successfully."
redirect_to(project_path(#work))
else
redirect_to new_project_path
end
end
private
def work_params
params.require(:work).permit(:title, :date, client_attributes: [:id, :clientName], author_attributes: [:id, :authorName])
end
This is the error that I get:
Started PATCH "/projects/21" for ::1 at 2019-10-14 20:00:27 -0700
Processing by ProjectsController#update as HTML
Parameters: {"authenticity_token"=>"rw3X7VJg8CDnOibniv1jKHTVTGp7pjE4ep6xHpHy0Zp8Xv/0uQd6y5xqq629M2FOOQNoYyOAXH//w5/VoeNEOA==", "work"=>{"title"=>"Progetto1", "author_attributes"=>{"authorName"=>"Autore1", "id"=>"34"}, "client_attributes"=>{"clientName"=>"Cliente4", "id"=>"30"}, "date"=>""}, "commit"=>"update", "id"=>"21"}
Work Load (0.7ms) SELECT `works`.* FROM `works` WHERE `works`.`id` = 21 LIMIT 1
↳ app/controllers/projects_controller.rb:42:in `update'
Client Load (0.6ms) SELECT `clients`.* FROM `clients` WHERE `clients`.`clientName` = 'Cliente4' ORDER BY `clients`.`id` ASC LIMIT 1
↳ app/controllers/projects_controller.rb:44:in `update'
Author Load (0.6ms) SELECT `authors`.* FROM `authors` WHERE `authors`.`authorName` = 'Autore1' ORDER BY `authors`.`id` ASC LIMIT 1
↳ app/controllers/projects_controller.rb:45:in `update'
Completed 404 Not Found in 15ms (ActiveRecord: 1.9ms | Allocations: 3805)
ActiveRecord::RecordNotFound (Couldn't find Client with ID=30 for Work with ID=21):
app/controllers/projects_controller.rb:47:in `update'
Even though those record do exist in the database (with those IDs that you see in the error) and their foreign keys are correctly stored in the works table (I checked in mysql).
What I am expecting to achieve is the same behaviour of the new action (that I described in the beginning of my post).
How can I solve this? Thank you!
Small update: if I change first_or_initialize with first_or_create it does create the author (or client), if not existing, but in the same time it still gives me the same error.
After dozens of tests I eventually came up with a sort of solution - it's not "elegant" and probably it's not the best, but at least it works without interruptions:
def edit
#work = Work.find(params[:id])
if #work.client.nil?
#work.build_client
end
if #work.author.nil?
#work.build_author
end
end
def update
#work = Work.find(params[:id])
#work.client = Client.where(clientName: work_params["client_attributes"]["clientName"]).first_or_create
#work.author = Author.where(authorName: work_params["author_attributes"]["authorName"]).first_or_create
if #work.update(update_params)
flash[:notice] = "work: #{#work.title} updated successfully."
redirect_to(project_path(#work))
else
redirect_to new_project_path
end
end
private
def work_params
params.require(:work).permit(:title, :date, client_attributes: [:id, :clientName], author_attributes: [:id, :authorName])
end
def update_params
params.require(:work).permit(:title, :date)
end
Being that first_or_initialize works smoothly with save method, but it gets stuck - at least in my code - with update, I replaced it with first_or_create.
This time, however, I am using another private method update_params which simply ignores :client_attributes and :authors_attributes.
This results in an Unpermitted parameters: :author_attributes, :client_attributes error, as expected, but at least everything goes through and gets updated.
Honestly I am not quite sure that I totally understood why it's working. But it does.
Anyway, if anyone have a better solution to this problem I am totally open to improve it.

How do I create a form that will update the value of a field for selected records?

I am using a link from an index page that has a group of nested records (row) that I need to update all at once. The link goes to an edit action that I need to make update the attributes of nested records (prisms).
I tried using the simple_form gem methods for nested models. It gives me a field for all of the objects, when I only want one field to enter a value to them all. The builder from that looks usable, but I don't know how to use it to update the fields. Either way, the form isn't right.
I have tried every variation of form_for and fields_for I could find on Google to develop the edit form. It looks like I'm the only one on Earth trying to solve this problem.
This is how I have my routes set up:
resources :gardens, shallow: true do
resources :prisms
resources :rows
Here is how my garden model is now:
class Garden < ApplicationRecord
mount_uploader :picture, ImageUploader
belongs_to :user
has_one :photo
has_many :rows, :dependent => :destroy
has_many :prisms
geocoded_by :address
after_validation :geocode
after_commit :populate_garden!
def id
self[:id]
end
def populate_garden!
# row 0
(0..length-1).each do |i|
Row.create(row_num: i, garden_id: id)
end
end
end
The garden model creates my rows through the populate_garden! method.
Here is the row model:
class Row < ApplicationRecord
belongs_to :garden
has_many :prisms, :dependent => :destroy
accepts_nested_attributes_for :prisms
after_commit :populate_rows
def id
self[:id]
end
def populate_rows
# row 0
(0..garden.width-1).each do |i|
Prism.create(:row_id => self.id, :row_num => self.row_num, :col_num => i, :garden_id => self.garden_id)
end
end
end
The row model creates prisms in the populate_rows method.
Here is the prism model:
class Prism < ApplicationRecord
belongs_to :row
belongs_to :garden
include RankedModel
ranks :column_order
end
Here is the table from my index.html.erb that I click to open the edit action.
<table>
<% #rows.each_with_index do |gardenrow, index| %>
<% #rows.select { | row | row.row_num == index}.each do |row| %>
<td class="prism-cols">
<%= link_to 'Edit Row', edit_row_path(row), class:"dark-link" %>
<br /><i class="fas fa-arrow-down"></i>
</td>
<% end %>
<% end %>
</table>
The row passes nicely into the edit action, and I currently have this incorrect form:
<h1>The row brought into this form page is: </h1>
<%= #row.inspect %>
<div class="container">
<%= simple_form_for #row do |m| %>
<%= m.simple_fields_for :prisms do |p| %>
<%= p.input :crop_name %>
<% end %>
<%= m.button :submit %>
<% end %>
</div>
The rows_controller update method looks like this:
def update
#row = Row.find(params[:row_id])
#row.prisms.build
redirect_to root_path
end
I need one form field for crop_name that will change all of the prisms in the selected row with a single submit. I don't have any problems updating one prism at a time through an edit action on the prism. The difficulty I'm having is working through the nesting of prisms inside of a specific row.
With the help of my mentor below I was able to develop a form that works with the controller to make this work. Here is the updated code for later use with this type of problem.
Here is the form data:
<%= form_tag({controller: "rows", action: "update"}, method: "patch") %>
<%= label_tag(:crop_name, "Crop Name") %>
<%= text_field_tag(:crop_name) %>
<%= hidden_field_tag(:row_id, #row.id) %>
<%= submit_tag("submit") %>
Here is the controller update method:
def update
#row = Row.find(params[:id])
#garden = Garden.find_by_id(:garden_id)
#row.prisms.each do |p|
p.crop_name = params[:crop_name]
p.save!
end
redirect_to :controller => 'gardens', :action => 'show', id: #row.garden_id
end
Thanks for the help. I don't think I could have figured this out from the documentation alone.
If I'm understanding correctly, I think simple_form may be limiting you. A basic ruby form may do what you want. I'm not 100% sure what the best way is to do a simple_form on nested fields but this stackoverflow answer may be able to help more.
Using a basic ruby form
You want a form that has one field. When submitted, it will take the value from the submitted form and update that field for all prisms of that row. I would recommend digging more into the basics of ruby forms for this kind of scenario and then do something like this.
// html.erb
<%= form_tag({controller: "rows", action: "update_prism_crop_name"}, method: "post", class: "nifty_form") %>
<%= label_tag(:crop_name, "Crop name") %>
<%= text_field_tag(:crop_name) %>
<%= hidden_field_tag(:row_id, #row.id) %>
<%= submit_tag("Submit") %>
<% end %>
// rows_controller
def update_prism_crop_name
#row = Row.find(params[:row_id])
#row.prisms.each do |prism|
prism.crop_name = params[:crop_name]
prism.save!
end
# other redirect stuff
end
The form_tag explicitly calls out an action but I have to imagine that you'll need to build a route for this custom action as well.
I haven't tested any of this and I'm a bit rusty in rails but I believe something like this would work.

How to move database operations from controller to model

I've started to work on old project with new tools. This time - Rails on Ruby. I managed to make some progress, and now i want to improve one element of my code.
Whole project is about bugtracking with full history search of all tracked bugs. Now i'm on stage where user is entering bugs. Every bug belong to table which belongs to projects.
Only problem - now - is autocompletion of table name when i'm using completely new name (with tables that are already present in database it's working just fine, fills table_id in Bug entry).
Part of view responsible for entering (or selecting from existing) table looks like this:
<div class="control-group">
<%= f.label :table_name, :class => 'control-label' %>
<div class="controls">
<%= f.autocomplete_field :table_name, autocomplete_table_name_bugs_path %>
</div>
</div>
Nothing unusual. This one goes to the model (Bug.rb)
class Bug < ActiveRecord::Base
attr_accessible :bugid, :fixdate, :fixnote, :fixstate_id, :milestone, :newtarget, :notes, :oldtarget, :project_id, :bugreason, :reason_id, :regressiondate, :regressionstate_id, :source, :stringid, :table_id
belongs_to :project
belongs_to :table
belongs_to :reason
belongs_to :state
def table_name
table.name if table
end
#def table_name=(name)
# self.table = Table.find_or_create_by_name(name) unless name.blank?
#end
end
No validation for now as you can see. table_name=(name) commented as it's apparently doing nothing in my code.
This is working with bugs controller (bugs_controller.rb)
def create
if params[:bug][:table_id].nil?
if Table.find_or_create_by_name(params[:bug][:table_name])
params[:bug][:table_id] = Table.find_by_name(params[:bug][:table_name]).id
params[:bug].delete :table_name
end
end
#bug = Bug.new(params[:bug])
respond_to do |format|
if #bug.save
format.html { redirect_to bugs_path, notice: 'Bug was successfully created.' }
format.json { render json: #bug, status: :created, location: #bug }
else
format.html { render action: "new" }
format.json { render json: #bug.errors, status: :unprocessable_entity }
end
end
end
I put here only part responsible for saving new bugs, i'll manage to handle update part when i do this part right.
What i want to improve is first part. Now it's responsible not only for changing table_name to table_id but for creation of new table if it doesn't exist. I'm aware that this part should be handled by model, but i've no idea how to do that, could use some help.
Another part, as btw. is my dropdown menu where user can select active project. It's handled by partial:
<% #projects.each do |project| %>
<% if project.id == session[:current_project].to_i %>
<li class="disabled"><%= link_to project.name, '#' %></li>
<% else %>
<li><%= link_to project.name, choose_project_path(project.id) %></li>
<% end %>
<% end %>
But it works fine only when used from projects controller. How - by the book - i can handle this from other controllers? To be exact, i want it working same way in whole project.
For now i'm handling it by snippet in every controller, but i'm pretty sure that RoR gods are not happy with me for that.
before_filter :projects
def projects
#projects = Project.all
end
How it should be done in proper way? :)
So i managed to move logic from controller to model. Also, it's saving all data i need - table_id to bugs table and project_id (stored in session) to tables table when creating new table.
So part of form render is not changed:
<div class="control-group">
<%= f.label :table_name, :class => 'control-label' %>
<div class="controls">
<%= f.autocomplete_field :table_name, autocomplete_table_name_bugs_path %>
</div>
</div>
Model now looks like this:
class Bug < ActiveRecord::Base
attr_accessible :table_name, :bugid, :fixdate, :fixnote, :fixstate_id, :milestone, :newtarget, :notes, :oldtarget, :project_id, :bugreason, :reason_id, :regressiondate, :regressionstate_id, :source, :stringid, :table_id
belongs_to :project
belongs_to :table
belongs_to :reason
belongs_to :state
def table_name
table.name if table
end
def table_name=(name)
self.table = Table.find_or_create_by_name(name, project_id: self.project_id ) unless name.blank?
end
end
So only change is uncommenting table_name= method and adding project_id to creation of new table (and lack of project_id was the reason it didn't work earlier).
Controller looks like this:
def create
#bug = Bug.new(params[:bug])
#bug.project_id = session[:current_project]
respond_to do |format|
if #bug.save
format.html { redirect_to bugs_path, notice: 'Bug was successfully created.' }
format.json { render json: #bug, status: :created, location: #bug }
else
format.html { render action: "new" }
format.json { render json: #bug.errors, status: :unprocessable_entity }
end
end
end
It's working like charm. Still i've question remaining, but will post it as separate one.

Ruby on Rails pass id to new create form

Ok, I've searched high and low, read tutorials, watched videos and I am still not getting any where with this. I've read similar questions here, but questions were more complex or lacked answers - so here goes...
I have models Account and Invoice. When showing an Account, I'd like a link to 'Create new invoice' which relates to that account. (Later I'd actually like a select field to choose an Account when creating an Invoice, but I'll leave that to another excruciation).
Here are my models...
Account:
class Account < ActiveRecord::Base
attr_accessible :name, :invoice
attr_accessible :name, :invoice
has_many :invoices
end
and Invoice:
class Invoice < ActiveRecord::Base
belongs_to :account
attr_accessible :amount_pretax, :amount_total, :date_sent, :project, :status, :tax, :account, :account_id
end
Now, in my /views/accounts/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
<%= #account.name %>
</p>
<%= link_to 'New Invoice', new_invoice_path(:account_id=>#account.id) %>
<%= link_to 'Edit', edit_account_path(#account) %> |
<%= link_to 'Back', accounts_path %>
So, what's happening is, when I click on the New Invoice link it shows the new form, with the account field populated with this weird text: #<Account:0x10fe16bc0> and then when I submit the form I get this error:
ActiveRecord::AssociationTypeMismatch in InvoicesController#create
with this statement: Account(#2281084000) expected, got String(#2267210740)
along with this:
app/controllers/invoices_controller.rb:45:in `new'
app/controllers/invoices_controller.rb:45:in `create'
This is what is in the Invoices Controller:
def new
#invoice = Invoice.new(:account_id => params[:account_id])
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #invoice }
end
end
def create
#invoice = Invoice.new(params[:invoice])
....
end
The above is where I think I'm going wrong, but what to put this those lines is beyond me at the moment. I'm totally a beginner, any help to solve this functionality will surely teach me loads.
Thanks for your time.
When you click the New invoice link on the /views/accounts/show page, I suppose that you want that your new invoice belongs to this account.
So in your form, you don't have to let the user choose an account. You can for example replace the corresponding field by a hidden_field:
<%= f.hidden_field :account_id, :value => params[:account_id] %>
Also in the new action of your controller, replace #invoice = Invoice.new(:account_id => params[:account_id]) by #invoice = Invoice.new
Hope this helps.
you did not post the code of your form, but i guess that you are using a text-field for handling the account association. THIS IS WRONG!
if you use a text-field, then rails will try storing it as a string => Account(#2281084000) expected, got String(#2267210740)
you need to use some kind of relational field like a dropdown or whatever to select one of the accounts that are already there.
there are tons of good examples out there, this might help you: http://railscasts.com/episodes/102-auto-complete-association-revised

New record not being saved with any values

I started the question differently, about a collection_select, but I found out that is not the problem.
This particular model won't save any data at all. It just ignores the values in the parameters. I can only save new records with NULL values (except for the timestamp fields).
See my comment for my latest try to fix it.
I have generated a few models with the handy scaffold command. Now I have tried to change a textbox to a collection_select for linking the new entity to the correct related one.
Using rails 3.1RC4 (hopefully this is not a bug).
In the _form.html.erb I use the following code:
<div class="field">
<%= f.label :category_id %><br />
<%= f.collection_select(:category_id, Admin::Category.all, :id, :name) %>
</div>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
...all other items...
<div class="actions">
<%= f.submit %>
</div>
After I click the submit button I receive error messages. It says that the name and permalink do not comply to the validation. I don't understand however, because in the logfiles I find this:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"my token is here", "admin_branche"=>{"category_id"=>"3", "name"=>"Verzekeraars", "permalink"=>"verzekeraars", "visible"=>"1"}, "commit"=>"Create Branche"}
To me it seems that the params contain all the needed values.
For the sake of completeness I will post my create method and model below.
So far I have tried switching back and forth between collection_select and f.coll... with no success. The current setup seems most appropriate to me, based on the logs.
I have also googled a lot, but haven't been able to find the answer. Question 2280106 on this site looks the same, but it had to do with attr_accessible which I have commented out in the model (I restarted the server afterwards and retried, just to be sure).
Help is much appreciated!
branche.rb:
class Admin::Branche < ActiveRecord::Base
# attr_accessible :name, :permalink
#relationships
has_many :courses, :as => :parent
belongs_to :category
#validations
validates :name, :presence => true, :length => {:maximum => 255}
validates :permalink, :presence => true, :length => { :within => 4..25 }
end
create action in the controller:
def create
#admin_branch = Admin::Branche.new(params[:admin_branch])
respond_to do |format|
if #admin_branch.save
format.html { redirect_to #admin_branch, notice: 'Branche was successfully created.' }
format.json { render json: #admin_branch, status: :created, location: #admin_branch }
else
format.html { render action: "new" }
format.json { render json: #admin_branch.errors, status: :unprocessable_entity }
end
end
end
In the controller, you're doing this:
#admin_branch = Admin::Branche.new(params[:admin_branch])
You should do this:
#admin_branch = Admin::Branche.new(params[:admin_branche])
If you look at the request parameters, the attributes are under "admin_branche", not "admin_branch".
I think that should solve your problems, if not, please let us know.
If you have problems with the generated inflections, you can completely customize them in the config/initializers/inflections.rb
just add something like this:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'branch', 'branches'
end