Yes, it is possible, and you don't need ActiveStorage. Rails's basic file upload facilities will be enough. Meanwhile, Administrate tries to follow Rails conventions, and provides hooks for you to alter the templates and refer to your own controllers and actions where you can implement this. Here's an example of how you could solve your problem.
First, you'll want to add a form with a file field. Admins will be able to use it to upload CSVs.
There are many places where you could put this form. For this example, let's say that it's "products" that you want to import (model Product
), and you want to put the link in the index page.
You can override Administrate's own templates with your own ones. The following command will generate a copy of the index
template that you can customize and Administrate will use instead of its own:
$ ./bin/rails g administrate:views:index
This will get you a new file app/views/admin/application/index.html.erb
.
Alter it to add a link to a separate page that will host the upload form:
<% if page.resource_name == "product" %>
<div class="link-to-upload"><%= link_to "CSV Import", upload_admin_products_path %></div>
<% end %>
You can put this in between the page title and the search bar. Or wherever you want, really. It does the following:
- The
if
checks that we are in the index page for "products" and not for something else.
- The
link_to
links to a page that we haven't created yet, which will host the upload form.
If you load the products index page now, it will show an error NameError
, with message undefined local variable or method 'upload_admin_products_path'
. This is because we still don't have a route for this new page of ours. Lets add it now.
In the routes file config/routes.rb
, you'll already have a route resources :products
. Change it to look like this:
resources :products do
collection do
get :upload, action: 'upload_form'
post :upload, action: 'upload_process'
end
end
This actually adds two routes, not one. One will be for the form page, while the other one will be for the action that will process the uploaded CSV.
For now, you can click on the link and go to the new page. It will break again. This time the error will be Unknown action - The action 'upload_form' could not be found for Admin::ProductsController
. That's because we haven't provided a view for this page yet. Let's do it now.
Add a view with the following contents at app/views/admin/products/upload_form.html.erb
:
<header class="main-content__header" role="banner">
<h1 class="main-content__page-title" id="page-title">
Import products from CSV
</h1>
</header>
<section class="main-content__body main-content__body">
<%= form_for :products, html: { class: "form" } do |f| %>
<p><%= f.file_field(:file) %></p>
<p><%= f.submit "Upload" %></p>
<% end %>
</section>
Now you should be able to load the page and see the form. I have added additional markup (like those header
and section
elements) to make it look similar to other pages that Administrate provides.
That form has a file_field
and a submit button. On submit, it will send the file to the current URL (/admin/products/upload
) as a POST request. It will be handled by the post
route we added earlier, on the action upload_process
. Let's write that now.
On the controller Admin::ProductsController
, add the following method to implement the action:
def upload_process
file = params[:products][:file]
data = CSV.parse(file.to_io, headers: true, encoding: 'utf8')
# Start code to handle CSV data
ActiveRecord::Base.transaction do
data.each do |row|
Product.create(row.to_h)
end
end
# End code to handle CSV data
redirect_to admin_products_url
end
This will create new products based on what's contained in the CSV, and then will redirect the user back to the products index page.
This example action is very simple and may not suit your needs exactly. You probably want to copy your code from seeds.rb
and paste it in between my Start
and End
comments, so that it does what you want exactly. There are many possibilities.