My nested relationship is switching the ids of the models - ruby-on-rails

I'm trying to delete an attachment from my rails project. I have the attachment nested under test_suite. When I hit the attachment delete button, the generated url switch the ids of attachment and test_suite. ex. the url should be localhost:3000/test_suites/3/attachments/11 but I'm getting localhost:3000/test_suites/11/attachmnts/3 and I get the error that attachment id=3 doesn't exist which is right because it should be attachment id = 11
Here is how I set it up.
routes.rb:
resources :test_suites do
resources :attachmnts, :only => [:create, :new, :destroy]
end
model/test_suite.rb:
class TestSuite < ApplicationRecord
has_many :attachemnts
end
model/attachment.rb:
class Attachment < ApplicationRecord
belongs_to :test_suite
has_attached_file :attach
validates_attachment_content_type :attach, :content_type => ["text/xml", "text/plain","text/html"]
end
I'm creating new attachment in test_suite show.The attachment will have a test_suite_id attribute.
test_suites_controller.rb:
def show
# create a variable pammed to test_suite with id passed from previous page
#test_suite = TestSuite.find(params[:id])
#attachment = Attachment.new
#attachment.test_suite_id = #test_suite.id
end
test_suite/show.html.erb:
<table class="table table-hover table-responsive">
<thead>
<tr>
<th>File Name</th>
<th>File Type</th>
<th>File Size</th>
<th>Created At</th>
<th></th>
</tr>
</thead>
<tbody>
<%= render partial: 'attachment', locals: {test_suite: #test_suite} %>
</tbody>
</table>
attachments/_attachment.html.erb:
<% test_suite.attachemnts.each do |attachment| %>
<tr>
<td> <%=attachment.attach_file_name %> </td>
<% if attachment.attach_content_type == 'text/plain'%>
<td>txt</td>
<% else %>
<td><%= attchement.attach_content_type.split('/').last %></td>
<% end %>
<td><%= attachement.attach_file_size %></td>
<td><%= attachement.created_at %></td>
<td><%= link_to '<span class="glyphicon glyphicon-remove-sign"></span>'.html_safe, test_suite_attachment_path(attachment),class:"btn btn-lg", method: :delete, data: {confirm: "Are you sure you want to delete the file?"} %>
</td>
</tr>
<% end %>
My rails routes outputs:
Prefix Verb URI Pattern Controller#Action
root GET / test_suites#index
test_suite_attachements POST /test_suites/:test_suite_id/attachements(.:format) attachements#create
new_test_suite_attachement GET /test_suites/:test_suite_id/attachements/new(.:format) attachements#new
test_suite_attachement DELETE /test_suites/:test_suite_id/attachements/:id(.:format) attachements#destroy
test_suites GET /test_suites(.:format) test_suites#index
POST /test_suites(.:format) test_suites#create
new_test_suite GET /test_suites/new(.:format) test_suites#new
edit_test_suite GET /test_suites/:id/edit(.:format) test_suites#edit
test_suite GET /test_suites/:id(.:format) test_suites#show
PATCH /test_suites/:id(.:format) test_suites#update
PUT /test_suites/:id(.:format) test_suites#update
DELETE /test_suites/:id(.:format) test_suites#destroy

When you're dealing with a nested object, you need the ids of both the parent and child object to perform show/edit/update/destroy operations. The issue is because you're not passing the test_suite object to test_suite_attachment_path helper. Modify your link to
<%= link_to '<span class="glyphicon glyphicon-remove-sign"></span>'.html_safe, test_suite_attachement_path(test_suite, attachment),class:"btn btn-lg", method: :delete, data: {confirm: "Are you sure you want to delete the file?"} %>
You can also pass an array of the parent and child objects instead of using the path helper.
#[test_suite, attachment]
<%= link_to '<span class="glyphicon glyphicon-remove-sign"></span>'.html_safe, [test_suite, attachment], class:"btn btn-lg", method: :delete, data: {confirm: "Are you sure you want to delete the file?"} %>
Please use a consistent spelling of 'attachments'. You're using a different version in every place.

Related

Create a child resource through a parent resource's index table - Ruby on Rails

Hi I want to put a link in a index table of a resource to create a child resource.
I got a Sale, and an operation:
class Sale < ApplicationRecord
belongs_to :user
has_one :operation, dependent: :destroy
end
class Operation < ApplicationRecord
belongs_to :sale
end
And I want to be able to create an operation through an index table of sales.
Thing is, I need it to be created on the button/link click, and not be transferred to a new operation view. (Operation has only sale_id and id as attributes)
Something like that
<table>
<tr>
<th>Sale Id</th>
</tr>
<% #sales.each do |sale| %>
<td><%= sale.id %></td>
<td><%= link_to 'Show', sale_path(sale) %></td>
<td><%= link_to 'Edit', edit_sale_path(sale) %></td>
<td><%= link_to 'Delete', sale,
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
<td> <%= form_for (#operation) do |o| %>
<%= o.submit 'Create Operation'%>
<% end %>
</td>
</tr>
<% end %>
</table>
But I got this error: 'First argument in form cannot contain nil or be empty'
I also tried this:
<td>
<%= fields_for :operation, #sales.operation do |o| %>
<%= o.submit "Create Operation" %>
<% end %>
</td>
And I got this:
undefined method `operation' for Sale::ActiveRecord_Relation:0xb1a4ece4>
tried this as well :
<td>
<%= link_to 'Create Operation', 'operations#create' %>
</td>
But it sends me to the operation index view without creating an operation.
First argument in form cannot contain nil or be empty
The error means #operation is nil in the form. You should define #operation in the sales#index method as the form is in sales/index.html.erb
def index
#operation = Operation.new
end
I need it to be created on the button/link click, and not be
transferred to a new operation view
If you meant,you shouldn't be redirected to another view after the button, then you have to use AJAX. I recommend you see these Guides to understand how to do with AJAX

bootstrap-confiirmation doesn't handle method :delete in rails app

I'm trying to use bootstrap-confirmation for an index view with datatables and a column for deleting the row item. I have it working without the confirmation, or with the default confirmation, but not with bootstrap-confirmation, it sends me to the show method, rather than the delete method of the controller. Its like it doesnt see the method: :delete
Here is the way I'm calling it in the index view. The popup confirmation displays, but when I click ok, it sends me to the show page.
<td><%= link_to '<i class="fa fa-trash-o fa-lg"></i>'.html_safe, role_path(id: role.id), method: :delete, :'data-toggle' => 'confirmation', :'data-copy-Attributes' => 'href data-method'%></td>
This following works (without confirmation), so I know my routes, controller action, etc work.
<td><%= link_to '<i class="fa fa-trash-o fa-lg"></i>'.html_safe, role_path(id: role.id), method: :delete, %></td>
Any ideas?
Heres my view:
<h1> Roles</h1>
</br>
<table width="100%" class="table table-striped table-bordered table-hover" id="roles-table">
<thead>
<tr>
<th>Roles</th>
<th>User Count</th>
<th>Delete Role</th>
</tr>
</thead>
<tbody>
<% #roles.each do |role| %>
<tr>
<td><%= role.name %>
<td><%= role.users.count %></td>
<% if (role.users.count == 0) %>
<!--td><%= link_to '<i class="fa fa-trash-o fa-lg"></i>'.html_safe, role_path(id: role.id), method: :delete, :data => {:confirm => 'Are you sure?'}%></td-->
<td><%= link_to '<i class="fa fa-trash-o fa-lg"></i>'.html_safe, role_path(id: role.id), method: :delete, :'data-toggle' => 'confirmation', :'data-copy-Attributes' => 'href data-method'%></td>
<% else %><
<td></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to "Add Role", new_role_path, class: "btn btn-success"%>
I couldn't make the library work because it doesn't provide a way to edit the popup html it generates, so, it also took for me the link to the show method and no way to delete it.
So what I did was to create a route to delete the resource that expects to receive the id from the resource and a value in the params for delete:
get 'role/:id/:delete', to: 'role#show', as: 'show_role'
Then in your controller you can check if you're receving a delete param, in order to separate between show and destroy, if you receive it, then you destroy it and redirects to the roles_url:
def show
#role = Role.find(params[:id])
if params[:delete]
#role.destroy
redirect_to roles_url
end
end
Then in your view you can use a link_to helper, passing the route created previously passing the resource and a value for the delete param, adding the data attributes Bootstrap Confirmation needs to make it work:
<%= link_to 'Destroy', show_role_path(role, delete: true), data: { toggle: 'confirmation', title: 'Delete it?' } %>

Rails - routing error on delete

UPDATE rephrased after learning
I have extended my app with a new model called 'tag' (very similar set up as with comments in blog).
Intended outcome: the user to stay on the page when clicking the link_to button to delete a tag; I now get an routing error (below) and I don't understand why.
No route matches [DELETE] "/annotations/7/tags"
The TAG list is added to the view for ANNOTATIONS like this:
<div class="panel panel-default" style="background-color: white; word-wrap: break-word; font-size: 0.9em;">
<table id="tags" class="table table-hover">
<thead>
<tr>
<th>Tag</th>
<th>Type</th>
<th></th>
</tr>
</thead>
<tbody>
<% #annotation.tags.each do |tag| %>
<tr>
<td><%= tag.content %></td>
<td><%#= tag.tagtype_id | tag.tagtype.typeoftag %></td>
<td><%= link_to '', [tag.annotation, tag], method: :delete, data: { confirm: 'Please confirm deletion!' }, :class => "glyphicon glyphicon-remove" %></td>
</tr>
<% end -%>
</tbody>
</table>
</div>
These are my routes:
Rails.application.routes.draw do
root 'dashboard#index'
devise_for :users
resources :users, :documenttypes, :tagtypes
resources :documents do
resources :tags
get "pdf", on: :member
end
resources :annotations do
resources :comments, :tags
get "pdf", on: :member
end
get "annotations/:id/annotate" => "annotations#annotate", as: 'annotate'
get "angular_test", to: "angular_test#index"
mount PdfjsViewer::Rails::Engine => "/pdfjs", as: 'pdfs'
And the tags.controller
class TagsController < ApplicationController
def create
#annotation = Annotation.find(params[:annotation_id])
#comment = #annotation.tags.create(tag_params)
redirect_to annotation_path(#annotation)
end
def destroy
#annotation = Annotation.find(params[:annotation_id])
#comment = #annotation.tags.find(params[:id])
#comment.destroy
redirect_to annotate_path(#annotation)
end
private
def tag_params
params.require(:tag).permit(:content, :location, :tagtype_id)
end
end
UPDATE
The table in the view always has an empty row, which I cannot delete. I then get the routing error. With rows that were created when adding a tag, this does not happen and I can delete them. Why am I getting an empty row?
This needs to be done using AJAX (asynchronously), all you need to do is to have your destroy action to respond to javascript. You can do that by adding the remote option to the link for the deletion as below:
<td><%= link_to 'Delete', [tag.annotation, tag], method: :delete, remote: true, data: { confirm: 'Please confirm deletion!' }, :class => "glyphicon glyphicon-remove" %></td>
The remote: true option makes the request AJAX. Now on the controller side you need to respond with the resulting javascript code that you want to execute, probably hiding this row that you want to delete.
In the views folder, under the tags controller you need to create a file called destroy.js and in this file you specify the code that you want to be executed for handling this to hide for example that row.

Battling to display nested data in Rails

So I have a form with nested data that is correctly displaying in the form and is being correctly saved. However, I'm now working on trying to display this data and I'm really battling.
So I have a lot of data, but will focus on two specific types of data:
I have users, and users can create projects. The two data fields I'm battling with related to projects are:
Each project has a budget (stored as a budget_id on the project table, and for which I want to call the name of the budget)
Each project a bunch of features through a features_projects join table
Project Model
class Project < ApplicationRecord
belongs_to :user
has_one :budget
has_and_belongs_to_many :features
end
Budget Model
class Budget < ApplicationRecord
belongs_to :project
end
Features Model
class Feature < ApplicationRecord
has_and_belongs_to_many :projects
end
Extract of the index method from the Projects Controller
def index
#projects = Project.all
end
Project View to show the index
<div class="container dashboard">
<div class="center">
<h1> Your Projects</h1>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Description</th>
<th>Budget</th>
<th>User_id</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #projects.each do |project| %>
<tr>
<% if project.user == current_user %>
<td><%= project.description %></td>
<td><%= project.budget_id %></td>
<td><%= project.user.email if project.user %></td>
<td><%= link_to 'Show', project, class: "btn btn-info btn-sm" %></td>
<td><%= link_to 'Edit', edit_project_path(project), class: "btn btn-warning btn-sm" %></td>
<td><%= link_to 'Destroy', project, class: "btn btn-danger btn-sm", method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<br>
<div class="center">
<%= link_to 'New Project', new_project_path, class:"btn btn-lg btn-success" %>
</div>
</div>
I basically want to be able to display the budget saved for each project as well as the list of features that they chose. I have been trying a few different things from solutions online, but haven't had any luck :( I think perhaps I need to create a #budget variable perhaps under the index method, but I haven't had any luck with that either. Could anyone kindly help explain how I would do this?
Thanks so much!
UPDATE
Using project.budget.name reveals the following error:
ActiveRecord::StatementInvalid in Projects#index
PG::UndefinedColumn: ERROR: column budgets.project_id does not exist
LINE 1: SELECT "budgets".* FROM "budgets" WHERE "budgets"."project_...
When you say that:
Each project has a budget (stored as a budget_id on the project table, and for which I want to call the name of the budget)
This is not how has_one works, it will expect a project_id on the budget table.
I think you need to use belongs_to.
http://guides.rubyonrails.org/association_basics.html#the-has-one-association
project.budget.name #give name of budget
project.features do |feature|
feature.name # gives feature name
end

How Do I Get an Modified Column from a Rails Scaffold to Hold Information as the others?

I generated a scaffold for a To-Do List app, and I left out some columns to add later.
I ran the command to create a migration to add a new column named client and I changed my files so that it shows on the projects index and form, but when i enter something into the client field and submit, it doesn't save the information and remains blank..
Update 1:
Here is whats in my routes:
'
Rails.application.routes.draw do
root :to => 'projects#index'
resources :projects
end
'
Here is my index view:
'
<h1 id="title">Project List</h1>
<table>
<thead>
<tr id="headers">
<th>Title</th>
<th>Client</th>
<th>Description</th>
<th>Hours</th>
<th>Done</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody class="col-md-2" id="listItems">
<% #projects.each do |project| %>
<tr id="table">
<td><%= project.title %></td>
<td><%= project.client %></td>
<td><%= project.description %></td>
<td><%= project.hours %></td>
<td><%= project.done %></td>
<td><%= link_to " #{image_tag('show.png')}".html_safe, project, id:'showButton' %></td>
<td><%= link_to " #{image_tag('edit.png')}".html_safe, edit_project_path(project), id:'editButton' %></td>
<td><%= link_to " #{image_tag('destroy.png')}".html_safe, project, id:'destroyButton', method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Project', new_project_path, id:"new" %>
<footer id="footer">Copyright 2014 Kira Banks</footer>
'
To keep your application secured, Rails has a feature called Strong Parameters and the docs says:
It provides an interface for protecting attributes from end-user
assignment. This makes Action Controller parameters forbidden to be
used in Active Model mass assignment until they have been whitelisted.
So, basically you need to whitelist the new client attribute in the Projects controller by adding it to the list:
class ProjectsController < ApplicationController
# ...
# at the end of the file
private
def project_params
params.require(:project).permit(:title, :description, :hours, :done, :client)
end
end

Resources