Carrierwave file delete - ruby-on-rails

Again I need your help. Now I need to understand how I can delete with Carrierwave uploaded files (in my case - images).
models/attachment.rb :
class Attachment < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
attr_accessible :file, :file
mount_uploader :file, FileUploader
end
models/post.rb :
class Post < ActiveRecord::Base
attr_accessible :content, :title, :attachments_attributes, :_destroy
has_many :attachments, :as => :attachable
accepts_nested_attributes_for :attachments
end
*views/posts/_form.html.erb :*
<%= nested_form_for #post, :html=>{:multipart => true } do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div id="field">
<%= f.label :Nosaukums %>:<br /><br />
<%= f.text_field :title %><br /><br />
</div>
<div id="field">
<%= f.label :Raksts %>:<br /><br />
<%= f.text_area :content %><br /><br />
</div>
<%= f.fields_for :attachments do |attachment| %>
<% if attachment.object.new_record? %>
<%= attachment.file_field :file %>
<% else %>
<%= image_tag(attachment.object.file.url) %>
<%= f.check_box :_destroy %>
<% end %>
<% end %>
<%= f.submit "Publicēt", :id => "button-link" %>
<% end %>
When I am trying to delete previous uploaded file I have this error:
unknown attribute: _destroy
Maybe there is problem because I have multiple file uploads not only one.

None of this worked for me, but after digging I came across this post that really helped. Basically...
Form (where f is your form objects):
<%= f.check_box :remove_image %>
Then, if you check the box and submit the form you'll get the following error:
Can't mass-assign protected attributes: remove_image
Which is easily solved by simply adding remove_image to your attr_accessible list on the model. In the end it'll look something like:
class Background < ActiveRecord::Base
attr_accessible :image, :remove_image
belongs_to :user
mount_uploader :image, BackgroundUploader
end
In my case it's a background image that belongs to the user. Hope this helps :)

According to the docs, the checkbox should be called remove_file.

You're calling the method on the wrong model. Your file mount is on the Attachment.
The error is telling you what is wrong.
undefined method 'remove_file' for #<Post:0x471a320
The key point of the error is the method is being called on the Post model when it needs to be called on the Attachment model.
Maybe try scoping the input for the checkbox to the correct model.
<%= attachment.check_box :remove_file %>

It should be
<%= attachment.check_box :_destroy%>
It works for me

Related

Rails 4: replace / delete uploaded file in edit view form

A similar question has already been asked, but the answer is only for Rails 3, so I am taking the liberty of asking a new question.
I have a Rails 4 app, with the following models:
class User < ActiveRecord::Base
has_many :administrations
has_many :calendars, through: :administrations
end
class Calendar < ActiveRecord::Base
has_many :administrations
has_many :users, through: :administrations
has_many: :posts
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Post < ActiveRecord::Base
belongs_to :calendar
end
The Post model has the following attributes:
references :calendar, index: true, foreign_key: true
date :date
time :time
string :subject
string :format
text :copy
attachment :image
and the attachment was setup as follows with Paperclip in Post model:
has_attached_file :image, styles: { small: "64x64", med: "100x100", large: "200x200" }
validates_attachment :image, :content_type => { :content_type => "image/png" },
:size => { :in => 0..3000.kilobytes }
Adding an image to a Post is working fine, through the following form:
<%= form_for [#calendar, #calendar.posts.build] do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<p>
<%= f.label :date %><br>
<%= f.date_select :date %>
</p>
<p>
<%= f.label :time %><br>
<%= f.time_select :time %>
</p>
<p>
<%= f.label :subject %><br>
<%= f.text_field :subject %>
</p>
<p>
<%= f.label :format %><br>
<%= f.text_field :format %>
</p>
<p>
<%= f.label :copy %><br>
<%= f.text_area :copy %>
</p>
<p>
<%= f.label :image %><br>
<%= f.file_field :image %>
</p>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Viewing the image attached to a Post is also working well, thanks to this show view:
<h2>Post from <%= #post.date.strftime("%A, %d") %></h2>
<div>
<p><strong>Date</strong></p>
<%= #post.date.strftime("%A, %d") %>
</div>
<div>
<p><strong>Time</strong></p>
<%= #post.time.strftime("%I:%M %p") %>
</div>
<div>
<p><strong>Subject</strong></p>
<%= #post.subject %>
</div>
<div>
<p><strong>Format</strong></p>
<%= #post.format %>
</div>
<div>
<p><strong>Copy</strong></p>
<%= #post.copy %>
</div>
<div>
<p><strong>Image</strong></p>
<%= image_tag #post.image.url(:med) %>
</div>
However, when I go to the edit view, for a post where I have previously uploaded an image, I only see the field that allows me to add an image and a message saying "no file chosen".
The edit view uses (for now) the same form as the new view, as shown above.
How can I achieve the following:
Have the uploaded image appear in the edit page.
Allow users to delete this image.
Allow users to replace this image with a new one.
–––––
UPDATE: I found a way to solve item #1 above, by replacing
<p>
<%= f.label :image %><br>
<%= f.file_field :image %>
</p>
with
<p>
<%= f.label :image %><br>
<% if #post.image.exists? %>
<%= image_tag #post.image.url(:med) %>
<% else %>
<%= f.file_field :image %>
<% end %>
</p>
in my Post edit view.
I am still working on items #2 & #3 and could definitely use some help with these.
–––––
I am happy to share more code if necessary.
Any idea?
For #2 - Allow user to delete image, you might find this SO Issue helpful
OR, as per Paperclip's documentation you could simply create a dummy checkbox and, in the update method of your controller, look if the checkbox has been ticked and delete the attachment as mentioned in the documentation.
For #3 - Allow users to replace this image with a new one
Simply rework your code as follow:
<p>
<%= f.label :image %><br>
<% if #post.image.exists? %>
<%= image_tag #post.image.url(:med) %>
<% end %>
<%= f.file_field :image %>
</p>
This way, the file_field is always present (allowing user to replace or add their image)

rails 4 nested form fields_for are not displayed

I just started learning Rails 4.2. The problem is that one field in the form is not being displayed.
I have restaurant, category and a dish. While creating a dish, the category and restaurant will also be inputted via /dishes/new.
Expected behaviour: Dish, Category and Restaurant fields are displayed.
Actual behaviour: Only Dish and Category fields are displayed.
Here are my models
models/restaurant.rb
class Restaurant < ActiveRecord::Base
has_many :categories
has_many :dishes, :through => :categories
end
models/category.rb
class Category < ActiveRecord::Base
belongs_to :restaurant
has_many :dishes
end
models/dish.rb
class Dish < ActiveRecord::Base
belongs_to :category
validates :name, :price, :category, :restaurant, :presence => true
accepts_nested_attributes_for :restaurant, :category
end
dish controller
def new
# I think this is where
# I am making a mistake
#dish = Dish.new
category = #dish.build_category
restaurant = category.build_restaurant
end
def create
#dish = Dish.new(dish_params)
respond_to do |format|
if #dish.save
.... # default stuff #
end
end
end
# strong params
def dish_params
params.require(:dish).permit(:name, :description, :price, restaurant_attributes: [:name], category_attributes: [:name])
end
Dishes views/dishes/_form.html.erb
<%= form_for(#dish) do |f| %>
<% if #dish.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#dish.errors.count, "error") %> prohibited this dish from being saved:</h2>
<ul>
<% #dish.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :nameWoW %><br>
<%= f.text_area :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.number_field :price %>
</div>
*** The restaurant name field is not being displayed **
<%= f.fields_for :restaurant do |restaurant| %>
<div class="field">
<%= restaurant.label :Restname %><br>
<%= restaurant.text_area :name %>
</div>
<% end %>
<%= f.fields_for :category do |category| %>
<div class="field">
<%= category.label :Catname %><br>
<%= category.text_area :name %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I have followed steps from rails guide, browsed questions on stackoverflow and read some blog posts as well but havent been able to figure out whats wrong. Some micro level mistake is blocking me :( . Anyone knows whats wrong ?
Thanks in advance.
UPDATE:
Hey I found a solution.
def new
#dish = Dish.new
#dish.build_category
#dish.category.build_restaurant
end
This works well.But this is just a part of the actual solution. I had to do lot of /dish/create controller modification as well. I think the entire solution will have to be put in blog post. Otherwise it wont make any sense. I will soon be posting and updating it here.
You can add this in your dish.rb
class Dish
delegate :restaurant, to: :category
end
Or you can do
<%= f.fields_for :restaurant, #dish.category.restaurant do |restaurant| %>
<div class="field">
<%= restaurant.label :Restname %><br>
<%= restaurant.text_area :name %>
</div>
<% end %>
I think you are missing:
class Dish
belongs_to :restaurant, through: :category
end
You have it on the other side (many) but not there. You could test this by trying to output #dish.restaurant on your form (should be empty but not nil).
def new
# I think this is where
# I am making a mistake
#dish = Dish.new
category = #dish.category.build
restaurant = category.restuarant.build
end

accepts_nested_attributes_for issue in Form

Before asking this, I have already tried with the solution detailed here, but it didn't work.
context: 2 models, Idea and Project. 1 idea belongs to 1 project, 1 projects has many ideas.
I want to create a form to create ideas that have fields of ideas but also to specify which Project they belong to, by indicating the project_id field. I'm doing it with accepts_nested_attributes_for
Issue: I'm not being able to grab the project_id when creating a new idea from a form. From the console I see that a new idea has been saved, but project_id for that idea always returns nil
code:
ideas_controller.rb
# GET /ideas/new
def new
#idea = Idea.new
#idea.build_project
respond_to do |format|
format.html # new.html.erb
format.json { render json: #idea }
end
models> idea.rb
class Idea < ActiveRecord::Base
belongs_to :project
accepts_nested_attributes_for :project
mount_uploader :picture, PictureUploader
validates :name, presence: true, allow_blank: false
end
_form.html.erb
<%= form_for(#idea) do |f| %>
<% f.fields_for :project do |project_fields| %>
<% if #idea.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#idea.errors.count, "error") %> prohibited this idea from being saved: </h2>
<ul>
<% #idea.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :picture %><br>
<%= f.file_field :picture %>
</div>
<div class="field">
<%= f.label :project %><br>
<%= f.number_field :project_id, :class=>"Number" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
"Nested attributes allow you to save attributes on associated records through the parent", according to the documentation. you are doing it through the child, which I don't think is possible.
So either move the accepts_nested_attributes_for to the parent model and use the parent's form to create the parent fields along with the child field. so you'll be creating project and idea through the projects_controller and projects/_form.html.erb
or opt out for something else.
without using accepts_nested_attributes_for, add project_ids to your ideas params attributes as an empty array:
def idea_params
params.require(:idea).permit(:name, :project_ids => [])
end
app/views/ideas/_form.html.erb:
<div>
<%= idea.label 'File under at least one project' %>
<% Project.all.order(name: :asc).each do |project| %>
<div>
<%= check_box_tag "idea[project_ids][]", project.id %>
<%= project.name %>
</div>
</div>
this code will give you checkboxes of your projects to select. this means first you have to create the projects separely in project's own controller and form. so you are not using nesting attributes.

Nested attributes (Paperclip) validation

I'm working on a nested form where a user when creating a Screen should also attached an image (a separate model called Screenshot). I'm trying to validate the presence of the attachment before saving the new Screen to the db. I tried to validate the presence of the attachment in the Screenshot model, but that only precent Screenshot to be saved, while it still creates a Screen.
Here's my models:
class Screen < ActiveRecord::Base
belongs_to :project
has_many :screenshots
validates :name, presence: true
accepts_nested_attributes_for :screenshots
validates_presence_of :screenshots
end
class Screenshot < ActiveRecord::Base
belongs_to :screen
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end
here's my controller:
def create
#screen = Screen.create(screen_params)
if #screen.save
flash[:notice] = "A new screen has been added to this project"
redirect_to [#screen.project]
else
render :action => 'new'
end
end
And lastly here's my form:
<%= form_for ([#project, #screen]), :html => { :multipart => true } do |f| %>
<% if #screen.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#screen.errors.count, "error") %> prohibited this screen from being saved:</h2>
<ul>
<% #screen.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.hidden_field :project_id %>
</div>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :screenshots, #screen.screenshots.build do |s| %>
<%= s.hidden_field :screen_id, :value => #screen.id %>
<%= s.hidden_field :version, :value => "1" %>
<%= s.label :image %><br>
<%= s.file_field :image %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Any help to this noob is greatly appreciated.
Several things for you:
Form
You shouldn't build new objects in the form itself. That needs to happen in the controller's new function
<%= f.fields_for :screenshots do |s| %>
<%= s.hidden_field :screen_id, :value => #screen.id %>
<%= s.hidden_field :version, :value => "1" %>
<%= s.label :image %><br>
<%= s.file_field :image %>
<% end %>
You can declare them like this:
def new
#screen = Screen.new
#screen.screenshots.build
#any more build declarations go here
end
Verification
I believe you should put the verification into the screnshots model, as the nested parameters are passed directly to the corresponding model. I'm not sure on that though.
In the screenshots model, I would put the validates_presence_of :screenshots, with the name as your Paperclip attachment's name

Rails 4 Nested form not generating multiple

I am really new to nested forms in Rails, so I am sure I probably left something out here. I am following a Paperclip tutorial where an article has many assets (paperclip attachments)
The part I am stuck at is creating multiple file upload fields with the article controller.
You will see in the form partial that I have added the asset model near the bottom:
<%= form_for(#article) do |f| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#article.errors.count, "error") %> prohibited this article from being saved:</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %><br>
<%= f.text_area :content %>
</div>
<%= f.fields_for :asset do |asset| %>
<%= asset.file_field :image %>
<% end %>
<div class="actions">
<%= f.submit %>
<% end %>
And in the article controller, I added what I thought would build that portion of the form 5 times:
def new
#article = Article.new
5.times {#article.assets.build}
end
For good measure, here is the article and asset models:
article
class Article < ActiveRecord::Base
has_many :assets
end
assets
class Asset < ActiveRecord::Base
belongs_to :article
has_attached_file :image,
:style => {
:thumb => '150x150#',
:medium => '300x300>',
:large => '600x600>'
}
end
What am I missing?
In order to get multiple fields for :assets generated, you need to add accepts_nested_attributes_for :assets in your Article model.
# article.rb
class Article < ActiveRecord::Base
has_many :assets
accepts_nested_attributes_for :assets
end
Then in your view:
<%= f.fields_for :assets do |asset| %>
<%= asset.file_field :image %>
<% end %>
I think you need to specify the nested fields as plural (same as the association):
<%= f.fields_for :assets do |asset| %>
<%= asset.file_field :image %>
<% end %>

Resources