accepts_nested_attributes_for - Preventing destroy on primary model? - ruby-on-rails

I have a photo gallery that has a Gallery model and an Asset model. See below:
class Gallery < ActiveRecord::Base
attr_accessible :gallery_name, :description, :assets_attributes
has_many :assets, :dependent => :destroy
accepts_nested_attributes_for :assets, :allow_destroy => true
def find_featured_image
assets.featured
end
def find_only_featured
assets.only_featured
end
class Asset < ActiveRecord::Base
belongs_to :gallery
has_attached_file :image,
:styles => {
:thumb => '150x150#',
:medium => '300x300>',
:large => '600x600>'
}
def self.featured
where( :featured => true ).limit(1)
end
scope :only_featured, where(:featured => true)
end
end
I am able to destroy the assets through my Gallery form, but when I try to destroy the Gallery itself, I am simply redirected to the Show view with no confirmation notice.
Here is my form:
<div id="gallery">
<div class="headers"><h1>MDN Photo Gallery</h1></div></br>
<% #galleries.each do |gallery| %>
<div id="gallery_wrap">
<div id="gallery_left">
<div id="gallery_head"><h2><%= gallery.gallery_name %></h2></div>
<div id="gallery_desc"><%= gallery.description %></div>
</div>
<div id="gallery_rt">
<div id="gallery_featured">
<% for asset in gallery.assets.featured %>
<%= link_to( image_tag(asset.image.url(:medium)), asset.image.url(:large), :class => "fancybox", :rel => gallery.id ) %>
<% end %>
</div>
<div id="gallery_photos">
<% for asset in gallery.assets %>
<%= link_to( image_tag(asset.image.url(:thumb)), asset.image.url(:large), :class => "fancybox", :rel => gallery.id ) %>
<% end %>
</div>
<td><%= link_to 'Show', gallery %></td>
<td><%= link_to 'Edit', edit_gallery_path(gallery) %></td>
<td><%= link_to 'Destroy', gallery, :confirm => 'Are you sure?', :method => :delete %></td>
</div>
</div>
</br>
<% end %>
<br />
<%= link_to 'New Gallery', new_gallery_path %>
</div>
I'm using the standard destroy method that gets created with scaffold:
def destroy
#gallery = Gallery.find(params[:id])
#gallery.destroy
respond_to do |format|
format.html { redirect_to(galleries_url) }
format.xml { head :ok }
end
Is there some change I need to make to the model? the destroy method? anything else? in order to be able to delete the parent of the nested model?
Thanks in advance for your insights.
Any ideas?

It sounds there's something wrong with how the JavaScript support is being loaded. There's no way to have an anchor make a DELETE request directly, so Rails uses JavaScript to respond to the link's click event, and then make the proper request. Without that, the link simply acts as a GET.

I found this discussion on another site that describes exactly the situation I'm faced with:
http://railsforum.com/viewtopic.php?id=38460
The recommendation was made to use the button_to instead of the standard link_to for purposes of security against web bots. I had not heard that before.
Also it was observed that the absence of the <%= csrf_meta_tag %> was found and that the original issue went away and the link_to worked. That is not the case for me.
So now I have a button_for and am able to destroy the record. However, I am not getting the confirmation notice (:confirm). So there is still something wrong, but I can at least deal with the need to delete the record.

I am now able to get the confirmation notification working (:confirm). What I had done was changed javascript_include_tag :defaults to javascript_include_tag :all. I had forgotten I made that change. I'm not sure what is included when the :defaults method is called, but just wanted to let anyone else running into this problem know that is how I fixed it. Sounds like using the Button_to instead of Link_to is still the best solution even though link_to will work now.

Related

Image not showing up from database in Rails

I am using Rails 4.1 and I created a User class. When the user is created the can import an avatar. However at best I have gotten an "image" to show up but it doesn't load the actual image from the database.
I then tried to use this post to fix it but now I am just getting "No Route Matches". Here is my exact error:
No route matches {:action=>"show_image", :controller=>"users_controller", :id=>"1"}
It claims it is coming from this line in my index.html.erb:
<%= image_tag url_for(:controller => "users_controller", :action => "show_image", :id => user.id) %>
Here is my controller:
def show_image
#user = User.find(params[:id])
send_data #user.avatar, :type => 'image/png', :disposition => 'inline'
end
And my users index.html.erb:
<html>
<head>
</head>
<body>
<h1>Users</h1>
<table>
<tr>
<th colspan="1"></th>
</tr>
<% #users.each do |user| %>
<tr>
<%= image_tag url_for(:controller => "users_controller", :action => "show_image", :id => user.id) %>
<td><%= user.username %></td>
</tr>
<% end %>
</table>
</body>
</html>
Any ideas or suggestions would be highly appreciated. Thanks ahead of time.
Edit: This is what my routes look like:
Rails.application.routes.draw do
get 'sessions/login'
get 'sessions/login_attempt'
get 'sessions/home'
get 'sessions/profile'
get 'sessions/setting'
get 'welcome/index'
get 'users/new'
post 'sessions/login_attempt'
post 'sessions/logout'
resources :users
root 'welcome#index'
end
Edit: I have also tried adding the following to the Routes file as suggested by #Question and #j0k in the thread I was following before:
resources :users do
get 'show_image', :on => :collection
end
and
get 'users/show_image' => 'users#show_image'
I also tried this
get 'users/show_image'
However I still got the same error.
Missing Image Icon that I get:
Two lines from my Users class where I add in avatar:
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
When looking up the images in the database this is what I found... I ran the following command:
sqlite> select avatar_file_name, avatar_content_type, avatar_file_size from users;
and got:
|image/png|660726
|image/gif|3614
|image/png|282
...so it is getting no file name.
When I looked up the file in the path I found that in this:
/public/system/users/avatars/000/000/001/original my image 'up.png' existed. It also existed in ../medium and ../thumb as well.
Edit: Update from #RichPeck advice.
So I was <%= image_tag #user.avatar.url %> ...instead this is what I was doing...
<% #users.each do |user| %>
<tr>
<%= image_tag user.avatar.url %>
</tr>
<% end %>
I am somewhat confused on how <%= image_tag #user.avatar.url %> would get a specific user's Avatar. Doesn't #user reference All Users? However, I tried #RichPeck suggestion anyway. I added both <%= image_tag #user.avatar.url %> and <%= debug #user.avatar %>. But both gave me the following error:
undefined method `avatar' for nil:NilClass
Edit: I just went through the Paperclip tutorial and compared it to what I have. I found a few differences but I don't think any of it would make a big difference. However, I will post it here so if anyone knows what is going wrong they can help.
Edit or New Views
Paperclip
<%= form_for #user, :url => users_path, :html => { :multipart => true } do |form| %>
<%= form.file_field :avatar %>
<% end %>
My code (The main thing I would be concerned about is the :url => but I think it should be fine)
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}, :html => { :multipart => true }) do |f| %>
<p> Username:</br> <%= f.text_field :username%> </p>
<p> Email:</br> <%= f.text_field :email%> </p>
<p> Password:</br> <%= f.password_field :password%></p>
<p> Password Confirmation:</br> <%= f.password_field :password_confirmation%> </p>
<%= f.file_field :avatar %>
<%= f.submit :Signup %>
<% end %>
Controller
Paperclip
def create
#user = User.create( user_params )
end
private
def user_params
params.require(:user).permit(:avatar)
end
My Code
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :avatar)
end
Edit: After looking at it for a bit I realized I was trying to access the users page w/o my session. I was doing localhost:3000/users not localhost:3000/users?id=1. So my ID was never being passed.
I had to add the following to my Users Controller:
def index
#users = User.all
#user = User.find(params[:id]) # <--- Added this line
end
However, <%= image_tag #user.avatar.url %> still shows the default blank image. But I have been able to get <%= debug #user.avatar %> to work. This is the output for the debug command.
--- &70308396554900 !ruby/object:Paperclip::Attachment
name: :avatar
instance: !ruby/object:User
attributes:
id: 1
username: jones1
email: stupid1#stupid.com
encrypted_password: $2a$10$dvvLzRCQQVngFpt5TfPqJOtU8s02GUj6UOHlzibWi1CaQFY1BA8tS
salt: $2a$10$dvvLzRCQQVngFpt5TfPqJO
created_at: 2014-05-10 05:19:07.138281000 Z
updated_at: 2014-05-10 05:19:07.138281000 Z
avatar_file_name:
avatar_content_type: image/png
avatar_file_size: 397
avatar_updated_at: 2014-05-10 05:19:06.715067000 Z
options: &70308396554880
:convert_options: {}
:default_style: :original
:default_url: /images/:style/missing.png
:escape_url: true
:restricted_characters: !ruby/regexp /[&$+,\/:;=?#<>\[\]\{\}\|\\\^~%# ]/
:filename_cleaner:
:hash_data: ':class/:attachment/:id/:style/:updated_at'
:hash_digest: SHA1
:interpolator: &70308352640280 !ruby/module 'Paperclip::Interpolations'
:only_process: []
:path: ':rails_root/public:url'
:preserve_files: false
:processors:
- :thumbnail
:source_file_options: &70308351975980 {}
:storage: :filesystem
:styles:
:medium: 300x300>
:thumb: 100x100>
:url: /system/:class/:attachment/:id_partition/:style/:filename
:url_generator: !ruby/class 'Paperclip::UrlGenerator'
:use_default_time_zone: true
:use_timestamp: true
:whiny: true
:check_validity_before_processing: true
post_processing: true
queued_for_delete: []
queued_for_write: {}
errors: {}
dirty: false
interpolator: *70308352640280
url_generator: !ruby/object:Paperclip::UrlGenerator
attachment: *70308396554900
attachment_options: *70308396554880
source_file_options: *70308351975980
whiny: true
Edit: Last edit for the night before I have to go to sleep. I also did <%= debug #user.avatar.url %> and it returned the following: --- /images/original/missing.png?1399954308
Since you're using paperclip, and to help maintain clarity for anyone looking at this in future:
Paperclip
When you use Paperclip, it creates an object & appends to your
original ActiveRecord object. When you define has_attached_file :____, the name of that element is what the Paperclip object is
called, meaning you'll be able to call #user.____ to access it
The problem you have is you're using another process to load your image. This is totally unnecessary as, as pointed out in the comments, Paperclip automatically assigns the .url method to the appended Paperclip object
You should be able to call <%= image_tag #user.avatar.url %> to output the relvant image. I know you know this, but I want to help anyone who may look at this thread in future
Error
In terms of your error, having looked at your comments & updates, it seems like it's either a problem with how you're calling the image, or perhaps how it's stored in the db
Can you do this for us:
<%= debug #user.avatar #-> this will show the entire avatar object %>
I believe your db will not have the correct columns, hence Paperclip pulling the wrong filename or something. There will be a really trivial error somewhere
In general the setup appears to be fairly complicated - considering you want to display a user image. For simply things like user image I am using paperclip for my projects: https://github.com/thoughtbot/paperclip .. you may want to take a look at it. It automates image upload, resize, etc. I used it in several projects and never hit any issues.
If that's not what you need, perhaps you let me know what exactly you are trying to do.

Rails 3: Can't add correct route to legacy code

Believe you can help me.
I'm trying to add new functionality to legacy code (Typo). But it seems that there is some problem about routing.
In the project routes are generated the following way:
%w{advanced cache categories comments content profiles feedback general pages
resources sidebar textfilters themes trackbacks users settings tags redirects seo post_types }.each do |i|
match "/admin/#{i}", :to => "admin/#{i}#index", :format => false
match "/admin/#{i}(/:action(/:id))", :to => "admin/#{i}", :action => nil, :id => nil, :format => false
end
My functionality is about merging articles. For that I've added new action in the /admin/content controller:
def merge
#some code here
end
A piece of a view partial (_form.html.erb) added by me:
<% if current_user.admin? and !#article.id.nil?%>
<div class=''>
<h4><%= _("Merge Articles") %></h4>
<%= label_tag :merge_with, 'Article ID' %><%= text_field_tag :merge_with, nil, :size => 20 %>
<%= button_to 'Merge', admin_content_merge_path(:id => #article.id) %>
</div>
<%end%>
This partial is rendered by another partial (_edit.html.erb)
<%= form_tag(form_action, :id => "#{form_type}_form", :enctype => "multipart/form-data", :class => className) do %>
<%= render :partial => "form" %>
<% end %>
And finally _edit.html.erb is rendered by view new.html.erb
<%= render "admin/shared/edit", { :form_type => "article", :form_action => { :action => "new", :id => #article.id , :class => ('autosave')} } %>
The problem is how to write a correct route for the controller action above which will allow me to render an edit page containing newly merged article. I wrote:
match "/admin/content/merge/:id" => "admin/content#merge",:as => 'admin/content/merge'
rake routes output:
admin_content_merge /admin/content/merge/:id(.:format) {:controller=>"admin/content", :action=>"merge"}
But the new or edit action is being invoked as I can see.
Apparently, my route is wrong, isn't it?
Could you please help me with this.
Thanks in advance!
Update
Up-to-date new.html.erb:
<% #page_heading = _('New article') %>
<%= render "admin/shared/edit", { :form_type => "article", :form_action => { :action => "new", :id => #article.id , :class => ('autosave')} } %>
<% if current_user.admin? and !#article.id.nil?%>
<%= form_tag "/admin/content/merge/#{#article.id}" do %>
<h4><%= _("Merge Articles") %></h4>
<%= label_tag :merge_with, 'Article ID' %>:
<%= text_field_tag :merge_with %><br />
<%= submit_tag "Merge" %>
<% end %>
<% end %>
Read the hint from the course:
HINT:Nesting is invalid in HTML.
That means that you can't nest form tags, don't put the form tag in another form tag, your nested form wont be able to do a correct action.
Since you have to put your code at the end of the page, try and see how to do it with having your merging form tag below the main edit article form tag. So basically you can find where the big form tag ends and put it below it.
Try to see if you can figure it out, and if not, don't hesitate to ask :)
Btw. I think everybody had some problem with this

Is that possible to add destroy action into _form.html.erb instead of index.html.erb

If I do scaffold, it automatically makes destroy action in index.html.erb.
What if I want to move this action to _form.html.erb?
And I'd like to make it appear only during edit mode(example.com/books/1212/edit)
not during new mode(example.com/books/new)
I think this would help you
model
class Job < ActiveRecord::Base
attr_accessible :delete_flag #with other attributes
attr_accessor :delete_flag
end
*in your view (_form)*
<%= form_for(#job) do |f| %>
#other code
<%= f.text_field :delete_flag%>
<div class="actions">
<%= f.submit %>
<%= f.submit "Delete",{:class => "delete_button"} %>
</div>
<% end %>
coffeescript
jQuery ->
$('#new_job').submit ->
#capture your delete button click
$('#job_delete_flag').val("1")
in your controller you will get params as :delete_flag, and from there you could
Parameters: {"utf8"=>"✓", "authenticity_token"=>"5oWQU+w0jVCQlw8wLvCyKajbBSKpK2sv6RMkSGTE2H8=", "job"=>{"delete_flag"=>"1"}, "commit"=>"Delete"}
HTH
This is possible, but make sure when you click on the delete button it does not file the form submit action.
You might consider using :remote => true for delete link and you could check if the record is new or existing by using:
<Object>.new_record?
Ex: if I have a model called Job
Job.first.new_record? #=> false
Job.new.new_record? #=> true
Ex: something like (not tested :D, just to give you an idea)
<%= form_for(#job) do |f| %>
#your form content
<%= f.submit %>
<%= (link_to 'Destroy', #job, :remote => true, method: :delete, data: { confirm: 'Are you sure?' }) unless #job.new_record? %>
<% end %>

Ruby on Rails new objects in partial identifier stops at 1

I have Article(s), that has_many ArticleAssets. Fairly simple. On the edit form for the Article I just want to add new article assets. I don't need to edit the current ones, so I created a partial like this:
<% f.fields_for :article_assets, article_asset do |builder| -%>
<div class="article_asset">
<%= builder.file_field :image %>
<%= builder.check_box :is_flagged, :class => "isFlagged" %> isFlagged
</div>
<% end -%>
No collection, because I only need one object at a time and need no data from the existing article assets. In the form of edit.erb I render the following:
<%= render :partial => 'article_asset', :locals => {:f => f}, :object => ArticleAsset.new %>
This makes one new article asset show up that I can add information to, all cool so far. Important is that this field gets the name-form of article[article_assets_attributes][0][is_flagged]. All good since this will also group the hidden field that always comes with a checkbox in rails to the rest of the fields. Then I have an "Add item" link that does this:
page.insert_html :bottom, :article_assets_fields, :partial => "article_asset", :locals => {:f => f}, :object => ArticleAsset.new
Clicking on this link gives a new field under the created one, as expected, with the name-form of the checkbox field of article[article_assets_attributes][1][is_flagged]. Incremented, that's perfect! Adding another one with the same link however, also gives the same form (also with the identifier of 1, duplicate), which makes submitting the form only have 2 items instead of 3. Does anyone know why this happens and what I can do to solve it?
Ruby on Rails 2.3.11
Nested form 2.3 fail. This one was the bane of my existence for some time, even having watched the railscast etc. Here's my how to:
1) this goes in article.rb
after_update :save_article_assets
def new_article_asset_attributes=(article_asset_attributes)
article_asset_attributes.each do |attributes|
article_assets.build(attributes)
end
end
def existing_article_asset_attributes=(article_asset_attributes)
article_assets.reject(&:new_record?).each do |article_asset|
attributes = article_asset_attributes[article_asset.id.to_s]
if attributes
article_asset.attributes = attributes
else
article_assets.delete(article_asset)
end
end
end
def save_article_assets
article_assets.each do |article_asset|
article_asset.save(false)
end
end
2) In a helper somewhere:
def add_article_asset_link(name)
button_to_function name, :class => "new_green_btn" do |page|
page.insert_html :bottom, :article_assets, :partial => "article_asset", :object => ArticleAsset.new()
end
end
def fields_for_article_asset(article_asset, &block)
prefix = article_asset.new_record? ? 'new' : 'existing'
fields_for("article[#{prefix}_article_asset_attributes][]", article_asset, &block)
end
3) in your partial:
<% fields_for_article_asset(article_asset) do |aa| %>
<tr class="article_asset">
<td><%= aa.text_field :foo %></td>
<td><%= link_to_function "remove", "$(this).up('.article_asset').remove()" %></td>
</tr>
<% end %>
4) in the _form:
<table>
<%= render :partial => "article_asset", :collection => #article.article_assets %>
</table>
<%= add_article_asset_link "Add asset" %>

Associated models in Rails?

In my rails application I have two models called Kases and Notes. They work in the same way comments do with blog posts, I.e. each Kase entry can have multiple notes attached to it.
I have got everything working, but for some reason I cannot get the destroy link to work for the Notes. I think I am overlooking something that is different with associated models to standard models.
Notes Controller
class NotesController < ApplicationController
# POST /notes
# POST /notes.xml
def create
#kase = Kase.find(params[:kase_id])
#note = #kase.notes.create!(params[:note])
respond_to do |format|
format.html { redirect_to #kase }
format.js
end
end
end
Kase Model
class Kase < ActiveRecord::Base
validates_presence_of :jobno
has_many :notes
Note Model
class Note < ActiveRecord::Base
belongs_to :kase
end
In the Kase show view I call a partial within /notes called _notes.html.erb:
Kase Show View
<div id="notes">
<h2>Notes</h2>
<%= render :partial => #kase.notes %>
<% form_for [#kase, Note.new] do |f| %>
<p>
<h3>Add a new note</h3>
<%= f.text_field :body %><%= f.submit "Add Note" %>
</p>
<% end %>
</div>
/notes/_note.html.erb
<% div_for note do %>
<div id="sub-notes">
<p>
<%= h(note.body) %><br />
<span style="font-size:smaller">Created <%= time_ago_in_words(note.created_at) %> ago on <%= note.created_at %></span>
</p>
<%= link_to "Remove Note", kase_path(#kase), :confirm => 'Are you sure?', :method => :delete, :class => 'important' %>
</div>
<% end %>
As you can see, I have a Remove Note destroy link, but that destroys the entire Kase the note is associated with. How do I make the destroy link remove only the note?
<%= link_to "Remove Note", kase_path(#kase), :confirm => 'Are you sure?', :method => :delete, :class => 'important' %>
Any help would, as always, be greatly appreciated!
Thanks,
Danny
<%= link_to "Remove Note", note_path(note), :confirm => 'Are you sure?', :method => :delete, :class => 'important' %>
you also will need the following entry in config/routes.rb (check if it already exists)
map.resources :notes
and check for following method in your NotesController
def destroy
#note = Note.find(params[:id])
#note.destroy
.... # some other code here
end
there's also another way of doing that if you don't have a NotesController and don't want to have it
You're calling the delete method on a kase -t hat's why it's deleting a kase. There's nothing in this link
<%= link_to "Remove Note", kase_path(#kase), :confirm => 'Are you sure?', :method => :delete, :class => 'important' %>
apart from the text that even mentions a note - so why would it delete a note? Try
<%= link_to "Remove Note", note_path(note), :confirm => 'Are you sure?', :method => :delete, :class => 'important' %>
This assumes you have the standard restful routes and actions set up.
As an additional point, you should never use link_to for non-get actions, because
google spiders and the like will
click on them. You might say 'they
can't because you need to be logged
in' and that's true but it's still
not a good idea.
if someone tries
to open the link in a new tab/window
it will break your site, or go to
the wrong page, since it will try to
open that url but with a get instead
of a delete.
generally, in web
design, links should take you
somewhere and buttons should 'do
stuff', ie make changes. A
destructive action like this
therefore belongs on a button not a
link.
Use button_to instead, which constructs a form to do that same thing.
http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M002420&name=button_to

Resources