0

我正在使用 words_import 类/控制器来验证我的单词,因为我从 csv 文件或 excel 电子表格导入它们。不幸的是,我收到一个错误:

undefined method 'fetch_value' for nil:NilClass

当我尝试加载 words_import#new 页面时。如果可以的话请帮忙!

importer/app/controllers/word_imports_controller.rb

class WordImportsController < ApplicationController
    def new
    @word_import = WordImport.new
  end

  def create
    @word_import = WordImport.new(params[:word_import])
    if @word_import.save
      redirect_to root_url, notice: "Imported words successfully."
    else
      render :new
    end
  end

end

importer/app/models/word_import.rb

class WordImport < ApplicationRecord
    attr_accessor :file

  def initialize(attributes = {})
    attributes.each { |name, value| send("#{name}=", value) }
  end

  def persisted?
    false
  end

  def save
    if imported_words.map(&:valid?).all?
      imported_words.each(&:save!)
      true
    else
      imported_words.each_with_index do |word, index|
        word.errors.full_messages.each do |message|
          errors.add :base, "Row #{index+2}: #{message}"
        end
      end
      false
    end
  end

  def imported_words
    @imported_words ||= load_imported_words
  end

  def load_imported_words
    spreadsheet = Roo::Spreadsheet.open(file.path)
    header = spreadsheet.row(1)
    (2..spreadsheet.last_row).each do |i|
      row = Hash[[header, spreadsheet.row(i)].transpose]
      word = Word.find_by(abbreviation: row["abbreviation"]) || Word.new
      word.attributes = row.to_hash
      word
    end    
  end

end

importer/app/views/product_imports/new.html.erb

<div class="container-fluid">
    <div class="jumbotron">     
        <h1>Import Words</h1>
        <p>A CSV or Excel file can be used to import records. The first row should be the column name. The following columns are allowed.</p>

        <ul>
          <% Word.columns.each do |column| %>
            <% if column.name.in? ["id", "name", "released_on", "price"] %>
              <li>
                <strong><%= column.name %></strong> 
                <%= column.type.to_s.titleize %> type
              </li>
            <% end %>
          <% end %>
        </ul>

        <%= form_for @word_import do |f| %>
          <% if @word_import.errors.any? %>
            <div id="error_explanation">
              <h2><%= pluralize(@word_import.errors.count, "error") %> prohibited this import from completing:</h2>
              <ul>
              <% @word_import.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
              <% end %>
              </ul>
            </div>
          <% end %>

          <div class="field">
            <%= f.file_field :file %>
          </div>
          <div class="buttons"><%= f.submit "Import" %></div>
        <% end %>
    </div>
</div>

importer/config/routes.rb

Rails.application.routes.draw do
  root 'words#home'
  resources :words, except: [:show] do 
      collection do
        post :import
      end
    end
  get 'results' => 'words#results'

  resources :session, only: [:create, :new, :destroy]

  resources :word_imports, only: [:create, :new]
end

importer/app/controllers/words_controller

class WordsController < ApplicationController
    include ApplicationHelper

    before_action :authorize, except: [:index, :home, :results]

    def index
        @words = Word.order(:abbreviation)
        respond_to do |format|
          format.html
          format.csv { send_data @words.to_csv }
          format.xls 
        end
        render :index
    end

    def create
        @word = Word.new(abbreviation: params[:word][:abbreviation], full_word: params[:word][:full_word], definition: params[:word][:definition])

        if @word.save
            redirect_to results_path
        else
            @errors = @word.errors.full_messages
            render :new
        end
    end

    def new
        @word = Word.new(params[:word])

        render :new
    end

    def edit
        @word = Word.find(params[:id])

        render :edit
    end

    def update
        @word = Word.find(params[:id])
        @word.update(abbreviation: params[:word][:abbreviation], full_word: params[:word][:full_word], definition: params[:word][:definition])

        if @word.save
            redirect_to root_path
        else
            @errors = @word.errors.full_messages
            render :edit
        end
    end

    def destroy
        @word = Word.find(params[:id])

        @word.destroy

        redirect_to root_path
    end

    def home
        render :home
    end

    def results
        @words = Word.search(params[:term])
        render :results
    end

    def import
      Word.import(params[:file])

      redirect_to root_url, notice: 'Words successfully added.'
    end

    private

    def word_params
        params.require(:word).permit(:abbreviation, :full_word, :definition, :term)
    end

end

importer/app/models/word

require 'csv'
class Word < ApplicationRecord
    include ApplicationHelper

    validates :abbreviation, presence: true, length: {maximum: 5}
    validates :full_word, presence: true, uniqueness: true
    validates :definition, presence: true

    def self.search(term)
        if term
            where('abbreviation iLIKE ?', "%#{term}%").order('full_word DESC')
        else
            all 
        end
    end

    def self.to_csv(options = {})
      desired_columns = ["abbreviation", "full_word", "definition"]
      CSV.generate(options) do |csv|
        csv << desired_columns
        all.each do |word|
          csv << word.attributes.values_at(*desired_columns)
        end
      end
    end

    def self.import(file)
  spreadsheet = Roo::Spreadsheet.open(file.path)
  header = spreadsheet.row(1)
  (2..spreadsheet.last_row).each do |i|
    row = Hash[[header, spreadsheet.row(i)].transpose]
    word = find_by(id: row["id"]) || new
    word.attributes = row.to_hash
    word.save!
  end
end

end
4

1 回答 1

0

在包含您的课程的文件中需要“roo”

于 2017-06-29T05:34:07.017 回答