Resource Route linked to incorrect action - ruby-on-rails

I am just starting out with rails and have run into some difficulties. I am trying to create a basic form that will add a new entry to the "Main" database. When I submit the form, instead of running "new" it appears to be trying to run "update", which according to the documentation should be called by /photos/:id
There error I am getting on the browser is
Unknown action
The action 'update' could not be found for AdminController
Controller:
class AdminController < ApplicationController
def index
#post = Main.create
end
def new
end
end
index.erb.html:
<%= form_for #post, :url => { :action => "new" }, :html => {:class => "nifty_form"} do |f| %>
<%= f.text_field :title %>
<%= f.text_area :entry, :size => "60x12" %>
<%= f.submit "New" %>
<% end %>
Routes:
Tasks::Application.routes.draw do
root :to => "Main#index"
resources :main
resources :admin

In Rails new is supposed to show the form to enter a new item.
edit is for showing the form to edit an existing item.
The form data is then POSTed to mains_url if it's a new item or PUT if its an exitsing item.
POST is routed to the create action.
PUT is routed to the update action.
So, to create an item, you have to implement create, to update it, you have to implement update
see
rake routes

Related

Rails form rendering wrong URL after validation errors (not keeping passed parameter)

On a project show page, I pass a very simple parameter on my 'create new task' that stores which project its from:
#project.id), :class => "btn btn-info col-md-12" %>
so that when i create a new task for it, it stores it in the URL on my new task form like this:
http://localhost:3000/task/new?project_id=5
My New form is as follows:
<div class="container sign-in-register">
<div class="authform">
<%= form_for #task, :html => {:multipart => true} do |f| %>
<h3>Add a task for this project...</h3><br/>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= hidden_field_tag 'project_id', #project_id %>
<%= f.fields_for :taskrelationships do |ff| %>
<%= ff.hidden_field :taskproject_id, value: #project_id %>
<%= ff.label :task_url %>
<%= ff.text_field :task_url, class: 'form-control' %>
<% end %>
<br clear="all">
<%= f.submit "Save Task", class: "btn btn btn-info" %>
<% end %>
</div>
</div>
As you can see i'm using nested attributes in the form (I am creating both a task and a TaskRelationship. Now, when I try to save without filling out all the required fields a validation is thrown but for some reason it redirects me to:
http://localhost:3000/tasks
instead of the original:
http://localhost:3000/tasks/new?project_id=5
I have read many posts and none seem to answer this particular case. The stackO post below was close but when I try it with task instead of user it still cannot find the task_ID
Render error on new page with customize url like http://localhost:3000/education_informations/new?user_id=10
How can I have rails simply render the same exact url as I started with - it seems like this should be easy so must be missing something small.
My controller actions:
def new
#project_id = params[:project_id]
#task = Task.new
#task.taskrelationships.build
end
def create
#project = Project.find(params[:project_id])
#task = Task.new(task_params)
if #task.save
flash[:success] = "This task has been added."
#task.taskrelationships.create!(#taskrelationships_params)
redirect_to tasks_project_path(#project)
else
#task.taskrelationships.build(#taskrelationships_params)
flash[:alert] = #task.errors.full_messages.to_sentence
render :new
end
end
private
def task_params
#taskrelationships_params = params.require(:task).permit(taskrelationships_attributes: [
:task_url,
:taskproject_id
])[:taskrelationships_attributes]["0"]
params[:task].delete(:taskrelationships_attributes)
params.require(:task).permit(
:name,
:user_id,
taskrelationships_attributes: [
:task_url,
:taskproject_id
]
).merge(owner: current_user)
end
UPDATE W / ROUTES
resources :projects do
resources :reviews, except: [:destroy]
member do
get :tasks
end
end
resources :tasks
resources :taskrelationships, only: [:create, :destroy] do
post :vote, on: :member, controller: "task_relationships"
end
thanks for any assistance...
Ok firstly an explanation as to what is going on here:
When you invoke http://localhost:3000/task/new?project_id=5 you are actually being routed to the new action on the task controller (with a project_id param).
Your new action then sets the variables and rails will render the new.html.erb which contains your new task form.
When you submit the form it actually is doing a http POST to /tasks, which routes to the create action of your tasks controller. That url and http method is a result of what is generated from the form_for helper:
<%= form_for #task, :html => {:multipart => true} do |f| %>
This is why the url changes from /tasks/new?project_id=5 to /tasks
Now the create action if it fails the validation simply renders the new form - it is not redirecting anywhere - the url remains unchanged from what it was when it entered this action - meaning, it remains as /tasks.
You do not actually need to navigate to /tasks/new?project_id=5 to render the new form but what you do need to do is set #project_id in the controller so the view has access to that variable (just as it does in the new action):
def create
#project = Project.find(params[:project_id])
#task = Task.new(task_params)
if #task.save
flash[:success] = "This task has been added."
#task.taskrelationships.create!(#taskrelationships_params)
redirect_to tasks_project_path(#project)
else
#task.taskrelationships.build(#taskrelationships_params)
#project_id = #project.id
flash[:alert] = #task.errors.full_messages.to_sentence
render :new
end
end
So, to clarify the change in url is not a redirection it's just that the form is posting to a different url than /tasks/new, and this is actually just a cosmetic issue.
Now if it is a concern to you, you can change the routing to something like the following:
resources :tasks, except: [:create, :new]
post 'new_task' => 'tasks#create'
get 'new_task' => 'tasks#new'
This is mapping the POST and GET http methods to /new_task so the url appears the same for the new and create action invocations. Note you do need to change the url in the form_for helper to use this new route:
<%= form_for #task, url: 'new_task', multipart: true do |f| %>
Since Rails default behaviour in forms is with remote: true, you can move the content of the form to a partial(let's name it _my_form.html.erb), add to the controller action (let's say in create action):
respond_to do |format|
format.js {}
end
Then add a create.js.erb file where you will render the form partial
$("#form").html(
"<%= j render partial: 'my_form', locals: { entity: #entity } %>"
);
Thus, validation errors and all attributes will be accessible inside the form and there is no need to hack the "Rails approach"
I had to adapt and experiment with the currently accepted answer,
and the following ended up working well (using schools rather than tasks):
In config/routes.rb:
resources :schools do
...
end
post 'schools/new' => 'schools#create'
And create the form like:
= simple_form_for [#school], url: 'new' do |f|
Thus the path /schools/new was preserved on validation :)

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?

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

Keep params after render Ruby on Rails

I have a Project that belongs to User. In my user view I have a link to add a new project, with the parameter for the user I want to add the project to.
<%= link_to 'Add new project', :controller => "project", :action => "new", :id => #user %>
Url: /projects/new?id=62
Adding a project to a user works. The problem is when the validation fails while adding a new project and I do a render.
def create
#project = Project.new(params[:project])
if #project.save
redirect_to :action => "show", :id => #project.id
else
render :action => "new"
end
end
view:
<%= form_for #project do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.hidden_field :user_id , :value => params[:id] %>
<%= f.submit "Create project" %>
<% end %>
routes
resources :users do
resources :projects
end
How can I keep the parameter for the user after the render? Or is there some better way to do this? Been looking at a lot of similar questions but can't get it to work.
try
render :action => "new", :id => #project.id
if its not works for you, then try alternate way to pass the parameter to your render action.
This can also help you->
Rails 3 Render => New with Parameter
You shouldn't use params[:id] to assign value to this form field. Instead, add this to your #new action in controller:
def new
#project = Project.new(user_id: params[:id])
end
and then just write this in your form:
<%= f.hidden_field :user_id %>
Because #project was defined in your #new and #create actions and because it already contains a Project instance with a user_id assigned to it, the value would automatically be added to this field.

Rails 3 : Can't get form_for to work as a 'delete' following the RESTful achitecture => always giving a ROUTING ERROR

I have a very simple render that goes as follow:
<%= form_for(:relationships, :url => relationships_path, :html => {:method => 'delete'}) do |f| %>
<div><%= f.hidden_field :user_id_to_unfollow, :value => #user.id %></div>
<div class="actions"><%= f.submit "Unfollow" %></div>
<% end %>
When I submit this form it will always give me a
Routing Error
No route matches "/relationships"
on my page.
In my relationships controller, I have created all the propers methods:
def create
...
end
def destroy
...
end
def update
...
end
def show
...
end
And in my routes config I have made sure to allow all routes for the relationships controller
resources :relationships
But I can't seem to get into the destroy method of the controller :(
However if I remove the
:html => {:method => 'delete'}
method parameter in the form_for then I get to the create method of the controller no pb.
I don't get it....
Alex
ps: this is the rake routes results for relationships:
relationships GET /relationships(.:format) {:action=>"index", :controller=>"relationships"}
POST /relationships(.:format) {:action=>"create", :controller=>"relationships"}
You should point the delete request to single resource url eg. relationships/4325. Run rake routes to view what url/verb combinations are valid.
--edit
Routes for relationship resources:
resources :relationships, :only => [:index, :create, :destroy]
Unfollow button (creates a form for itself):
= button_to "Unfollow", relationship_path(relationship), :method => 'delete'

Resources