Using paperclip gem to add an attachment - ruby-on-rails

I've attempted to use the paperclip gem to add attachments to my contact model. It looks as though I can add an attachment - I can select the file without a problem. However when I then create the contact, the attachment is not saved. I know this because I can see in the rails console that the document_id is 'nil'.
In the _form.html.erb for creating a new contact, I have:
<%= simple_form_for(#contact, html: {class: "form-inline well", multipart: true}, role: "form") do |f| %>
<% if #contact.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#contact.errors.count, "error") %> prohibited this contact from being saved:</h2>
<ul>
<% #contact.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= f.input :name %>
<%= f.input :category %>
<%= f.input :area %>
<%= f.input :phone %>
<%= f.input :website %>
<%= f.input :email %>
<%= f.fields_for :document do |document_fields| %>
<%= document_fields.input :attachment, as: :file %>
<% end %>
<%= f.button :submit, class: "btn-success" %>
</div>
<% end %>
my db migration for this was:
class CreateDocuments < ActiveRecord::Migration
def change
create_table :documents do |t|
t.integer :user_id
t.timestamps
end
add_index :documents, :user_id
add_attachment :documents, :add_attachment
add_column :contacts, :document_id, :integer
end
end
My model for document.rb is:
class Document < ActiveRecord::Base
has_attached_file :attachment, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :attachment, :content_type => /\Aimage\/.*\Z/
end
and for contact.rb is:
class Contact < ActiveRecord::Base
belongs_to :user
belongs_to :document
accepts_nested_attributes_for :document
validates :name, presence: true,
length: { minimum: 2}
validates :user_id, presence: true
end
and my actions in the contacts_controller.rb are:
def create
#contact = current_user.contacts.new(contact_params)
respond_to do |format|
if #contact.save
format.html { redirect_to #contact, notice: 'Contact was successfully created.' }
format.json { render action: 'show', status: :created, location: #contact }
else
format.html { render action: 'new' }
format.json { render json: #contact.errors, status: :unprocessable_entity }
end
end
end
def contact_params
params.require(:contact).permit(:name, :email, :category, :area, :organisation, :website, :phone, :user_id, document_attributes: [:id, :attachment])
end
# PATCH/PUT /contacts/1
# PATCH/PUT /contacts/1.json
def update
#contact = current_user.contacts.find(params[:id])
if params[:contact] && params[:contact].has_key?(:user_id)
params[:contact].delete(:user_id)
end
respond_to do |format|
if #contact.update((contact_params))
format.html { redirect_to #contact, notice: 'Contact was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #contact.errors, status: :unprocessable_entity }
end
end
end
When I try to create a new contact with an attachment, here is my heroku log:
2014-03-10T23:41:49.594786+00:00 app[web.1]: Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"FGitLGkHzfw15yksCusGDvwdb//CgMyOMFh4SS4l63Y=",
"contact"=>{"name"=>"Oliver OT", "category"=>"OT", "area"=>"Rockdale",
"phone"=>"", "website"=>"", "email"=>"",
"document_attributes"=>{"attachment"=>#,
#original_filename="Screen Shot 2014-03-03 at 9.24.40 pm.png",
#content_type="image/png", #headers="Content-Disposition: form-data;
name=\"contact[document_attributes][attachment]\"; filename=\"Screen
Shot 2014-03-03 at 9.24.40 pm.png\"\r\nContent-Type:
image/png\r\n">}}, "commit"=>"Create Contact"}
2014-03-10T23:41:49.594786+00:00 app[web.1]: Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"FGitLGkHzfw15yksCusGDvwdb//CgMyOMFh4SS4l63Y=",
"contact"=>{"name"=>"Oliver OT", "category"=>"OT", "area"=>"Rockdale",
"phone"=>"", "website"=>"", "email"=>"",
"document_attributes"=>{"attachment"=>#,
#original_filename="Screen Shot 2014-03-03 at 9.24.40 pm.png",
#content_type="image/png", #headers="Content-Disposition: form-data;
name=\"contact[document_attributes][attachment]\"; filename=\"Screen
Shot 2014-03-03 at 9.24.40 pm.png\"\r\nContent-Type:
image/png\r\n">}}, "commit"=>"Create Contact"}
I'm not sure how get the attachment to save correctly?

Move the contact_params method outside of the update action so it can be accessed by the create action and change it to the following code.
def contact_params
params.require(:contact).permit(:name, :email, :category, :area, :organisation, :website, :phone, :user_id, document_attributes: [:id, :attachment])
end
The code above allows the id and attachment params for document_attributes.

Your migration has a problem:
add_attachment :documents, :add_attachment
This will mean your database table will have the attachment columns as add_attachment instead, thus causing Paperclip not to work
You'll need to change the columns in your db with a migration like this:
class Migration < ActiveRecord::Migration
def change
rename_column :documents, :add_attachment_file_name, :attachment_file_name
rename_column :documents, :add_attachment_content_type, :attachment_content_type
rename_column :documents, :add_attachment_file_size, :attachment_file_size
rename_column :documents, :add_attachment_updated_at, :attachment_updated_at
end
end

Related

Rails 5: multipart field inside a nested form doesn't work

I'm using the carrierwave gem in order to upload files. I have a form with one nested_form field from where I'm trying to upload multiple files. But for some odd reason the multipart=true doesn't work when I try to upload multiple files. I get this error: Validation failed: Attachments media files can't be blank and in the console I get an Unpermitted parameter: :media_files....even thought the params seem to pass when the form is submitted:
Started POST "/item/create" for 127.0.0.1 at 2018-10-23 13:08:19 +0300
Processing by ItemsController#create as HTML
Parameters: {"utf8"=>"✓","authenticity_token"=>
"tDpyPV9fPxLUHgRVb4G6P3eMLFUv0hKVnNPJCAcgYISCwjIbjA5bu/iXOy0k6yL
8+QWXs3MyoJ3lzzyhj3rqkw==", "item"=>{"title"=>"Sample", "description"
=>"Lorem", "attachments_attributes"=>{"0"=>{"media_files"=>[#
<ActionDispatch::Http::UploadedFile:0x007fe818a5ecb0 #tempfile=#
<Tempfile:/var/folders/hq/pr4rt14n7s31v3f6292wtjm00000gn/T/
RackMultipart20181023-1573-1r65sf9.jpg>, #original_filename=
"image1.jpg", #content_type="image/jpeg", #headers="Content-
Disposition: form-data; name=\"item[attachments_attributes]
[0][media_files][]\"; filename=\"image1.jpg\"\r\nContent-Type:
image/jpeg\r\n">, #<ActionDispatch::Http::UploadedFile
:0x007fe818a5ebe8 #tempfile=#
<Tempfile:/var/folders/hq/pr4rt14n7s31v3f6292wtjm00000gn
/T/RackMultipart20181023-1573-1lbr709.jpg>,
#original_filename="image2.jpg", #content_type=
"image/jpeg", #headers="Content-Disposition: form-data;
name=\"item[attachments_attributes][0][media_files][]\";
filename=\"image2.jpg\"\r\nContent-Type: image/jpeg\r\n">], "item_id"=>"#
<Item:0x007fe813ef65e8>"}}}, "commit"=>"Create"}
Unpermitted parameter: :media_files
This is nested_form setup I have:
create_table "attachments", force: :cascade do |t|
t.integer "item_id"
t.string "media_files"
t.string "content_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
class Item < ApplicationRecord
has_many :attachments, dependent: :destroy
accepts_nested_attributes_for :attachments, allow_destroy: true
end
class Attachment < ApplicationRecord
belongs_to :item
mount_uploader :media_files, AttachmentUploader
validates_presence_of :media_files
end
This is the attachments_controller.rb method:
def create
params["item"]["attachments_attributes"]["0"]["media_files"].each { |file| #attachment = Attachment.new(:media_files => file)}
respond_to do |format|
if #attachment.save
format.html { redirect_back fallback_location: root_path, notice: 'Attachment was successfully created.' }
format.json { render :'shows/show', status: :created, location: #attachment }
else
format.html { render :new }
format.json { render json: #attachment.errors, status: :unprocessable_entity }
end
end
end
and the nested params inside the items_controller.rb:
params.require(:item).permit(:title, :description, attachments_attributes: [:media_files, :item_id, :content_type])
The form with the nested field:
<%= form_for(#item, url: items_create_path, :html => { :multipart => true }) do |form| %>
<%= form.text_field :title, id: :item_title, autofocus: true, :class=>"form-control" %>
<%= form.text_area :description, id: :item_description, :class=>"form-control" %>
<%= form.fields_for :attachments do |f| %>
<div class="form-group">
<%= f.file_field :media_files, multiple: true, :id=>"upload-photo" %>
<% end %>
<%= form.submit "Create", :class=>"btn btn-default" %>
<% end %>
Any ideas on how to make this work?
Unpermitted parameter: :media_files
Means the parameter "media_files", which you use in the following line has not been permitted:
params["item"]["attachments_attributes"]["0"]["media_files"].each { |file| #attachment = Attachment.new(:media_files => file)}
This line itself is quite strange, as you're iterating on the parameters to instantiate a new Attachment each time oO
This line is almost OK, just change the attachments_attributes:
params.require(:item).permit(:title, :description, attachments_attributes: [:item_id, :content_type, :media_files => []])
But that's what you have to give to Item.new, as your form points to items_create_path.
Why do you expect it to reach attachments_controller.rb ?

Nested form attributes wont save

I am required to use nested forms on an assignment I am working on and I got stuck because my nested form attributes wont submit to database.
Here is what my controller looks like
def new
#booking = Booking.new
params[:no_of_passengers].to_i.times { #booking.passengers.build }
end
def create
#booking = Booking.new(booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to '/booking_confirmed', notice: 'Booking was successfully created.' }
format.json { render :show, status: :created, location: #booking }
else
format.html { render :new }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
private
def booking_params
params.permit(
:airline, :origin, :destination, :departure_date, :departure_time, :arrival_date,
:arrival_time, :flight_id, :price, :no_of_passengers, :user_id, :booking,
passenger_attributes: [
:id,:booking_id, :name, :email,:done,:_destroy
]
)
end
Here is the association between the models
class Booking < ActiveRecord::Base
has_many :passengers
accepts_nested_attributes_for :passengers, reject_if: lambda { |attributes| attributes['name'].blank? }
end
class Passenger < ActiveRecord::Base
belongs_to :bookings
end
And here is the form
<%= form_for #booking do |b| %>
<%= b.fields_for :passengers do |p| %>
<%= p.text_field :name, placeholder: "Passenger Name" %>
<%= p.text_field :email, placeholder: "Passenger Email" %>
<% end %>
<% end %>
I checked the passenger table using Passenger.all in rails console and it returns nothing.
What am I doing wrong?
After a pairing session with sunnyk, I was able to see the errors.
The first error was that my class Passenger has belongs_to :bookings instead of belongs_to :booking. This is a common error though. The Associations between these classes now looks like:
class Booking < ActiveRecord::Base
belongs_to :flight
has_many :passengers
accepts_nested_attributes_for :passengers, reject_if:
lambda {|attributes| attributes['name'].blank?}, :allow_destroy => true
end
class Passenger < ActiveRecord::Base
belongs_to :booking
end
class Flight < ActiveRecord::Base
has_many :bookings
has_many :passengers, through: :bookings
accepts_nested_attributes_for :passengers
accepts_nested_attributes_for :bookings
end
Next:
Instead of using the default value of no_of_passengers for building my nested form, I used the cocoon gem, which makes nested forms building and management easier. I also crated a new params method, in which I made the flight_id permitted, and then passed it as an argument for my booking instance in my new method alongside my current user. So now my new method looks like this.
def new
#booking = Booking.new(new_booking_params)
#booking.user = current_user if current_user
end
def new_booking_params
params.permit(:flight_id)
end
After that, I had to make another params method for my create method, so as to allow the parameters I want in the bookings table, this include the passengers_attributes. Now my create method looks like this.
def create
#booking = Booking.new(another_booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to '/booking_confirmed', notice: 'Booking was successfully created.' }
format.json { render :show, status: :created, location: #booking }
else
format.html { render :new }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
def another_booking_params
params.require(:booking).permit(:flight_id, :user_id, :no_of_passengers,
passengers_attributes:[:name, :email])
end
Lastly, I had to adjust my form to look like this.
<%= form_for(#booking, url: bookings_path) do |f| %>
<%= f.hidden_field(:flight_id)%>
<%= f.hidden_field(:user_id) %>
<%= f.hidden_field(:no_of_passengers)%>
<%= f.fields_for :passengers do |passenger| %>
<%= render 'passenger_fields', :f => passenger %>
<% end %>
<%= link_to_add_association 'Add Another passenger',f, :passengers, :class => 'btn btn-primary add' %>
<%= submit_tag "Book Now", class: "btn btn-primary book" %>
<% end %>
and passenger_fields partial looks like.
<div class="nested-fields form-inline">
<div class="form-group">
<%= f.text_field :name, :class => "form-control", placeholder: "Passenger Name" %>
</div>
<div class="form-group">
<label>-</label>
<%= f.text_field :email, :class => "form-control", placeholder: "Passenger Email" %>
</div>
<div class="links pull-right">
<%= link_to_remove_association "Delete", f, class: "btn btn-danger" %>
</div>
<hr>
</div>
All that did the trick. I hope this will help others to understand nested forms better

How validate <%= file_field_tag "images[]", type: :file, multiple: true %>

I have a two model
Gallery
class Gallery < ActiveRecord::Base
has_many :pictures, :dependent => :destroy
accepts_nested_attributes_for :pictures, :allow_destroy => true
attr_accessible :name, :description, :pictures_attributes
validates_presence_of :pictures
end
Picture
class Picture < ActiveRecord::Base
belongs_to :gallery
has_attached_file :image,
:path => ":rails_root/public/images/:id/:filename",
:url => "/images/:id/:filename"
end
And form
<%= form_for #gallery, :html => { :class => 'form-horizontal', multipart: true } do |f| %>
.........
<div class="controls">
<%= file_field_tag "images[]", type: :file, multiple: true %>
</div>
.........
<% end %>
Controller
def create
#gallery = Gallery.new(gallery_params)
respond_to do |format|
if #gallery.save
if params[:images]
params[:images].each { |image|
#gallery.pictures.create(image: image)
}
end
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render json: #gallery, status: :created, location: #gallery }
else
format.html { render action: "new" }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
private
def gallery_params
params.require(:gallery).permit(:description,
:name,
:pictures
)
end
If I want to validate has_many pictures image blank?, I got every time a error that Pictures is blank, even I selected images.
How I validate has_many association with multiple image field?
Params
Parameters: {"utf8"=>"✓", "authenticity_token"=>"MCkZFvsYvRndlfaqwXiw5MfhSHGoesawEAPFWzn0Ulw=", "gallery"=>{"name"=>"", "description"=>""}, "images"=>[#<ActionDispatch::Http::UploadedFile:0x007fb7ad9cb7d0 #tempfile=#<Tempfile:/var/folders/r4/jqx7vz8d48z2ky3ndpyzv9vc0000gn/T/RackMultipart20150926-38180-1xrchqg>, #original_filename="yamaha-blaster-BOARD-HEROa.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"images[]\"; filename=\"yamaha-blaster-BOARD-HEROa.jpg\"\r\nContent-Type: image/jpeg\r\n">], "commit"=>"Create Gallery"}
(0.1ms) begin transaction
(0.0ms) rollback transaction
Your problem lies in your use of accepts_nested_attributes_for - your model reference is to pictures whilst you're referring to images in your code.
Here's how you should do it:
#app/models/picture.rb
class Picture < ActiveRecord::Base
belongs_to :gallery
validates :image, presence: true
has_attached_file :image,
:path => ":rails_root/public/images/:id/:filename",
:url => "/images/:id/:filename"
end
#app/models/gallery.rb
class Gallery < ActiveRecord::Base
has_many :pictures
accepts_nested_attributes_for :pictures, allow_destroy: true
end
You're getting confused about the image object and the Picture model. IE you need to pass the images to the Picture model through your Gallery model:
#app/controllers/galleries_controller.rb
class GalleriesController < ApplicationController
def new
#gallery = Gallery.new
#gallery.pictures.build
end
def create
#gallery = Gallery.new gallery_params
#gallery.save
end
private
def gallery_params
params.require(:gallery).permit(:description, :name, pictures_attributes: [:images])
end
end
This means you're going to have to use f.fields_for, which can be tricky.
Here's how to do it:
#app/views/galleries/new.html.erb
<%= form_for #gallery do |f| %>
<%= f.text_field :name %>
<%= f.fields_for :pictures do |picture| %>
<%= picture.file_field type: :file, multiple: true %>
<% end %>
<%= f.submit %>
<% end %>
This should work for you. The multiple aspect of the upload, I am not sure about.

Simple_form multiple select field doesn't work

new rails user here. I'm trying to have my schedule form store an array of "days" but after several attempts I just can't make it work.
Here are my codes currently
*schedules/_form.html.erb:*
<%= simple_form_for #schedule do |f| %>
<% if #schedule.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#schedule.errors.count, "error") %> prohibited this schedule from being saved:</h2>
<ul>
<% #schedule.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.input :section_id do %>
<%= f.select :section_id, Section.all.map{|s| [s.seccon, s.id]}, :include_blank => true %>
<% end %>
<%= f.association :subject %>
<%= f.collection_select :day_ids, #days, :id, :name, {}, {:multiple => true, :size => 1} %>
<div class="field">
<%= f.label :start_time %>
<%= f.time_select :start_time %>
</div>
<%= f.input :professor do %>
<%= f.select :professor_id, Professor.all.map{|j| [j.procon, j.id]}, :include_blank => true %>
<% end %>
<%= f.association :room %>
<%= f.button :submit %>
<% end %>
*schedules_controller.rb:*
class SchedulesController < ApplicationController
before_action :set_schedule, only: [:show, :edit, :update, :destroy]
# GET /schedules
# GET /schedules.json
def index
#schedules = Schedule.all
#days = Day.all
end
# GET /schedules/1
# GET /schedules/1.json
def show
end
# GET /schedules/new
def new
#schedule = Schedule.new
#days = Day.all
end
# GET /schedules/1/edit
def edit
end
# POST /schedules
# POST /schedules.json
def create
#schedule = Schedule.new(schedule_params)
#days = Day.all
respond_to do |format|
if #schedule.save
format.html { redirect_to #schedule, notice: 'Schedule was successfully created.' }
format.json { render action: 'show', status: :created, location: #schedule }
else
format.html { render action: 'new' }
format.json { render json: #schedule.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /schedules/1
# PATCH/PUT /schedules/1.json
def update
respond_to do |format|
if #schedule.update(schedule_params)
format.html { redirect_to #schedule, notice: 'Schedule was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #schedule.errors, status: :unprocessable_entity }
end
end
end
# DELETE /schedules/1
# DELETE /schedules/1.json
def destroy
#schedule.destroy
respond_to do |format|
format.html { redirect_to schedules_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_schedule
#schedule = Schedule.find(params[:id])
#days = Day.all
end
# Never trust parameters from the scary internet, only allow the white list through.
def schedule_params
params.require(:schedule).permit(:section_id, :subject_id, :start_time, :finish_time_id, :professor_id, :room_id, :day_ids)
end
end
schedule.rb:
class Schedule < ActiveRecord::Base
belongs_to :section
belongs_to :subject
belongs_to :finish_time
has_and_belongs_to_many :days
accepts_nested_attributes_for :days, :allow_destroy => true
validates :section_id, :subject_id, :start_time, :professor_id, :room_id, :presence => true
belongs_to :professor
belongs_to :room
end
day.rb:
class Day < ActiveRecord::Base
has_and_belongs_to_many :schedules
default_scope { order(:id)}
has_paper_trail
validates :name, :desc, :presence => true
end
As was said here, the best thing to do is to create a has_many model relationship between Schedule and Day. You'll need a separate join table to make the relationship work. It will have: schedule_id and day_id as the two columns. You'd do this because you have a many > many relationship. There can be many schedules that belong to a day and many days that belong to a schedule.
I used this scenario in my app:
Recipe.rb
class Recipe < ActiveRecord::Base
has_and_belongs_to_many :wines
default_scope { order(:name) }
end
Wine.rb
class Wine < ActiveRecord::Base
has_and_belongs_to_many :recipes
accepts_nested_attributes_for :recipes, :allow_destroy => true
end
Migration
class AddRecipesWinesJoinTable < ActiveRecord::Migration
def self.up
create_table :recipes_wines, :id => false do |t|
t.column :recipe_id, :integer, :null => false
t.column :wine_id, :integer, :null => false
end
add_index :recipes_wines, [:wine_id]
end
def self.down
remove_index :recipes_wines, [:wine_id]
drop_table :recipes_wines
end
end
_wine_form.html.erb
# #recipes is Recipe.all generated by the controller
<%= w.collection_select :recipe_ids, #recipes, :id, :name, {}, {:multiple => true, :size => 6, :style => 'width:100%'} %>
Hope this helps.

Paperclip picture is not being uploaded?

I am currently using paperclip to upload images of items to my site. I set up my paperclip exactly as https://github.com/thoughtbot/paperclip. I uploaded pictures to my site but then nothing is saved to the database. There's also images found in my public folder.
in my config,
Paperclip.options[:command_path] = "/opt/local/bin/"
In my item.rb
class Item < ActiveRecord::Base
attr_accessible :photo_file_name, :photo_content_type, :photo_file_size,
:photo_updated_at
has_attached_file :photo
end
class AddPhotoColumnToItem < ActiveRecord::Migration
def self.up
add_column :items, :photo_file_name, :string
add_column :items, :photo_content_type, :string
add_column :items, :photo_file_size, :integer
add_column :items, :photo_updated_at, :datetime
end
...
end
items/_form.html.erb:
<%= form_for #item, :html => { :multipart => true } do |f| %>
<%= f.error_messages %>
<p>
<%= f.file_field :photo %>
</p>
<p><%= f.submit %></p>
<% end %>
and finally items_controller.rb (I am using cancan)
class ItemsController < ApplicationController
load_and_authorize_resource
def edit
end
def update
if #item.update_attributes(params[:item])
redirect_to #item, :notice => "Successfully updated item."
else
render :action => 'edit'
end
end
end
I checked with firebug that an image is indeed uploaded. I even puts params and I got this:
Parameters: {"utf8"=>"✓", authenticity_token"=>"XB0ptrmoxqRIakUB4YCBmbpYm2Pjex8KNm9g0pscpgo=",
"item"=>{"photo"=>#<ActionDispatch::Http::UploadedFile:0x007f9c5460ef80 #original_filename="lindsay-lohan.jpg",
#content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"item[photo]\"; filename=\"lindsay-lohan-boobs.jpg\"\r\nContent-Type: image/jpeg\r\n", #tempfile=#<File:/var/folders/14/gbn6ww_d1fs0lrcw22w50xdr0000gn/T/RackMultipart20110909-26576-auv9vl>>}, "commit"=>"Update Item", "id"=>"1"}
Anyone know why this is not working?
attr_accessible i think must be just attr_accessible :photo

Resources