I am new to ruby on rails and I want to store data of a row in the database table. How can I do this. How do I pass data
The route:
post 'matches/:id', to: 'matches#savedata', as: 'savedata'
The erb form:
<%= form_with(url: savedata_path, local: true) do |f| %>
<% #result.each do |r| %>
<% #i = 0 %>
<% #j = 1 %>
<tr>
<td><%= r[#i] %></td>
<td><%= r[#j] %></td>
<td><%= #match.start_date %></td>
<td>
<select>
<option value="<% r[#i] %>" ><%= r[#i] %></option>
<option value="<% r[#j] %>" ><%= r[#j] %></option>
</select>
</td>
<td>
<%= f.submit "Save" %>
</td>
</tr>
<% end %>
<% end %>
The controller:
def savedata
#match = Match.new(match_params)
end
private
def match_params
params.require(:match).permit(:player_id, :tournament_id, :wins, :no_of_matches)
end
The migration:
class CreateMatches < ActiveRecord::Migration[6.0]
def change
create_table :matches do |t|
t.references :player
t.references :tournament
t.string :wins
t.integer :no_of_matches
t.timestamps
end
end
end
Welcome to SO!
The form submit button submits the whole form, not just a single row. Having multiple submit buttons won't make a difference, each button will do the same thing.
To save just one row, you'd need to make each row its own unique form, or use AJAX, TurboFrames, or JS to send just that row data to the controller for saving.
Check out a few of these AJAX tutorials for tips and ideas:
https://medium.com/#jelaniwoods/how-to-actually-use-ajax-in-rails-83e667ea7953
https://www.rubyguides.com/2019/03/rails-ajax/
Related
I have been trying to add project_id and user_id to the following
create_table "project_users", force: :cascade do |t|
t.integer "project_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Model project_user.rb
class ProjectUser < ApplicationRecord
has_many :users
has_many :projects
end
This is my controller that is supposed to add data in the table:
def addtoproject
#emp_id = params[:eid]
#project_id = params[:pid]
#add_user = ProjectUser.new(params.require(:projectuser).permit(:project_id, :user_id))
#add_user.project_id = #project_id
#add_user.user_id = #emp_id
#add_user.save
end
#emp_id and #project_id both contain valid values.
Rails is throwing the error on the line
#add_user = ProjectUser.new(params.require(:projectuser).permit(:project_id, :user_id))
Error screenshot:
Any clues how this error can be fixed? As the table name is "project_users", I reckon I maybe missing an "_" somewhere?
Update 1
Clicking Add to Project invokes the method addtoproject inside controller.
addusers.html.erb:
<table id='employee-table' class="table table-hover">
<thead class="thead-light">
<tr>
<th>Employee ID</th>
<th>Name</th>
<th>Role</th>
<th colspan="2"></th>
</tr>
</thead>
<tbody>
<% #employees.each_with_index do |employee, index| %>
<tr>
<td><%= employee.id %></td>
<td><%= employee.firstname + ' ' + employee.lastname %></td>
<td><%= employee.user_type %></td>
<td><%= link_to "Add to Project", add_to_project_path(eid: employee, pid: #project_id), :class => "btn btn-success", :style => "float:right"%></td>
</tr>
<% end %>
</tbody>
</table>
It's complaining because you're doing
params.require(:projectuser)
This means that :projectuser doesn't exist in params. You can see the parameters that are being sent to your controller further down in that screenshot. Only a pid and eid are present.
If you're using a form, you may need to change it to use something like form_for so your parameters are namespaced to the model.
<%= form_for #project_user do |f| %>
<%= f.number_field :pid %>
<%= f.number_field :eid %>
You'll also face errors with the `permit section since you specify
permit(:project_id, :user_id)
But the params passed are pid and uid. Use the name of your model fields in your inputs:
<%= form_for #project_user do |f| %>
<%= f.number_field :project_id %>
<%= f.number_field :user_id %>
It turns out that if the parameters are not passed using form_with as in my case, there is no need for params.require or params.permit (as quoted by #Cjmarkham these attributes are not present).
Just initialising an empty instance of the model and saving it worked for me.
#add_user = ProjectUser.new
#add_user.project_id = #project_id
#add_user.user_id = #emp_id
#add_user.save
I have manager remark model that takes input as a remark and decision value and saves it with the project site ID. I have a project site model that takes input as name, date, and file and stores it. Many remarks have a many to one relation with project site ID, and the project site belongs to the manager remark. I want to access the decision attribute boolean value in project site index form, but I am unable to access that boolean value in the index page of the project site. Here is my code of project site and manager remarks model, view and controller-
project site index.html.erb
<table>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Attendance</th>
<th>Status</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #project_sites.each do |project_site| %>
<tr>
<td><%= project_site.name.titleize %></td>
<td><%= project_site.date %></td>
<td><%= link_to ' View attendance', project_site.file, :class => "fi-page-export-csv" %></td>
<td><%= "here i want to access manager remark decision value" %></td>
<td><%= link_to 'Remark ', project_site %><span>(<%= project_site.manager_remarks.size %>)</span></td>
<td><%= link_to 'Edit', edit_project_site_path(project_site) %></td>
<td><%= link_to 'Destroy', project_site, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
project site controller
def index
#project_sites = ProjectSite.all.order("created_at DESC")
#manager_remark = ManagerRemark.joins(:project_site).where(:project_sites => { :user_id => #user.id })
end
# GET /project_sites/1
# GET /project_sites/1.json
def show
#manager_remark = ManagerRemark.new
#manager_remark.project_site_id = #project_site.id
end
# GET /project_sites/new
def new
#project_site = ProjectSite.new
end
def project_site_params
params.require(:project_site).permit(:name, :date, :file)
end
manager_remark controller
class ManagerRemarksController < ApplicationController
def create
#manager_remark = ManagerRemark.new(remark_params)
#manager_remark.project_site_id = params[:project_site_id]
#manager_remark.save
redirect_to project_site_path(#manager_remark.project_site)
end
def remark_params
params.require(:manager_remark).permit(:remark, :decision)
end
end
manager_remark view form
<%= form_for [ #project_site, #manager_remark ] do |f| %>
<div class="row">
<div class="medium-6 columns">
<%= f.radio_button :decision, true %>
<%= f.label :approve %>
<%= f.radio_button :decision, false %>
<%= f.label :reject %>
</div>
<br>
<br>
<div class="medium-6 cloumns">
<%= f.label :remark %><br/>
<%= f.text_area :remark %>
</div>
</div>
<div>
<%= f.submit 'Submit', :class => 'button primary' %>
</div>
<% end %>
routes.rb
Rails.application.routes.draw do
root to: 'home#index'
devise_for :users
resources :project_sites do
resources :manager_remarks
end
get '/project_manager_level_two' => 'project_manager_level_two#index'
get '/project_managers' => 'project_managers#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
If I understand correctly, you have a ProjectSite that contains a ManagerRemark with a decision, right? If that's the case, the simple answer is:
<%= project_site.ManagerRemark.decision %>
If you are saying that each ProjectSite has many ManagerRemarks, you'll want to place the above inside a loop, like so:
<% project_site.manager_remarks.each do |manager_remark| %>
<%= manager_remark.decision %><br/>
<% end %>
This assumes that your models are correctly configured to recognize these relationships. The above may also be optimized by adding an include clause to your fetch inside the controller and there's no need to fetch the ManagerRemark objects separately. Therefore, you'd probably want something like:
def index
#project_sites = ProjectSite.all.includes( :manager_remark ).order("created_at DESC")
end
I have a rails application which is not routing as I expected. The search method in the controller is rending show. I've cut down the code to the minimal components and I am posting them here as suggested.
Rails.application.routes.draw do
resources :backups
get 'backups/search' => 'backups#search'
resources :components
resources :backup_media
end
class Component < ActiveRecord::Base
has_many :backups
has_many :backup_media, :through => :backups
end
class BackupMedium < ActiveRecord::Base
has_many :backups
has_many :components, :through => :backups
end
class Backup < ActiveRecord::Base
belongs_to :component
belongs_to :backup_medium
# value to match either the name of the component or backup_medium
def self.search(value)
tables = "backups, components, backup_media"
joins = "backups.backup_medium_id = backup_media.id and components.id = backups.component_id"
c = find_by_sql "select * from #{tables} where components.name like '%#{value}%' and #{joins}"
b = find_by_sql "select * from #{tables} where backup_media.name like '%#{value}%' and #{joins}"
c.count > 0 ? c : b
end
end
class BackupsController < ApplicationController
def search
#backups = Backup.search(params[:search])
render 'index'
end
def index
#backups = Backup.all
end
def show
# this would normally be the code to show an individual backup
# but I'm re-using the code from index because the routing is broken
#backups = Backup.all
end
end
views/backups/_search.html.erb
<%= form_tag backups_search_path, :method => 'get' do %>
<%= label_tag(:search, "Search for:") %>
<%= text_field_tag :search, params[:search], {:placeholder => 'Component or Media' }%>
<%= submit_tag "Search", :name => nil %>
<% end %>
views/backups/index.html.erb
<h1>Listing Backups</h1>
<p id="notice"><%= notice %></p>
<%= render :partial => 'search' %>
<table>
<tr>
<th>id</th>
<th>component_id</th>
<th>backup_medium_id</th>
</tr>
<% #backups.each do |backup| %>
<tr>
<td><%= backup.id %></td>
<td><%= backup.component.name %></td>
<td><%= backup.backup_medium.name %></td>
</tr>
<% end %>
</table>
views/backups/show.html.erb is copied from index.html.erb since it is incorrectly receiving the search results
<h1>Show Backup</h1>
<p id="notice"><%= notice %></p>
<%= render :partial => 'search' %>
<table>
<tr>
<th>id</th>
<th>component_id</th>
<th>backup_medium_id</th>
</tr>
<% #backups.each do |backup| %>
<tr>
<td><%= backup.id %></td>
<td><%= backup.component.name %></td>
<td><%= backup.backup_medium.name %></td>
</tr>
<% end %>
</table>
Suggestions on improving the search method will be welcomed.
As mentioned above, after the search is executed, the show.html.erb is rendered instead of search.html.erb
For a working demo (with better code thanks to suggestions here) see
https://github.com/pamh09/rails-search-demo
You do not have a backups_search_path in your routes, therefore it is treating search in the query string as an id and thus rendering show.html.erb, so try
get 'backups/search' => 'backups#search', as: :backups_search
In debugging I found that rails consistently routed to the wrong view when it was unhappy with the return object coming from the model.
For example, I have two models:
class Task < ApplicationRecord
has_many :task_details
end
class TaskDetail < ApplicationRecord
belong_to :task
end
I want to display a table, each row in table is one TaskDetail and allow user input. After that user submits, all data will put to server. Here is my code:
(Note that: I #data[:task] is a task object because I want to return a hash with some information for view)
<%= form_for #data[:task], :url => tasks_path do |f| %>
<table> ... </table>
<% end %>
My question is: How can I do as my requirement.
thanks
Ensure that your Task model has accepts_nested_attributes_for :task_details and then you can do something like...
<%= form_for #data[:task], :url => tasks_path do |f| %>
<table>
<tr>
<th>Task Name</th>
<th>Task Description</th>
<tr>
<%= f.fields_for :task_details do |task_detail| %>
<tr>
<%= task_detail.hidden_field :id %>
<td><%= task_detail.text_field :name %></td>
<td><%= task_detail.text_field :description %> </td>
<tr>
<% end %>
</table>
<% end %>
Note the use of the hidden field for :id ... you need that so that rails can distinguish data from existing tasks versus a new task you're entering.
In your new method you should ensure there's at least one new task detail to provide an empty line on the form to input the detail
def new
...
#data[:task].task_details.build
...
end
I'm using the field_for form helper with a loop:
<% f.fields_for :permissions do |permission_form| %>
<tr>
<td><%= permission_form.object.security_module.name %><%= permission_form.hidden_field(:security_module_id) %></td>
<td><%= permission_form.object.security_module.description %></td>
<tr>
<% end %>
The resulting output of the above code is this:
<input id="role_permissions_attributes_0_id" name="role[permissions_attributes][0][id]" type="hidden" value="76" />
<tr>
<td>Diary<input id="role_permissions_attributes_0_security_module_id" name="role[permissions_attributes][0][security_module_id]" type="hidden" value="13" /></td>
<td>Access to the Diary Module</td>
</tr>
<!-- next input field then <tr> tag -->
The problem with this markup is that the input tag falls outside of the tr tag which there for causes validation issues with XHTML.
Does anyone know how I can have the input tag fall inside the tr tag therefore giving me valid XHTML 1.0 STRICT markup?
Thanks
If you take a look at the Rails source code you'll find this.
# in actionpack/lib/action_view/helpers/form_helper.rb
def fields_for_nested_model(name, object, args, block)
if object.new_record?
#template.fields_for(name, object, *args, &block)
else
#template.fields_for(name, object, *args) do |builder|
#template.concat builder.hidden_field(:id)
block.call(builder)
end
end
end
Notice it is adding the hidden field directly here, and it doesn't look like there is any option to change this behavior. The easiest thing is probably to create your own custom form builder.
# in lib/no_id_form_builder.rb
class NoIdFormBuilder < ActionView::Helpers::FormBuilder
private
def fields_for_nested_model(name, object, args, block)
#template.fields_for(name, object, *args, &block)
end
end
And then use this in your form. You'll need to add the id field manually.
<% f.fields_for :permissions, :builder => NoIdFormBuilder do |permission_form| %>
<tr>
<td>
<%= permission_form.object.security_module.name %>
<%= permission_form.hidden_field(:security_module_id) %>
<%= permission_form.hidden_field(:id) unless permission_form.object.new_record? %>
</td>
<td><%= permission_form.object.security_module.description %></td>
<tr>
<% end %>
You may want to submit a lighthouse ticket on this. perhaps there could be a :skip_id_field option to fields_for which does this.
There is a workaround available as of 2.3.5. If you explicitly place the :id field, it won't implicitly add it for you:
<% form_for #foo do |f| %>
<table>
<tbody>
<% f.fields_for :bars do |bf| %>
<tr>
<td>
<%= bf.hidden_field :id %>
<%= bf.text_field :name %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
See
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/3259
Slight correction:
The :builder option needs to go on the form_for containing the fields_for, not the fields_for.