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_counter和insert_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




