问题
I am trying to upload an excel file to my Rails 5 app.
I have tried this tutorial, and this one, and this one. I can't get any of them to work.
I have a namespaced model called:
class Randd::Field < ApplicationRecord
require 'csv'
I have tried each of these methods in my model:
def self.import(file)
CSV.foreach(file.path, headers: true) do | row |
Randd::Field.create! row.to_hash
end
end
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
randd_field_hash = row.to_hash # exclude the price field
randd_field = Randd::Field.where(id: randd_field_hash["id"])
if randd_field.count == 1
randd_field.first.update_attributes(randd_field_hash)
else
Randd::Field.create!(product_hash)
end # end if !product.nil?
end # end CSV.foreach
end # end self.import(file)
In my controller, I have:
class Randd::FieldsController < ApplicationController
def index
@randd_fields = Randd::Field.all
end
def new
@field = Randd::Field.new
end
def import
Randd::Field.import(params[:file])
redirect_to root_path, notice: "Data imported"
end
end
Notes on the controller:
- I don't have any strong params - which seems odd to me but none of the tutorials seem to suggest they are required.
- I made the above new action because I tried to make a separate form to upload the file, instead of using the index action to keep the file filed. I have tried using the new as well as using the process outlined in the tutorials in which the form field is set out in the index view. Neither approach works.
In my routes I have:
namespace :randd do
resources :fields do
collection do
post :import
end
end
end
In my form, I have:
<%= simple_form_for [:randd, @field], multipart: true do |f| %>
<div class="form-inputs">
<%= f.file_field :file %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
When I try using the new view to upload the file, I get an error that says:
undefined method `randd_randd_fields_path' for #<#<Class:0x007fd5edf73738>:0x007fd5f698f430>
Did you mean? randd_fields_path
When I try using the index view to hold the form field, I have:
<%= form_tag import_randd_fields_path, multipart: true do |f| %>
<%= f.file_field :file %>
<%= submit_tag 'Import' %>
<% end %>
When I try using the index view to upload the file, I get an error that says:
undefined method `file_field' for nil:NilClass
Can anyone recommend another tutorial for how to upload an excel file to rails 5. It's possible that the ones I'm using are too old - but I can't find a better source of help.
NEXT ATTEMPT
I managed to get the form to render with the suggestion below:
<%= simple_form_for (@field), multipart: true do |f| %>
I had to define show and create actions in my fields controller in order to let this process run. I added to my randd fields controller so it now has:
class Randd::FieldsController < ApplicationController
def index
@randd_fields = Randd::Field.all
end
def new
@field = Randd::Field.new
end
def create
redirect_to action: "import"
end
def show
redirect_to action: "index"
end
def import
Randd::Field.import(params[:file])
redirect_to action: "index", notice: "Data imported"
end
end
When I go through the process, upload the file and submit, I can see this happening in the log:
Started GET "/images/upload/cache/presign?extension=.xls&_=142388" for ::1 at 2016-11-14 10:50:12 +1100
Started POST "/randd/fields" for ::1 at 2016-11-14 10:50:12 +1100
Processing by Randd::FieldsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8vJ7bA==", "randd_field"=>{"file"=>"{\"id\":\"04f4e0679f2a562.xls\",\"storage\":\"cache\",\"metadata\":{\"size\":165376,\"filename\":\"FOR codes.xls\",\"mime_type\":\"\"}}"}}
Redirected to http://localhost:3000/randd/fields/import
Completed 200 OK in 43ms (ActiveRecord: 0.0ms)
Started POST "/randd/fields" for ::1 at 2016-11-14 10:50:13 +1100
Processing by Randd::FieldsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8v+bbA==", "commit"=>"Create Field"}
Redirected to http://localhost:3000/randd/fields/import
Completed 302 Found in 40ms (ActiveRecord: 0.0ms)
Started GET "/randd/fields/import" for ::1 at 2016-11-14 10:50:13 +1100
Processing by Randd::FieldsController#show as HTML
Parameters: {"id"=>"import"}
Redirected to http://localhost:3000/randd/fields
Completed 302 Found in 29ms (ActiveRecord: 0.0ms)
Started GET "/randd/fields/import" for ::1 at 2016-11-14 10:50:13 +1100
Started GET "/randd/fields" for ::1 at 2016-11-14 10:50:13 +1100
Processing by Randd::FieldsController#show as HTML
Parameters: {"id"=>"import"}
Redirected to http://localhost:3000/randd/fields
Completed 302 Found in 32ms (ActiveRecord: 0.0ms)
Processing by Randd::FieldsController#index as HTML
Rendering randd/fields/index.html.erb within layouts/application
Randd::Field Load (1.6ms) SELECT "randd_fields".* FROM "randd_fields"
Rendered randd/fields/index.html.erb within layouts/application (46.9ms)
User Load (1.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 4], ["LIMIT", 1]]
Setting Load (1.0ms) SELECT "settings".* FROM "settings" WHERE "settings"."user_id" = $1 LIMIT $2 [["user_id", 4], ["LIMIT", 1]]
Rendered layouts/nav/_inner.html.erb (93.3ms)
Completed 200 OK in 2773ms (Views: 2732.5ms | ActiveRecord: 4.4ms)
Started GET "/randd/fields" for ::1 at 2016-11-14 10:50:16 +1100
Processing by Randd::FieldsController#index as HTML
Rendering randd/fields/index.html.erb within layouts/application
Randd::Field Load (0.9ms) SELECT "randd_fields".* FROM "randd_fields"
But -- this didn't work because the table is empty. It has no instances.
COURT3NAY'S SUGGESTION
The suggestion is that I swap the content of the import action in the fields controller to the create action. My controller now has:
class Randd::FieldsController < ApplicationController
def index
@randd_fields = Randd::Field.all
end
def new
@field = Randd::Field.new
end
def create
Randd::Field.import(params[:file])
redirect_to action: "index", notice: "Data imported"
# @field = Randd::Field.new(randd_field_params)
# redirect_to action: "import"
end
def show
redirect_to action: "index"
end
# def import
# # byebug
# Randd::Field.import(params[:file])
# redirect_to action: "index", notice: "Data imported"
# end
private
def randd_field_params
params.fetch(:randd_field, {}).permit(:title, :anz_reference)
end
end
My form now has (i removed the import url path):
<%= simple_form_for (@field), multipart: true do |f| %>
<%= f.error_notification %>
<div class="form-inputs" style="margin-bottom: 50px">
<div class="row">
<div class="col-md-12">
<div class="form_title">Research Field Codes</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%= f.file_field :file %>
</div>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-1" style="margin-top: 50px">
<div class="form-actions">
<%= f.button :submit %>
</div>
</div>
</div>
<% end %>
The error now says:
The action 'import' could not be found for Randd::FieldsController
The error message highlights a problem with code that I didn't write myself.
def process(action, *args)
@_action_name = action.to_s
unless action_name = _find_action_name(@_action_name)
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
end
I think its a problem though because I don't have an import action anymore. I'm supposed to have create to do the things that were in import previously (per the tutorials).
MY GUESS AT GETTING AROUND THIS:
I searched for where I was still asking for 'import'. I use it in two places, so I tried swapping 'import' for create in both.
Now, my model has:
def self.create(file)
CSV.foreach(file.path, headers: true) do |row|
randd_field_hash = row.to_hash # exclude the price field
randd_field = Randd::Field.where(id: randd_field_hash["id"])
if randd_field.count == 1
randd_field.first.update_attributes(randd_field_hash)
else
Randd::Field.create! row.to_hash#(randd_field_hash)
end # end if !product.nil?
end # end CSV.foreach
end # end self.import(file)
The create action in my controller has:
def create
Randd::Field.create(params[:file])
redirect_to action: "index", notice: "Data imported"
# @field = Randd::Field.new(randd_field_params)
# redirect_to action: "import"
end
This guess doesnt solve anything though. I still get an error message that says:
undefined method `path' for nil:NilClass
I also don't know what that means.
回答1:
You're really close. You need to post to import. You're just getting fields#show
Processing by Randd::FieldsController#show as HTML
Parameters: {"id"=>"import"}
You need to post to fields#import
<%= simple_form_for (@field), url: import_fields_path, multipart: true do |f| %>
or, just use fields#create instead of redirecting. Once you post to #create the redirect will lose the params.
来源:https://stackoverflow.com/questions/40579864/rails-5-how-to-upload-excel-files-tutorials-not-working-out