You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Rails 5.2控制器向模型传递变量的问题与解决方案

问题:导入Excel时,模型无法访问控制器的实例变量,如何实现插入/更新计数?

我明白你现在的困扰:想统计Excel导入时插入和更新的记录数,原本在控制器里定义了@update_counter@insert_counter,但模型里根本读不到这些变量,导致最终显示的计数都是0。咱们来一步步解决这个问题。

为什么模型访问不到控制器的实例变量?

核心原因是:控制器实例和模型实例是完全独立的对象,它们的实例变量空间互不共享。你在控制器里定义的@update_counter是属于ClassificationValuesImportsController这个实例的,而模型ClassificationValuesImport是另一个全新的实例,它根本不知道控制器里的变量存在。你在模型里写的@update_counter其实是模型自己的实例变量,和控制器的那个完全没关系,所以控制器里的数值永远不会被更新。

解决方案:让模型自己维护计数器,控制器读取模型的属性

我们可以给模型添加计数器属性,让模型自己负责统计插入和更新的数量,之后控制器直接从模型实例里读取这些值就行。具体修改如下:

1. 修改模型,添加计数器属性并维护数值

首先给ClassificationValuesImport模型添加计数器的attr_accessor,并在初始化时设为0,同时在导入逻辑中更新这些值:

class ClassificationValuesImport
  include ActiveModel::Model
  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations
  # 新增update_counter和insert_counter的属性访问器
  attr_accessor :file, :parent_id, :update_counter, :insert_counter

  def initialize(attributes = {})
    attributes.each { |name, value| send("#{name}=", value) }
    # 初始化计数器为0
    self.update_counter = 0
    self.insert_counter = 0
  end

  # ... 其他原有方法不变

  def load_imported_values_lists
    # ... 原有代码不变
    (2..spreadsheet.last_row).map do |i|
      # ... 原有代码不变
      if record = Value.find_by(values_list_id: values_lists[spreadsheet.cell(i,level).to_i], code: spreadsheet.cell(i,code))
        # 使用self.访问模型自己的计数器属性
        self.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))
        self.insert_counter += 1
      end
      # ... 原有代码不变
    end
  end
end

2. 修改控制器,读取模型的计数器值

现在控制器不需要自己定义计数器变量了,直接在save成功后读取模型实例的update_counterinsert_counter属性即可,同时还要修正一个小问题:原来的文件为空判断后没有终止后续代码,容易引发错误,加上return

class ClassificationValuesImportsController < ApplicationController
  before_action :authenticate_user!

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

  def create
    @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')
      return # 终止后续代码执行,避免错误
    end
    
    if @classification_values_import.save
      # 直接从模型实例读取计数
      redirect_to @classification, notice: "#{t('ImportedObjects')}: #{@classification_values_import.insert_counter} inserted, #{@classification_values_import.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

验证效果

现在你再执行导入操作,模型里的puts语句会打印正确的计数,控制器跳转时的notice也会显示实际插入和更新的记录数了。

内容的提问来源于stack exchange,提问作者user1185081

火山引擎 最新活动