Skip to content

Allowing admins to import data from Excel and CSV files

Valentin Ballestrino edited this page May 12, 2017 · 4 revisions

Para components allow you to import Excel or CSV files into your system.

The only thing that you need to write, is the logic to import a single row, the rest (the file input, the controller action, the file parsing and iteration) is handled by Para.

The following steps allow you to make a complete importer feature in a CRUD component. If you're in a Form or custom component, you'll need an extra step to create the link to your importer, which is described at the bottom of this tutorial.

  1. Generate the importer structure
  2. Configure your component
  3. Write the importing logic
  4. Error handling
  5. Managing multiple importers for a given component

For the demonstration, we'll suppose that we want to allow importing a users XLS file in our system.

1. Generate the importer structure

First, we generate de importer file structure with the provided generator :

rails generate para:importer users

This will generate a file at : app/importers/users_importer.rb

2. Configure component

Let's say we have a :users component, we configure our config/components.rb file to handle importing files with our importer :

component :users, :crud, importers: ['UsersImporter']

We can now go to our users component, and an import file field will be present at the top of the table.

Note : our importer and component both have the users prefix in their name, but this is not mandatory since we can configure any importer in our component.

3. Write the importing logic

Now, we go to the app/importers/users_importer.rb file, which contains an import_from_row method that we must define. When the importer is run, the file headers are stripped and each row is passed to that method.

Here's how you would import an excel file with 3 columns, email, first_name, last_name :

class UsersImporter < Para::Importer::Base
  def import_from_row(row)
    email, first_name, last_name = row

    User.where(email: email).first_or_create! do |user|
      user.first_name = first_name
      user.last_name = last_name
    end
  end
end

4. Error handling

When you create each row, you can use the bang version of ActiveRecord::Base saving methods (save!, create!, first_or_create!) to let errors raise and the importer handle the rest.

The importer will be able to complete even when there are errors, and errors will be displayed in the flash message on page reloading.

5. Managing multiple importers for a given component

You can add several importers to the same component. This will let you handle multiple data sources differently, without having to handle the different cases in a single importer.

Since the :importers component option is an array, just add the other importers to that array, like :

component :users, :crud, importers: ['UsersImporter', 'GuestsImporter']

This will transform the import button to be a dropdown.
Each link in the dropdown will have the name of the importer. You can edit this name with active model translations. Here, we would add the following keys :

en:
  activemodel:
    models:
      users_importer: "Users"
      guests_importer: "Guests list"

Linking to the importer from your component

CRUD components automatically show importers for a given component available as a button or a dropdown in the main table view of the component. For other cases, where you want to create a link to access the import form, you'll need to add the link manually to your views.

The URL to access the desired import form can be accessed through the component#path method, like the following : component.path(namespace: :import, action: :new, importer: 'YourImporterClassName').

Since importers are run in background and to allow the UI to work as needed, you'll need to add some specific attributes to your link to allow Para's async job tracker interface to work : the [data-remote] and [data-job-tracker-button] attributes.

For a basic link with an upload icon and a button appearance, the could would look like :

= link_to @component.path(namespace: :import, action: :new, importer: 'UsersImporter'), class: 'btn btn-lg btn-primary', remote: true, data: { :'job-tracker-button' => true } do
  = fa_icon 'upload'
  Import data
Clone this wiki locally