How do I fix my resource post and delete methods - ruby-on-rails

ControlPanelController aka control_panel_controller.rb is my main controller code in which I want to handle all the logistics. Picture class aka picture.rb is the model I use.
In my resources I have
resource :control_panel, only: [:index, :new, :create, :destroy], controller: 'control_panel'
rake routes shows me
control_panel POST /control_panel(.:format) control_panel#create
new_control_panel GET /control_panel/new(.:format) control_panel#new
DELETE /control_panel(.:format) control_panel#destroy
Here is my ControllerPanelController
class ControlPanelController < ApplicationController
def index
#pictures = Picture.all
end
def new
#picture = Picture.new
end
def create
#picture = Picture.new(picture_params)
if #picture.save
redirect_to control_panel_path, notice: "The picture #{#picture.name} has been uploaded."
else
render "new"
end
end
def destroy
#picture = Picture.find(params[:id])
#picture.destroy
redirect_to control_panel_path, notice: "The picture #{#picture.name} has been deleted."
end
private
def picture_params
params.require(:picture).permit(:name, :attachment)
end
end
in my app/views/control_panel/new.html.erb I use url: control_panel_path(#picture) so I can post to the ControlPanel create method but it doesn't work. I get
ERROR 1:
No route matches [GET] "/control_panel"
<% if !#picture.errors.empty? %>
<div class="alert alert-error">
<ul>
<% #picture.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for #picture, url: control_panel_path(#picture), :html => {:multipart => true} do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.file_field :image %>
</p>
<p>
<%= f.label :remote_image_url, "or image URL" %><br />
<%= f.text_field :remote_image_url %>
</p>
<p><%= f.submit %></p>
<% end %>
ERROR 2:
When I try to delete a resource from index. I get
Couldn't find Picture without an ID
Extracted source (around line #22):
def destroy
#picture = Picture.find(params[:id])
#picture.destroy
redirect_to control_panel_path, notice: "The picture #{#picture.name} has been deleted."
end
Line 22 in that case is #picture = Picture.find(params[:id])
so what's the proper way to fix my routes.. I am using resource instead of resources due to having only one control panel since it's an app for one single user instead of multiple ones
EDIT:
code for app/views/control_panel/index.html.erb
<h1>Control Panel</h1>
<p>Manage all your pictures here</p>
<% if !flash[:notice].blank? %>
<div class="alert alert-info">
<%= flash[:notice] %>
</div>
<% end %>
<br />
<%= link_to "Add Picture", new_control_panel_path, class: "btn btn-primary" %>
<br />
<br />
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Picture</th>
<th> </th>
</tr>
</thead>
<tbody>
<% #pictures.each do |picture| %>
<tr>
<td><%= picture.name %></td>
<td><%= image_tag picture.image_url(:thumb) %></td>
<td><%= button_to "Delete", control_panel_path(picture), method: :delete, class: "btn btn-danger", confirm: "Are you sure that you wish to delete #{pi
</tr>
<% end %>
</tbody>
</table>
**EDIT 2:**
Got the delete working by fixing button_to code as such
<td><%= button_to "Delete", control_panel_path(picture.id), method: :delete, class: "btn btn-danger", confirm: "Are you sure that you wish to delete #
</tr>

ERROR 1
You are getting the error because your app does not have a route corresponding to [GET] "/control_panel". Add :show to your control_panel routes and show action to your ControlPanelController. Also remember that singular resource does not have :index route/action.
http://guides.rubyonrails.org/routing.html#singular-resources
ERROR 2
You are getting the error because params[:id] is nil. control_panel_path(picture) does not set params[:id] because you are using control_panel_path and not pictures_path. Try passing id as GET parameter: control_panel_path(id: picture.id).
Nested resources is also a good way to go: http://guides.rubyonrails.org/routing.html#nested-resources

It seems your route control_panel#destroy does not take id param.
Use "resoureces" instead of "resource" like below.
resources :control_panel, only: [:index, :new, :create, :destroy], controller: 'control_panel'
This generates route like this.
control_panel_index GET /control_panel(.:format)
POST /control_panel(.:format)
new_control_panel GET /control_panel/new(.:format)
control_panel DELETE /control_panel/:id(.:format)

Related

What is the best way to do many CRUDs in one view (without using ajax)?

I have 3 forms in a view called 'home' each form submit different model, each form redirect to a create action in the respective controller. After create each controller redirect to the administration view which also index each model and provide edit and delete links.
I don't know if this approach is a good practice since i have to instance all in the administrators_controller, also i have to instance each model in their respective controllers again.
administrators_controller.rb
def home
#user = Employee.new(role: :emp)
#market = Market.find(params[:market_id])
#INDEX EMPLOYEE:
#employees = #market.users.where(role: :emp)
#INDEX SERVERS:
#server = Server.new(market_id: #market.id)
#servers = #market.servers
#INDEX TRAMITES:
#task = Task.new(market_id: #market.id)
#tasks = #market.tasks
end
users_controller.rb
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attribute(:email, params[:user][:email])
flash[:success] = "User updated"
redirect_to market_admin_path(current_user.market_id)
else
render 'edit'
end
end
home.html.erb
<% provide(:button_text, 'create') %>
<%= render 'users/form', url: market_employees_path %>
<h1> Empleados: </h1>
<% #employees.each do |employee| %>
<ul>
<li><%= employee.email%> <%= link_to 'edit', edit_user_path(employee) %></li>
</ul>
<% end %>
<br>
<h1> Servers: </h1>
<table style="width:100%">
<tr>
<th>ID</th>
<th>Descripcion</th>
</tr>
<%= render 'servers/form', url: market_servers_path %>
<% #servers.each do |serv| %>
<tr>
<td><%= serv.id%></td>
<td><%= serv.description%> <%= link_to 'edit', edit_server_path(serv) %></td>
</tr>
<% end %>
</table>
<h1> Tramites: </h1>
<%= render 'tasks/form', url: market_tasks_path %>
<table style="width:100%">
<tr>
<th>ID</th>
<th>Descripcion</th>
<th>Duracion</th>
</tr>
<% #tasks.each do |task| %>
<tr>
<td><%= task.id%></td>
<td><%= task.name%></td>
<td><%= task.duration%> <%= link_to 'edit', edit_task_path(task) %></td>
</tr>
<% end %>
</table>
users/_form.html.erb
<%= form_for(#user, url: local_assigns[:url]) do |f| %>
<%= render 'shared/flash_type', type: 'employee'%>
<%= f.hidden_field :role %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
routes.rb
resources :markets, only: [], shallow: true do
resources :employees, only: [:create]
resources :users, only: [:edit, :update]
resources :tasks, only: [:create, :edit, :update]
resources :servers, only: [:create, :edit, :update]
get '/administration', :to => 'administrators#home', :as => :admin
end
It actually works but i would like to know if there is a better way to do this.
Note: 'employee' class was created to use form objects pattern

form_for submit button does not work for ruby on rails

my form_for submit button only works partially; the data is saved in the database but the redirection fails. Here are the relevant codes for the form:
app/views/orders/new.html.erb
<h1>Menu</h1>
<%= link_to "Back to channel", current_channel_path %>
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th>Meal</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<% #menu_items.each do |t| %>
<p>
<tr>
<td><%= t.name %></td>
<td><%= number_to_currency(t.price) %></td>
<%= form_for (#order), url: orders_path, remote: true do |f| %>
<td><%= f.number_field :quantity, value: 1, class: "form-control", min: 1 %></td>
<%= f.hidden_field :meal, :value => t.name %>
<%= f.hidden_field :unit_price, :value => t.price %>
<td><%= f.submit "Order", class: "btn btn-primary" %></td>
<% end %>
</tr>
</p>
<% end %>
</tbody>
</div>
Here are the codes for routes.rb
Rails.application.routes.draw do
#For Orders
get 'orders' => 'orders#new'
post 'orders' => 'orders#create'
get 'all_orders' => 'orders#show'
resources :users
resources :orders
Here are the relevant codes for orders
def new
#menu_items = MenuItem.all
#order = Order.new
end
def create
#order = current_channel.orders.build(order_params)
#order.user = current_user
if #order.save
flash.now[:success] = "Order has been recorded!"
redirect_to all_orders_path
else
flash.now[:danger] = "Order was not recorded!"
render 'new'
end
end
I read that the submit button may not work as it is nested in the table. I have tried putting the form outside of the table but the submit still does not redirect; the submit button however creates the record in the orders database in both cases. Any idea why this is the case? Thanks in advance for any help!
You need a route set for the index path which isn't listed above. If you're following standard crud conventions you should just use "resources :orders" in your routes files which will generate the post/put/delete/get routing you need. Then your redirection will work.

How can I leave a comment and associate with the post in post' index page?

How can I leave a comment and associate with the post in post' index page?
It is my PostsController:
def index
#posts = Post.all
"what should I add here?"
end
# GET /posts/1
# GET /posts/1.json
def show
#comments = #post.comments.all
#comment = #post.comments.build
end
and its my posts show view:
<p id="notice"><%= notice %></p>
<p>
<h3><%= #post.name %></h3>
</p>
<p>
<%= (#post.descriptopm).html_safe %>
</p>
<%= link_to 'Edit', edit_post_path(#post), :class => "btn btn-info btn-xs" %>
<%= link_to 'Back', posts_path, :class => "btn btn-info btn-xs" %>
<h3>Comments</h3>
<% #comments.each do |comment| %>
<div>
<strong><%= comment.user_name %></strong>
<br />
<p><%= (comment.body).html_safe %></p>
</div>
<% end %>
<%= render 'comments/form' %>
and its my posts index view:
<h1>Listing posts</h1>
<%= link_to 'Create a New Post', new_post_path, :class => "btn btn-success btn-sm" %>
<% #posts.each do |post| %>
<div class="post thumbnail">
<h3><%= post.name %></h3>
<div><%= (post.descriptopm).html_safe %></div>
<div class="bottom-bottoms">
<%= link_to 'Display', post, :class => "btn btn-info btn-xs" %>
<%= link_to 'Edit', edit_post_path(post), :class => "btn btn-info btn-xs" %>
<%= link_to 'Delete', post, method: :delete, data: { confirm: 'Are you sure?' }, :class => "btn btn-info btn-xs" %>
</div>
<h3>Comments</h3>
<% post.comments.each do |comment| %>
<div>
<strong><%= comment.user_name %></strong>
<br />
<p><%= (comment.body).html_safe %></p>
</div>
<% end %>
<%= render 'comments/form' %>
</div>
<% end %>
the post.rb :
class Post < ActiveRecord::Base
has_many :comments
end
the comment.rb :
class Comment < ActiveRecord::Base
belongs_to :post
end
the show page's comment function looks right
but when I leave a comment in my post's index page
It might not save the comment with the right post's id
how do I fix it?
and onother one:
how can I redirect the page to the index page after I save the comment not to comment's index page?
How can I leave a comment and associate with the post in post' index
page?
There are several things to consider:
Comment Objects
Firstly, you need to appreciate that since Rails is an object orientated framework (by virtue of being built on Ruby), you will need to ensure your new comment corresponds with the relevant Post object
I think this the core of what you're getting confused about:
def index
#posts = Post.all
# Here needs to go your "comment" build methodology. Except, it only works *per* post ;)
end
The trouble you have is that you can't "build" a comment for Posts - you have to build them per post, like this:
#post = Post.find 2
#post.comments.new #-> allows you to create a new comment for that particular post
--
Implementation
The best solution I can give you will be somewhat constricted, but will serve your purposes correctly. Here it is:
#app/controllers/posts_controller.rb
Class PostsController < ApplicationController
def index
#posts = Post.all
#comment = Comment.new
end
end
#app/controllers/comments_controller.rb
Class CommentsController < ApplicationController
def create
#comment = Comment.new(comment_params)
#comment.save
end
private
def comment_params
params.require(:comment).permit(:comment, :params, :post_id)
end
end
This will take into consideration that for each comment on your index, you'll have a form with the post_id attached; and will be a "linear" flow (IE you can only post a single comment at a time):
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= post.title %>
<%= form_for #comment do |f| %>
<%= f.hidden_field :post_id, value: post.id %>
<%= f.text_field :title %>
<%= f.submit %>
<% end %>
<% end %>
Yes, this will give each Post a "new comment" form on your index page (hence my denoting its constriction). However, what it will do is give you the ability to add a comment for any of the posts on the page, handling them with the CommentsController
To answer your second second question, you can use
redirect_to post_index_path
As for the first question, you can set the post id of the post by using:
#post.comments.new
Instead of just Comment.new, which won't set the ID of the post.
In this scenario...this is the best example that you can get help
Railscasts show post with comments using polymorphism

form_for on commentables in rails

I'm having problems with the "First argument in form cannot contain nil or be empty" error that I haven't been able to find an answer for.
Basically, I have Posts, which show up on a User's show page, which should each have the option to comment on them. My routes are as follows:
resources :posts, only: [:create, :destroy] do
resources :comments, only: [:create, :destroy]
end
users/show.html.erb
<ol class="posts">
<%= render #posts %>
</ol>
posts/_post.html.erb
<li>
<span class="content"><%= post.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(post.created_at) %> ago.
</span>
<% if current_user?(post.user) %>
<%= link_to "delete", post, method: :delete,
data: { confirm: "You sure?" },
title: post.content %>
<% end %>
<span class="content">
<ul> Comments: <%= post.comments.count %></ul>
<% post.comments.each do |comment| %>
<ul> <%= comment.comment %> </ul>
<% end %>
</span>
<% if post != nil %>
<% form_for [post, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Comment..." %>
</div>
<%= f.submit "Post", class: "btn btn-lg btn-primary" %>
<% end %>
<% end %>
</li>
s comments_controller.rb
def create
#post = Post.find(params[:id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
if #comment.save
flash[:success] = "Posted!"
redirect_to #post
else
render 'static_pages/home'
end
end
def destroy
#comment.destroy
redirect_to root_url
end
Showing C:/app/views/posts/_post.html.erb where line #21 raised: (line 21 is the form_for line)
First argument in form cannot contain nil or be empty
app/views/posts/_post.html.erb:21:in _app_views_posts__post_html_erb___306000501_37434348'
app/views/users/show.html.erb:19:in_app_views_users_show_html_erb__480533737_37130988'
If each post had a show page, I know that I would put #posts on the show action and then make it an instance variable, but since the each post needs a separate comment dialogue box, I can't make it that general. It doesn't go through the posts controller though so I can't make it specific to each post. I'm using acts_as_commentable for the comments. Ideally, I would like to make them generic and put all the form_for comment stuff into a partial, but I'll figure that out later.

errors uploading a file "name cant be blank"

I am trying to add the option to upload and download files in my Rails application, but I keep getting this error when I try to upload my file:
name can't be blank
Here's my code:
newsletters_controller.rb:
class NewslettersController < ApplicationController
def index
#newsletters = Newsletter.all
end
def new
#newsletter = Newsletter.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #newsletter }
end
end
def create
#newsletter = Newsletter.new(newsletter_params)
if #newsletter.save
redirect_to newsletters_path, notice: "The newsletter #{#newsletter.name} has been uploaded."
else
render "new"
end
end
def destroy
#newsletter = Newsletter.find(params[:id])
#newsletter.destroy
redirect_to newsletters_path, notice: "The newsletter #{#newsletter.name} has been deleted."
end
private
def newsletter_params
params.require(:newsletter).permit(:newsletter, :attachment)
end
end
index.html.erb:
<% if !flash[:notice].blank? %>
<div class="alert alert-info">
<%= flash[:notice] %>
</div>
<% end %>
<br />
<%= link_to "New Newsletter", new_newsletter_path, class: "btn btn-primary" %>
<br />
<br />
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Download Link</th>
<th> </th>
</tr>
</thead>
<tbody>
<% #newsletters.each do |newsletter| %>
<tr>
<td><%= newsletter.name %></td>
<td><%= link_to "Download Newsletter", newsletter.attachment_url %></td>
<td><%= button_to "Delete", newsletter, method: :delete, class: "btn btn-danger", confirm: "Are you sure that you wish to delete #{newsletter.name}?" %></td>
</tr>
<% end %>
</tbody>
</table>
new.html.erb:
<% if !#newsletter.errors.empty? %>
<div class="alert alert-error">
<ul>
<% #newsletter.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="well">
<%= form_for #newsletter, html: { multipart: true } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :attachment %>
<%= f.file_field :attachment %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
</div>
routes.rb:
resources :newsletters, only: [:index, :new, :create, :destroy]
root "newsletters#index"
get "newsletters/index"
get "newsletters/new"
get "newsletters/create"
get "newsletters/destroy"
change:
params.require(:newsletter).permit(:newsletter, :attachment)
to:
params.require(:newsletter).permit(:newsletter, :attachment, :name)
This way, the model will receive the name from the form and validation will succeed.

Resources