首页 > 解决方案 > 如何将变量从控制器传递到 Rails 5.2 中的模型?

问题描述

我创建了一个控制器模型来导入 Excel 工作表,并在数据库中插入或更新记录。

它工作正常,我现在想添加一个插入/更新计数器来通知用户。我期望使用实例变量来做到这一点,但这不起作用:模型内部不知道变量。

您能帮我理解原因并找到解决方案吗?

这是控制器:

class ClassificationValuesImportsController < ApplicationController
  # Check for active session
  before_action :authenticate_user!

  def new
    @classification = Classification.find(params[:classification_id])
    @classification_values_import = ClassificationValuesImport.new
  end

  def create
    @update_counter = 0
    @insert_counter = 0
    @classification = Classification.find(params[:classification_id])
    @classification_values_import = ClassificationValuesImport.new(params[:classification_values_import])
    if @classification_values_import.file.nil?
      render :new, notice: t('FileNameCannotBeEmpty')
    end

    if @classification_values_import.save
      redirect_to @classification, notice: "#{t('ImportedObjects')}: #{@insert_counter} inserted, #{@update_counter} updated"
    else
      @classification_values_import.errors.full_messages.each do |msg|
        puts msg
        @classification.errors[:base] << msg
      end
      render :new
    end
  end


end

这是模型:

class ClassificationValuesImport
  include ActiveModel::Model
  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  attr_accessor :file, :parent_id

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

  def persisted?
    false
  end

  def save
    if imported_values_lists.map(&:valid?).all?
      imported_values_lists.each(&:save!)
      puts "///////////////////// Count of linked values_lists /////////////////////"
      print "Updates : "
      puts @update_counter
      print "Insert : "
      puts @insert_counter
      true
    else
      imported_values_lists.each_with_index do |column, index|
        column.errors.full_messages.each do |message|
          errors.add :base, "Row #{index+2}: #{message}"
        end
      end
      false
    end
  end

  def imported_values_lists
    @imported_values_lists ||= load_imported_values_lists
  end

  def load_imported_values_lists

    # Read input file
    spreadsheet = self.open_spreadsheet(file)
    puts spreadsheet.sheets
    puts spreadsheet.sheets[1]
    spreadsheet.default_sheet = spreadsheet.sheets.last
    header = spreadsheet.row(1)

    # Get the list of values lists to upload
    @classification = Classification.find(parent_id)
    playground_id = @classification.playground_id
    values_lists = Array.new
    @classification.values_lists_classifications.order(:sort_order).each do |link|
      values_lists[link.sort_order] = link.values_list_id
    end
    header = spreadsheet.row(1)

    # Upload or update values for each values list
    (2..spreadsheet.last_row).map do |i|
      # Read column indexes
      code = header.index("Code") +1
      parent = header.index("Parent") +1
      level = header.index("Level") +1
      valid_from = header.index("Valid from") +1
      valid_to = header.index("Valid to") +1
      name = header.index("Name_en") +1

      if record = Value.find_by(values_list_id: values_lists[spreadsheet.cell(i,level).to_i], code: spreadsheet.cell(i,code))
        @update_counter += 1
      else record = Value.new(playground_id: playground_id, values_list_id: values_lists[spreadsheet.cell(i,level).to_i], code: spreadsheet.cell(i,code))
        @insert_counter += 1
      end
      #record.active_from = spreadsheet.cell(i,'Valid from')
      #record.active_to = spreadsheet.cell(i,'Valid to')
      record.name = spreadsheet.cell(i,name)
      record.description = 'HCL upload test'
      record.created_at = record.created_at || Time.now
      record.updated_at = record.updated_at || Time.now
      #record.anything = { 'Comment' => 'This is a test', 'Author' => 'Fred'}.to_json
      ### create first translation for current_user if data is available
      #record.name_translations.build(field_name: 'name', language: 'en', translation: record.name) unless record.name.blank?
      #record.description_translations.build(field_name: 'description', language: language, translation: record.description) unless record.description.blank?
      puts "test"
      puts record.attributes
      record
    end
  end

  def open_spreadsheet(file)
    case File.extname(file.original_filename)
    when ".csv" then Roo::CSV.new(file.path, csv_options: {col_sep: ";"})
#    when ".xls" then Roo::Excel.new(file.path, nil, :ignore)
    when ".xlsx" then Roo::Excelx.new(file.path)
    else raise "Unknown file type: #{file.original_filename}"
    end
  end
end

标签: ruby-on-rails

解决方案


您可以在模型中定义瞬态属性:

attr_accessor :update_counter, :insert_counter

...他们设置和访问来自控制器和模型的值,作为常规属性:

@classification.update_counter = 0
@classification.insert_counter = 0

但是,您应该小心,因为这种方法会使调试值的来源变得更加困难。

我相信最好的办法是将整个逻辑移动到模型中,处理update_counterinsert_counter作为实例变量,然后为它们公开一个公共 getter。在这种情况下,您可以使用attr_reader :update_counter, :insert_counter而不是。attr_accessor然后,您可以使用@classification.update_counter来获取控制器中的值。

如果模型需要来自控制器的任何信息,您可以将它们作为参数传递给save方法,并根据需要将它们向前传递。


推荐阅读