How can I get post parameter in controller - ruby-on-rails

I need to get post parameter deal_id in controller.
My view -> show.html.erb
<%= form_for([#deal, #contract], :url => pay_payments_path, html: { method: :post }) do |f| %>
<%= f.hidden_field :deal_id, value: #deal.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Send", :class => "btn btn-danger" %>
<% end %>
My controller -> payments_controller
def pay
#deal = Deal.find(params[:deal_id])
end
My code in controller dont works, I got this:
Couldn't find Acordo with 'id'=
I want to get the #deal according deal_id parameter sent via post.
Routes
resources :payments, only: [:index, :new, :create] do
collection do
post :pay
end
end

If you are getting an empty value for params[:deal_id] you are probably missing adding it to the allowed parameters which may not be part of the default fields for a Payment resource:
def params
params.require(:payment).permit(:deal_id)
end
Make sure you have the field in the params method on your payments controller.

I am not a Ruby expert but maybe this can help you.
Accessing POST parameters

Related

Want to edit/PUT/PATCH but error says "No route matches [POST]"

I'm building my second-ever basic Ruby on Rails application and having fun doing it, but have gotten stuck at precisely the same place that gave me trouble (and was never solved) on my last effort: the PUT or PATCH request.
My application has two models: entries and users. A logged-in user should be able to edit only those entries that were originally created by that user.
CONTROLLER
class EntriesController < ApplicationController
# authenticate user (Devise)
before_action :authenticate_user!, :except => [:index, :show]
# set entry upon page load
before_action :set_entry, :only => [:show, :edit, :update, :destroy]
# GET request - display all entries
def index
#all_entries = Entry.all
end
# GET request - display an individual entry
def show
# nothing required here because entry identified with before_action :set_entry on line 2 above
end
# GET request - access form to create a new entry
def new
#entry = Entry.new
#user = User.find(current_user[:id])
end
# GET request - access form to update an existing entry
def edit
if #entry[:user_id] != current_user[:id]
redirect_to root_path
else
redirect_to edit_entry_path
end
end
# POST request - make a new entry/save new data into db
def create
user = current_user[:id]
Entry.create({
entry_title: params[:entry][:entry_title],
book_title: params[:entry][:book_title],
text: params[:entry][:text],
img_url: params[:entry][:img_url],
tag: params[:entry][:tag],
created_at: params[:entry][:created_at],
user_id: user
})
redirect_to entries_path
end
# PUT request - save changes to an existing entry
def update
if #entry.update(entry_params)
redirect_to entry_path
else
render :new
end
end
# DELETE request - delete an existing entry from db
def destroy
#entry.destroy
redirect_to entries_path
end
private
def set_entry
#entry = Entry.find(params[:id])
end
def entry_params
params.require(:entry).permit(:email, :text, :tag)
end
end
VIEW (show.html.erb - shows a single entry and includes links allowing the logged-in user who originally authored the entry to edit or delete it)
<h3>Selected Entry</h3>
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6">
<div>Entry title: <%= #entry.entry_title %></div>
<div>Book title: <%= #entry.book_title %></div>
<div>Text: <%= #entry.text %></div>
</div>
<div class="col-md-4">
<div><%= #entry.created_at.strftime("%b %d, %Y") %></div>
<div>Submitted by: <i><%= #entry.user.email %></i></div>
<div>File under: <i><%= #entry.tag %></i></div>
<% if current_user %>
<%= link_to 'Edit', #entry, :method => 'update' %>
<%= link_to 'Delete', #entry, :method => 'delete' %>
<% end %>
</div>
</div>
ROUTES.RB - At first my routes were the commented-out lines, but then I had a thought that was either madness or sudden realization - should only the GET routes lead with "get"? So that's the non-commented-out attempt you see. Somehow the app works (except for the issue at hand) both ways.
In researching I've come across routes defined using a much more elaborate syntax than that I'm using here. I've been unable to figure out whether a given way of doing things is different convention, outdated, or just inadequate to the task.
Rails.application.routes.draw do
devise_for :users
resources :entries
# root 'entries#index'
# get '/entries' => 'entries#index'
# get '/users' => 'users#index'
# get '/entries/:id' => 'entries#show'
# get '/entries/:id' => 'entries#update'
# get '/entries/new' => 'entries#new'
# get '/entries/:id/edit' => 'entries#edit'
# get '/users/:id' => 'users#show'
# get '/about' => 'pages#index'
root 'entries#index'
get '/entries' => 'entries#index'
get '/entries/new' => 'entries#new'
post '/entries' => 'entries#create'
get '/entries/:id' => 'entries#show'
get '/entries/:id/edit' => 'entries#edit'
put '/entries/:id' => 'entries#update'
delete '/entries/:id' => 'entries#destroy'
get '/users' => 'users#index'
get '/users/:id' => 'users#show'
get '/about' => 'pages#index'
end
Thanks in advance for any insight. If additional context is needed I'm happy to provide.
Edited to add:
PARTIAL (_form.html.erb)
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6" id="form-container">
<%= form_for #entry do |form| %>
<br>
<%= form.text_field :entry_title, :size => 59, :placeholder => "Entry Title"%>
<br><br>
<%= form.text_field :book_title, :size => 59, :placeholder => "Book Title"%>
<br><br>
<%= form.text_field :img_url, :size => 59, :placeholder => "Image URL"%>
<br><br>
<%= form.text_area :text, :placeholder => "Text" %>
<br><br>
<%= form.text_field :tag, :placeholder => "Tag" %>
<br><br>
<%= form.submit %>
<% end %>
</div>
<div class="col-md-4"></div>
</div>
To edit a record you
first, should use a GET request to get the edit form
second, should submit that form using a PUT/PATCH request
To get to the edit form you should link to the edit path for your entry
<%= link_to 'Edit', edit_entry_path(#entry) %>
The Rails form helpers will automatically set the form to submit with the proper method, PUT OR PATCH.
:method in link_to helpers refers to HTML verb (get, post, etc), while controllers methods naming convention is action.
link_to
You need something as
<%= link_to 'Edit', #entry, :method => 'put' %>
or
<%= link_to 'Edit', #entry, :action => 'update' %>
At a glance you are trying to post with the edit link. Remember new/edit are get methods to render form, so just just delete method part in your links. Like from
<%= link_to 'Edit', #entry, :method => 'update' %>
to
<%= link_to 'Edit', edit_entry_path(#entry) %>
I'm building my second-ever basic Ruby on Rails application
Congrats! You need at least 3 more before it all starts to make sense
To add to the existing answers, you'll be best looking at the resources directive to clean the routes up:
#config/routes.rb
root 'entries#index'
devise_for :users
resources :entries
resources :pages, only: [:index], path_names: { index: "about" }
resources :users, only: [:index,:show]
--
A logged-in user should be able to edit only those entries that were originally created by that user.
This is known as authorization.
Authentication = is user logged in?
Authorization = can user do this?
Although people confuse Devise with being able to handle authorization, it only handles authentication. Whilst you have a simple implementation of this in your controller, you should check out either the CanCanCan or Pundit gems:
#Gemfile
gem "cancancan"
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :manage, Post, user_id: user.id
end
end
#app/controllers/entries_controller.rb
class EntriesController < ApplicationController
def edit
authorize! :edit, #entry
end
end
--
Finally, to answer your question directly, you're calling the update method (which doesn't exist) to access the edit view:
<% if current_user %>
<%= link_to 'Edit', #entry, :method => 'update' %>
<%= link_to 'Delete', #entry, :method => 'delete' %>
<% end %>
You should read up about http verbs - this is what the "method" option invokes with the link. As mentioned above, you don't need to set the method for edit as it uses GET. Update uses put/patch, which I can explain later.
A much better way to achieve what you want would be the following:
<%= link_to "Edit", edit_entry_path(#entry) if can? :edit, #entry %>
<%= link_to "Delete", #entry, method: :delete, if can? :destroy, #entry %>
The above uses the CanCanCan authorization method can?

Connecting routes, views and controllers (Rails)

I'm learning Rails so please pardon my amateur mistakes, but I've been stuck for about an hour or two and have made negative progress.
Goal:
From the user profile view, link to a form that allows this user
to change their email. Once the form is submitted, it should trigger
an appropriate method within the user controller.
I can handle the rest, I just haven't managed to connect the parts mentioned above. I have been reading railsTutorial.org and guides.rubyonrails.org but haven't been able to understand routing form_for() sufficiently.
User Profile Link:
<%= #user.email %> <%= link_to "(change)", email_path(#user) %>
Routes
Rails.application.routes.draw do
get 'email' => 'users#email_form'
post 'email' => 'users#changeemail'
end
User Controller
class UsersController < ApplicationController
def email_form
#user = User.find(params[:id])
end
def changeemail
#user = User.find(params[:id])
redirect_to #user
end
end
Currently the error I get once I click the link is Couldn't find User with 'id'= which I assume means user ID is nil because I fail at passing it.
I would greatly appreciate an explanation of what data is being passed through this workflow so I can be a better developer, thank you very much!
EDIT:
The form itself:
<%= form_for(#user, url: user_path(#user)) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :new_email %>
<%= f.text_field :new_email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.submit "Submit New Email", class: "btn btn-primary" %>
<% end %>
You could do this (note :id and "as"):
Rails.application.routes.draw do
get 'email/:id' => 'users#email_form', as :email
post 'email/:id' => 'users#changeemail', as :change_email
end
The :id is then expected to be part of the route.
Alternatively, pass the id directly when generating the url:
<%= #user.email %> <%= link_to "(change)", email_path(id: #user) %>
This will make a call to "UsersController#update"
<%= form_for(#user, url: user_path(#user)) do |f| %>
...instead you would use something like::
<%= form_for(#user, url: change_email_path(#user), method: :put) do |f| %>
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for
...but in terms of best practices, if you want to do separate flow for email updating, you could be more explicit in treating it as a different resource (even though it's still the user record).
For example, you could map these to an explicit 'resource' with a #show and #update action...
Routes:
resources :user_emails, only: [:show, :update]
Controller:
class UserEmailsController < ApplicationController
def show
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
redirect_to #user # goes back to UsersController#show
end
end
Then the route would be:
<%= #user.email %> <%= link_to "(change)", user_email_path(#user) %>
In this case we don't have to say (id: #user) since the 'resource' generates the right urls for you.
...and this would be
<%= form_for(#user, url: user_email_path(#user), method: :post) do |f| %>

Couldn't find Timetable with 'id'=clas

Generated a timetable scaffold. Then later created a new action "clas" in my timetables_controller.rb.
timetables_controller.rb
class TimetablesController < ApplicationController
before_action :set_timetable, only: [:show, :edit, :update, :destroy, :clas]
def clas
#classtimetable = Timetable.where(params[:clas])
end
//other actions
def set_timetable
#timetable = Timetable.find(params[:id])
end
def timetable_params
params.require(:timetable).permit(:day, :clas, :one_teacher, :one_sub, :two_teacher, :two_sub, :three_teacher, :three_sub, :four_teacher, :four_sub, :five_teacher, :five_sub, :six_teacher, :six_sub, :seven_teacher, :seven_sub, :eight_teacher, :eight_sub)
end
end
Created form_for to the action "clas". Need to pass the select option values as params to the controller
clas.html.erb
<% form_for #classtimetable, :url => clas_timetables_path, :html => { :method => :post} do |f| %>
<div class="field">
<%= f.label "Select class" %>
<% values = ["1c", "2c", "3c", "4d", "5i"] %>
<%= f.select :clas, options_for_select(values) %>
</div>
<div class="actions">
<%= f.submit "Submit"%>
</div>
<% end %>
<% #classtimetable.each do |class_timetable| %>
<%= class_timetable.day %>
<% end %>
routes.rb
resources :timetables do
member do
get 'clas'
end
end
I need to get all the day for the class in my clas.html.erb page. Which is done by selecting the class from dropdown and submit. The value should be passed in params when i click submit.
don't know how to fix it. Any ideas?
Add controller in form_for tag,
<% form_for #classtimetable, :url => { :controller => "your-controller-name", :action => :clas } do |f| %>
Or you can directly write like this,
<% form_for #classtimetable, :url => clas_timetables_path, :html => { :method => :post} do |f| %>
Change before_action like,
before_action :set_timetable, only: [:show, :edit, :update, :destroy, :clas]
You would change collection to member and pass id
resources :timetables do
member do
get 'clas'
end
end
Try to change your form_tag like this,
<% form_for #classtimetable, :url => clas_timetables_path(#classtimetable), :method => :post do |f| %>
Can you try this
def clas
#classtimetable = Timetable.where(timetable_params[:clas]).first
end
First I recognized: You used get in routes, and post in form. So the actions won't match.
Second: You've written:
still getting the same error undefined method `model_name' for Timetable::ActiveRecord_Relation:Class
So look, how you defined #classtimetable
#classtimetable = Timetable.where(params[:clas])
This is a relation, not the ActiveRecord, you want to find by clas. The error will be thrown on this line:
<% form_for #classtimetable, :url => clas_timetables_path, :html => { :method => :post} do |f| %>
form_for expects as first argument an ActiveRecord or something similar (at least something extending ActiveModel::Naming). Since I do not see your definition of Timetable, I just guess, it has an attribute called clas and you want to find the Timetable with clas equals the selection in that form. So it would be:
#classtimetable = Timetable.find_by(clas: params[:clas])
what is the same as:
#classtimetable = Timetable.where(clas: params[:clas]).first
To get the first element from that Relation, you tried to use.
Third: Since you did define your route to be a member route, it should expect an id in clas_timetable_path. Since you load #timetable in clas action in before_filter, I think you want to have there:
<% form_for #classtimetable, :url => clas_timetables_path(#timetable), :html => { :method => :get} do |f| %>

Ruby on Rails custom update action

I want to update member_id, which triggers update of :location_stock.
I have got these custom actions in
costumes_and_cost_records_controller.rb:
def rent
#costumes_and_cost_record = CostumesAndCostRecord.find(params[:costumes_and_cost_record_id])
end
def rent_method
#costumes_and_cost_record = CostumesAndCostRecord.find(params[:costumes_and_cost_record_id])
#costumes_and_cost_record.update_attributes(:member_id => params.permit(:member_id), :location_stock => true)
redirect_to #costumes_and_cost_record
end
I used simple form in view rent.html.erb:
<%= simple_form_for #costumes_and_cost_record do |f| %>
<%= f.association :member, :label => "Member", label_method: :to_s, value_method: :member_id, include_blank: true %>
<%= f.submit "Rent", :controller => :costumes_and_cost_records, :action => :rent_method, :method => :put, :member_id => :member %> <%# updates only member_id, doesnt update :location_stock %>
<%# link_to "Rent", :controller => :costumes_and_cost_records, :action => :rent_method, :method => :put, :member_id => :member_id %> <%# updates :location_stock, sets member_id = NULL %>
<% end %>
Now if I use submit button :member_id updates but controller doesn't update :location_stock.
If I use link_to :location_stock updates, but :member_id is set to NULL.
I want to update both attributes. Should I use submit button or link_to and how to fix this issue?
I set routes.rb to make both link_to and submit methods in view work:
resources :costumes_and_cost_records do
post 'show'
get 'rent'
get 'rent_method'
end
Any help is greatly appreciated.
If I understand you correctly you need to call rent_method to update member_id
Routes changes
put 'rent_method'
View changes
<%= simple_form_for #costumes_and_cost_record, method: :put, url: [#costumes_and_cost_record, :rent_method] do |f| %>
<%= f.association :member, :label => "Member", label_method: :to_s, value_method: :member_id, include_blank: true %>
<%= f.submit "Rent" %>
<% end %>
Controller
def rent_method
#costumes_and_cost_record = CostumesAndCostRecord.find(params[:costumes_and_cost_record_id])
#costumes_and_cost_record.update_attributes(:member_id => member_params[:member_id], :location_stock => true)
redirect_to #costumes_and_cost_record
end
def member_params
params.require(:costumes_and_cost_record).permit(:member_id)
end
Routes
Firstly, if you want to have member_id present, you'll probably be best using nested routes as follows:
#config/routes.rb
resources :member do.
resources :costumes_and_cost_records do
... #-> domain.com/members/2/costumes_and_cost_records/
end
end
This will give you the value required for params[:member_id] from your link:
<%= link_to "Member", member_costumes_and_cost_records_path(member_id) %>
Form
In your form, you then need to be able to define the url correctly (Rails naturally submits to the CRUD based actions, not custom ones):
<%= simple_form_for #costumes_and_cost_record, url: your_rent_method_path, method: :patch do |f| %>
This will submit to the rend_method path, or whichever custom action you wish to request.
--
I would personally keep any activity you have in the controller to a single action - this will allow you to keep all the business logic in one action, which is preferred for the MVC programming pattern
It took me 3 days but I finally fixed the problem. Ruslan Kyrychuk's answer was good, but the problem was that I didn't follow Rails naming conventions. I had foreign key: costumes_and_cost_records.member_id and primary key:member.member_id so I renamed member.member_id to member.id and that fixed the problem.

Undefined method which is not used

Hello i'm new one in ruby on rails. I have strange problem. I use some tutorial and get error which i shouldn't get.
I have controller
class DiaryController < ApplicationController
before_action :authenticate_user!
respond_to :html, :xml, :json
respond_to :js, :only => [:create, :update, :destroy]
def create
#record = Record.create(record_params)
#record.userId=current_user.id
if request.xhr? || remotipart_submitted?
sleep 1 if params[:pause]
render :layout => false, :template => (params[:template] == 'escape' ? 'comments/escape_test' : 'diary/create'), :status => (#record.errors.any? ? :unprocessable_entity : :ok)
else
redirect_to diary_path
end
end
def add
#record = Record.new
#respond_with(#record, :layout => false)
respond_with do |format|
format.html { render :layout => ! request.xhr? }
end
end
# PUT /comments/1
# PUT /comments/1.xml
def update
#record = Record.find(params[:id])
respond_with do |format|
format.html{ redirect_to #record }
end
end
def delete
#comment = Comment.destroy(params[:id])
end
def edit
#record = Record.find(params[:id])
end
def index
#records = Record.where(userId: current_user.id)
end
private
def record_params
params.require(:record).permit(:photo, :comment, :date, :photo_cache)
end
end
Have view
<h1 align="centre">
Добавить запись
</h1>
<%= render 'form' %>
<%= link_to 'Отмена', diary_path, :id => 'cancel-button' %>
and
<%= form_for(#record, :remote => (params[:action] == 'add' ? true : false)) do |f| %>
<fieldset>
<div class="field">
<%= f.label :date, :class => 'required' %><br />
<%= f.date_select :date %>
</div>
<div class="field">
<%= f.label :comment %><br />
<%= f.text_area :comment %>
</div>
<div class="field">
<%= image_tag(#record.photo_url(:thumb)) if #record.photo? %><br />
<%= f.label :photo %><br/>
<%= f.file_field :photo %><br/>
<%= f.hidden_field :photo_cache %>
</div>
</fieldset>
<table>
<tr>
<td>
<div class="actions">
<%= f.submit "Добавить", :data => {:'disable-with' => "Submitting..."} %>
</div>
</td>
<td>
<%= link_to 'Отмена', diary_path, :id => 'cancel-button' %>
</td>
</tr>
</table>
And get ActionView::Template::Error (undefined method `records_path' for #<#:0x000000054461c8>): error on "<%= form_for(#record, :remote => (params[:action] == 'add' ? true : false)) do |f| %>" line. Even records_path i did'n use.
I Have routes
devise_for :users
get 'welcome/index'
root 'welcome#index'
get 'diary' => 'diary#index'
get 'diary/add_record', to: 'diary#add', as: 'add_record'
post 'diary/add_record', to: 'diary#create'
get 'diary/edit_record/:id', to: 'diary#edit'
delete 'diary/edit_record/:id' => 'diary#delete
And and try to use add_record route. Maybe it would be better to use resources :records.But i want to figure out why my routes doesn't work.
view name "diary".
Because you're new to RoR, let me explain why you're receiving the error
form_for
form_for is the likely reason why you're receiving this error (oh, I just saw it actually states this is where the error occurs - sweet)
The problem you have is that form_for is meant as a way to render a form around an ActiveRecord object. It's mean to give some semi-persistence to the data, by using AR in both the new and create actions (allowing you to show the in-putted data on the form after submission)
When you pass an object to form_for, Rails automatically "builds" the form from the ActiveRecord object, one of the options it uses being the url
--
Routes
The problem you have is the object you pass to the form_for takes the model_name attribute to build the route. This means if you want to use the form_for method by just passing an object, it's going to look for routes pertaining directly to that object
If you don't have any [model]_path route set up, you'll likely receive the error you're getting. The fix firstly involves the routes, and secondly involves the controller:
#config/routes.rb
root 'welcome#index'
devise_for :users
resources :diary, path_names: { new: "add_record", create: "add_record", edit: "edit_record", destroy: "edit_record" }
resources :welcome, only: :index
This is down to the idea that Rails' routing structure is built around resources - every route you have should lead to a specific controller action. Whilst including custom actions is completely fine, you have to appreciate that the basis of the routing structure is to construct resourceful routing, which essentially means that Rails perceives every controller / model to have corresponding routes:
--
URL
The second thing to observe is the url of the form
If you have your routes set up as above, and if your routing structure differs from your model structure (different names), you'll want to use the following setup to define the url explicitly:
<%= form_for #record, url: your_custom_path do |f| %>

Resources