I am trying to upload a csv file with Rails from Active Admin.
I have a model User, which has columns name(string) and age(integer).
My csv file looks like this:
name,age
"Peter",31
"Susan",30
"John",40
Then I have in my admin/user.rb:
ActiveAdmin.register User do
permit_params :name, :age
collection_action :upload_csv do
render "admin/csv/upload_csv_user"
end
collection_action :import_csv_data_user, :method => :post do
ret_val = CsvDb.update_model_from_csv("user", params[:dump][:file])
redirect_to :action => :index, :notice => ret_val
end
end
And in my admin/csv/upload_csv_user.html.erb I have:
<%= form_for(:dump, :url=>{:action=>"import_csv_data_user"}, :html => { :multipart => true }) do |f| %>
<%= f.file_field :file %>
<%= f.submit "Submit", confirm: "Are You Sure?" %>
<% end %>
Inside a csv_db.rb file I have:
require 'csv'
module CsvDb
def self.update_model_from_csv(model_name, file)
csv_data = CSV.read(file.path)
columns = csv_data.shift
model = model_name.classify.constantize
ret_val = "Data updated successfully!"
begin
model.import columns, csv_data, on_duplicate_key_update: columns, validate: false
rescue Exception => e
ret_val = e
end
ret_val
end
end
When I try to upload the file I get the following error: Illegal quoting in line 1.
And below it's written:
part "\"name"
parse "\"name,age\""
parts ["\"name", "age\""]
unconverted nil
in_extended_col false
csv []
I checked lots of examples and I can't find the error. Maybe my csv file is formatted incorrectly.
require 'csv'
module CsvDb
def self.update_model_from_csv(model_name, file_params)
CSV.foreach(file_params.path, headers: true) do |row|
model = model_name.classify.constantize
begin
name = row["name"]
age = row["age"]
if model.create(name: name, age: age)
result = "Imported successfully!"
end
rescue Exception => e
result = e
end
end
result
end
end
Use this method in controller as: -
#pass model name and params[:file], which is csv uploaded from user end
CsvDb.update_model_from_csv(model_name, params[:file])
Related
I'm helping a friend with a ruby on rails site. I'm really new at ruby on rails and the developer that developed the site is long.
The app uses the activeadmin gem and currently there is a column in the documents section that supposedly exports the document in PDF format but is not working. Here is the code. From app/admin/documents.rb
ActiveAdmin.register Document do
index do
column :id
# This was buggy
column :user_name do |d|
d.user.nil? ? 'No User' : (link_to d.user.name, admin_user_path(d.user))
end
column :user_email do |d|
d.user.nil? ? 'No User' : d.user.email
end
column :price do |document|
document.price.nil? ? 'Gratis' : document.price
end
column :type_document
column :status
column :download do |document|
link_to 'Download PDF', document_path(document, format: :pdf)
end
default_actions
end
form do |f|
f.inputs "Edicion" do
f.semantic_errors
f.input :content
f.input :price
f.input :status, as: :select, collection: Document::STATUSES
f.actions
end
end
csv do
column :user_name
column :user_email
column :price
column('Tipo') { |document| document.type_document }
column :status
column(:content) { |document| strip_tags(document.content) }
end
end
I Installed the wicked_pdf gem, but I'm stuck here. I do not know how to proceed. I found this code to generate the PDF
def generate_pdf(document)
pdf = WickedPdf.new.pdf_from_string(
document.content,
encoding: 'UTF-8',
page_size: 'A4',
orientation: 'Portrait',
template: 'documents/documento.pdf.rb',
margin: { top: 30, # default 10 (mm)
bottom: 30,
left: 20,
right: 20
},
layout: 'layouts/pdf.html'
)
send_data(
pdf,
filename: "document_#{document.type_document}_#{document.user.name}.pdf",
disposition: 'attachment'
)
end
I'm not sure if that code is correct and also how can I link the Download PDF link to this function?
Any help would be greatly appreciated.
Thanks in advance
Carlos Sosa
You already have the download PDF link here:
link_to 'Download PDF', document_path(document, format: :pdf)
The generate_pdf method contents could be added to, or called from, that controller endpoint.
Assuming you already have a DocumentsController#show action, it could look something like this:
def show
#document = Document.find(params[:id])
respond_to do |format|
format.html # render existing view
format.pdf do
generate_pdf(#document)
end
end
end
private
def generate_pdf(document)
# same content you posted
end
I tried to export a csv file in my Rails app.
It works fine locally and if will return a csv file with data. However when push to production, it return a csv file which is empty.
Is there any possible reason about this problem?
View:
<%= link_to "Export Places", {controller: "admin/neighborhoods", action: "export_csv", format: "csv"}, 'data-no-turbolink' => true, :class => "button" %>
Route:
get 'admin_neighborhoods_export' => 'admin/neighborhoods#export_csv'
Controller:
def export_csv
#neighborhoods = Neighborhood.order(:created_at)
time = Time.now.strftime("%Y%m%d%H%M%S").to_s
respond_to do |format|
format.html
format.csv do
headers['Content-Disposition'] = "attachment; filename=\"places_export_#{time}.csv\""
headers['Content-Type'] ||= 'text/csv'
send_data(Neighborhood.to_csv(#neighborhoods), :type => "text/csv", :filename => "places_export_#{time}.csv")
end
end
end
Modal:
def self.to_csv(neighborhoods)
CSV.generate(headers: true) do |csv|
nbh_columns = []
nbh_columns.concat(column_names)
nbh_columns.concat(["maponics_neighborhood", "maponics_city", "maponics_state"])
csv << nbh_columns
neighborhoods.each do |neighborhood|
values = []
values.concat(neighborhood.attributes.values_at(*column_names))
if neighborhood.gid.present?
nbh_maponic = NeighborhoodBoundariesMaponics.find(neighborhood.gid)
values.concat([nbh_maponic.neighborhd, nbh_maponic.city, nbh_maponic.state])
else
values.concat(["", "", ""])
end
csv << values
end
end
end
Have found the reason.
There is a bad data in production db. So one error happens when I use 'find' to search a row in db.
NeighborhoodBoundariesMaponics.find(neighborhood.gid)
Now I change to use 'where' and it works.
Hello dear Programmers,
I'm trying to develop a web application with the ebook "Praxiswissen - Ruby on Rails". My problem is that I want to save Images through a form to my project directory. The database just saves the name of the pictures with the saving time:
def unique_and_proper_filename(filename)
Time.now.to_i.to_s + '_' + File.basename(filename)
end
My problem is that my pictures dont get saved after submitting my form. I dont get some exceptions, thats why I dont know where my issue is.
Controller:
class PostsController < ApplicationController
require 'will_paginate'
def new
#post = Post.new
end
# information about saving the picture
def create
#post = Post.new(params[:post].permit(:title, :description, :date, :image_file, :thumbnail_file))
# Form isn't correctly filled message
if !#post.valid?
flash.now[:notice] = "Bitte füllen Sie alle Felder aus und überprüfen Sie Ihre Angaben."
render(:action => :new)
# Files weren't saved message
elsif !#post.save_files
flash.now[:notice] = "Es trat ein Fehler beim Hochladen der Dateien auf."
render(:action => :new)
# Files saved correctly message
else
#post.save
flash[:notice] = "Dateien wurden hochgeladen und die Daten wurden gespeichert."
redirect_to(:action => :list)
end
end
# list action for listing my pictures
def list
#posts = Post.paginate(:page => params[:page], :order => "date DESC", :per_page => 15)
#post_pages = Post.paginate(:page => params[:page], :order => "date DESC", :per_page => 15)
end
end
HTML Form:
<h2>Neues Foto anlegen</h2>
<%= form_tag({:action => :create}, :multipart => true) %>
<h3>Bilddaten</h3>
<p>
Titel<br/>
<%= text_field(:post, :title) %>
</p>
<p>
Beschreibungen<br/>
<%= text_field(:post, :description) %>
</p>
<p>
Datum und Uhrzeit<br/>
<%= datetime_select(:post, :date, :order => [:day, :month, :year, :hour]) %>
</p>
<p>
<h3>Datei-Upload</h3>
<p>
Bilddatei:<br/>
<%= file_field(:post, :image_file) %>
</p>
<p>
Thumbnail:<br/>
<%= file_field(:post, :thumbnail_file) %>
</p>
<%= submit_tag("Speichern") %>
</p>
</form>
Model:
class Post < ActiveRecord::Base
validates_presence_of(:title, :description, :date, :image, :thumbnail)
I18n.enforce_available_locales = false
def image_file= (fileobj)
if fileobj.size > 0
#image_file = fileobj
self.image = unique_and_proper_filename(fileobj.original_filename)
end
end
def thumbnail_file= (fileobj)
if fileobj.size > 0
#thumbnail_file = fileobj
self.thumbnail = unique_and_proper_filename(fileobj.original_filename)
end
end
def save_files
# Bilddatei save
if !save_uploaded_file(#image_file, IMAGE_DIR, self.image)
return false
end
# Thumbnail save
if !save_uploaded_file(#thumbnail_file, THUMBNAIL_DIR, self.thumbnail)
return false
end
end
private
def unique_and_proper_filename(filename)
Time.now.to_i.to_s + "_" + File.basename(filename)
end
private
def save_uploaded_file(fileobj, filepath, filename)
# Complete Path
complete_path = Rails.root + "/public/" + filepath
# if neccessary, create directory
FileUtils.mkdir_p(complete_path) unless File.exists?(complete_path)
# save data
begin
f = File.open(complete_path + "/" + filename, "wb")
f.write(fileobj.read)
rescue
return false
ensure
f.close unless f.nil?
end
end
end
I'm only getting the message that there went something wrong with saving the files when i fill the form correctly but it should return a message that says that my file were saved.
I'm sorry for that massive length of my question but I really dont know where my issue is... If there's a need for more information or code, I will add it as fast as I can.
Thank you very much in advance!
Update 17/02/22:
Paperclip has since been deprecated, it is recommended you use Rails' own Active Storage.
Original Answer:
I'm sorry but I'll only be able to recommend what we use:
Paperclip
I appreciate you're using a tutorial, but I'd highly recommend using the Paperclip gem for this
This handles ALL the heavy lifting for you:
#GemFile
gem "paperclip", "~> 4.1.1"
Model
#app/models/post.rb
Class Post < ActiveRecord::Base
has_attached_file :image
end
#migration
add_attachment :posts, :image
Controller
#app/controllers/posts_controller.rb
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
end
private
def post_params
params.require(:post).permit(:image, :other, :params)
end
View
#app/views/posts/new.html.erb
<%= form_for #post do |f| %>
<%= f.file_field :image %>
<% end %>
I'm lucky to tell that I found my issue. My save_files method in my Post model doesn't returned true..
I post this answer because maybe someone could use this question as an answer for his own problem. Here's where I added my return true :
def save_files
# Bilddatei save
if !save_uploaded_file(#image_file, IMAGE_DIR, self.image)
return false
end
# Thumbnail save
if !save_uploaded_file(#thumbnail_file, THUMBNAIL_DIR, self.thumbnail)
return false
end
return true # <--------- Need to be added!
end
Try to add enctype= multipart/formdata in your form tag if you are using form to post your data
I'm following the tutorial http://www.funonrails.com/2012/01/csv-file-importexport-in-rails-3.html]for upload files in rails 3, because I need that my app's user could upload csv files but when I tried to save the file I get: uninitialized constant CustomersController::CSV message, before change my routes to get "customers/import" to post "customers/import" I had other error No route matches [POST] "/customers/import" what Im doing wrong? thanks in advance.
MY CONTROLLER:
class CustomersController < ApplicationController
def import
if request.post? && params[:file].present?
infile = params[:file].read
n, errs = 0, []
CSV.parse(infile) do |row|
n += 1
# SKIP: header i.e. first row OR blank row
next if n == 1 or row.join.blank?
# build_from_csv method will map customer attributes &
# build new customer record
customer = Customer.build_from_csv(row)
# Save upon valid
# otherwise collect error records to export
if customer.valid?
customer.save
else
errs << row
end
end
# Export Error file for later upload upon correction
if errs.any?
errFile ="errors_#{Date.today.strftime('%d%b%y')}.csv"
errs.insert(0, Customer.csv_header)
errCSV = CSV.generate do |csv|
errs.each {|row| csv << row}
end
send_data errCSV,
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=#{errFile}.csv"
else
flash[:notice] = I18n.t('customer.import.success')
redirect_to import_url #GET
end
end
end
end
MY MODEL:
class Customer < ActiveRecord::Base
scope :active, where(:active => true)
scope :latest, order('created_at desc')
def self.csv_header
"First Name,Last Name,Email,Phone,Mobile, Address, FAX, City".split(',')
end
def self.build_from_csv(row)
# find existing customer from email or create new
cust = find_or_initialize_by_email(row[2])
cust.attributes ={:first_name => row[0],
:last_name => row[1],
:email => row[3],
:phone => row[4],
:mobile => row[5],
:address => row[6],
:fax => row[7],
:city => row[8]}
return cust
end
def to_csv
[first_name, last_name, email, phone, mobile, address, fax, city]
end
end
*MY VIEW:
<h1>Subir Archivos</h1>
<%= form_tag('import', :multipart => true) do %>
<p>
File:<br />
<%= file_field_tag 'file' %><br />
</p>
<p>
<%= submit_tag "subir" %>
</p>
<% end %>
MY ROUTES:
Pruebaupcsv::Application.routes.draw do
post "customers/import"
You need to add a require 'csv' before you use it, either in an initializer, or at the top of your controller.
I am relatively new to rails cannot get the following code to work. I am trying to upload a data file (Excel or csv), copy it to a temp folder and create a record in a Datafiles model which holds basic file information, such as filename, type, and date. Then I want to read the file and use the data to create or update records in several other models. If all goes well, move the file to a permanent location and write the new path in the Datafiles record.
Controller:
def new
#datafile = Datafile.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #datafile }
end
end
def create
#datafile = Datafile.new(params[:upload])
#datafile.save!
redirect_to datafile_path(#datafile), :notice => "Successfully imported datafile"
rescue => e
logger.error( 'Upload failed. ' + e.to_s )
flash[:error] = 'Upload failed. Please try again.'
render :action => 'new'
end
View:
<%= form_for #datafile, :html => {:multipart => true} do |f| %>
<p>
<%= f.label(:upload, "Select File:") %>
<%= f.file_field :upload %>
</p>
<p> <%= f.submit "Import" %> </p>
<% end %>
Model:
require 'spreadsheet'
class Datafile < ActiveRecord::Base
attr_accessor :upload
attr_accessible :upload
before_create :upload_file
def upload_file
begin
File.open(Rails.root.join('uploads/temp', upload.original_filename), 'wb') do |file|
file.write(upload.read)
self.filename = upload.original_filename
Spreadsheet.client_encoding = 'UTF-8'
#book = Spreadsheet.open(file.path)
self.import
end
rescue => e
#upload_path = Rails.root.join('uploads/temp', upload.original_filename)
File.delete(#upload_path) if File::exists?(#upload_path)
raise e
end
end
def import
case #book.worksheet(0).row(0)[0]
when "WIP Report - Inception to Date"
self.report_type = 'WIP'
puts 'report_type assigned'
self.import_wip
else
self.report_type = 'Unknown'
end
end
def import_wip
self.end_date = #book.worksheet(0).row(0)[3]
puts 'end_date assigned'
end
def formatted_end_date
end_date.strftime("%d %b, %Y")
end
end
However, it fails and the rails server window says
Started POST "/datafiles" for 127.0.0.1 at 2011-05-24 16:05:25 +0200
Processing by DatafilesController#create as HTML
Parameters: {"utf8"=>"✓", "datafile"=>{"upload"=>#<ActionDispatch::Http::UploadedFile:0xa0282d0 #original_filename="wip.xls", #content_type="application/vnd.ms-excel", #headers="Content-Disposition: form-data; name=\"datafile[upload]\"; filename=\"wip.xls\"\r\nContent-Type: application/vnd.ms-excel\r\n", #tempfile=#<File:/tmp/RackMultipart20110524-14236-1kcu3hm>>}, "commit"=>"Import"}
Upload failed. undefined method `original_filename' for nil:NilClass
Rendered datafiles/new.html.erb within layouts/application (54.5ms)
Completed 200 OK in 131ms (Views: 56.3ms | ActiveRecord: 0.0ms)
I have rspec model tests that pass and controller tests that fail to redirect after saving. I can post them if it would be useful.
I inserted the raise #datafile.to_yaml and got the following in the terminal.
ERROR RuntimeError: --- !ruby/object:Datafile
attributes:
filename:
report_type:
import_successful:
project:
begin_date:
end_date:
created_at:
updated_at:
attributes_cache: {}
changed_attributes: {}
destroyed: false
marked_for_destruction: false
persisted: false
previously_changed: {}
readonly: false
I notice that :upload is not listed. Can I set model instance variables from the form? :upload is an instance variable, not an attribute, because I do not want to keep the uploaded file in the database (just its path to the local directory). If I cannot set instance variables in the view's form, any suggestions? Does it make sense (in terms of MVC) to upload the file to a temp folder in the controller, then create a model record by passing it the temp file's path?
Hello I am pretty new to Rails and was strugling with this as well I found a solution though it probably isn't the best. It does work though.
in you model make a public function called import_upload
def import_upload( upload )
#uploaded_file = upload
end
now in your controller you can explicitly pass it. I don't know why this doesn't happen automatically if you make an attr_accsessor with the same name as the file_field but this was the solution that worked for me.
def new
foo = Foo.new( params[:foo] )
foo.import_upload( params[:foo][:uploaded_file] ) #This is were the majic happens
#Do your saving stuff and call it a day
end