我正在使用 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