新手问题 如何一次更新多条记录的一个属性

sylan401 · 2013年06月24日 · 最后由 sylan401 回复于 2013年06月25日 · 3442 次阅读

我建了一个表 user_courses,已经存在一些记录,现在我想一次性更新多条记录中的一个属性,如下面的给每个学生打分,可以出来一个表格,用输入值来更新 grade 这个属性。 <%= form_tag do %> <% @user_courses.each do |user_course| %>

<%= user_course.course_name %> <%= user_course.user_name %> <% if user_course.grade %> <%= user_course.grade %> <% else %> <%= text_area_tag 'user_course', nil, :class => "input-medium search-query" %> <% end %> <% end %> <%=submit_tag "submit" , :class => "btn" %> <% end %> 备注:我可以试着用 data[] 数组来存储每次的 text 值,在控制器里面在依次读取出来赋值,但是我觉得这样可能会出错,例如排序之后。有没有更好的方法能够解决这个问题而又不出错。

user_course.update_attributes(hash)

#1 楼 @karmue 这个方法我知道,我说的是取出多个 text 中的数据,依次更新每条记录的属性,而保证不会出错。

你应该把 user_course.id 和对应的成绩当作 hash 的 key 和 value 用 post 方法传回 controller,然后用 hash key value 对应的特性来赋值,例如,在你的 controller 里

user_courses = UserCourse.find(hash.keys)
user_courses.each do |user_course|
  user_course.grade = hash[user_course.id]
  user_course.save
end

这样可以避免用顺序来赋值容易出现的错误,但是其他错误还是得靠测试和 validation 找出来

#3 楼 @karmue 我在页面里面使用了<%= text_area_tag 'hash[user_course.id]' %>,但是这样的话,hash 好像没有定义,到控制器里面也找不到,我试着在控制器里面定义 hash= Hash.new,好像也不对,因该是因为变量有限制范围吧。

我用 hash 只是举个例子,你传回到 controller 里面的 params hash 如果没有特别修改应该是 name 和 value 一一对应的,我的意思是让你用 id 做 name 来取 value

<input type='text' name=<%= user_course.id%> value=<%= user_course.grade %> /> 把这个加在你 form 中的@user_courses遍历里,这样你 controller 里面会接到相关的 params

#6 楼 @karmue mue 我是这样写的 <% @user_courses.each do |user_course| %>

<%= user_course.course_name %> <%= user_course.user_name %> <% if user_course.grade %> <%= user_course.grade %> <% else %> > <% end %> <% end %> 浏览器把 user_course.id 当成了一个字符串变量,能不能把这句代码写完整啊,我还是不太明白。怎样取出的数字赋值给 key。我是用的 params[] hash,错误提示 Couldn't find all UserCourses with IDs (utf8, authenticity_token, user_course.id, commit, controller, action) (found 0 results, but was looking for 6). 另外,还有个问题,我的复制上贴吧的代码为什么这么不好看,有什么地方可以设置吗?

#7 楼 @sylan401 不知道为什么这条没有了 >

#8 楼 @sylan401 textarea id="comment" name="user_course.id" value=<%= user_course.grade %>></textarea

#5 楼 @karmue 我按照你的思路做了 这是 create 的函数

def create
  data = Array.new
  params.keys().each do |key|
    if key.is_a?(Integer)
    else
      data[] = key
    end
    end
  user_courses = UserCourse.find(data)
  user_courses.each do |user_course|
    user_course.grade = params[user_course.id]
    user_course.save
  end
  redirect_to :action => "index", :controller => "select"
end

而 html 页面中,提取代码为 textarea id="comment" name="<%= user_course.id %>" value=<%= user_course.grade %>>"✓", "authenticity_token"=>"/9RSuxSpGdWxu2/PnCsxoK8CxtzUaXQWnIA/D5CsDbY=", "1"=>"85", "11"=>"58", "commit"=>"submit"}

@sylan401 或许可以考虑做一个 FormObject, 譬如 UserCoursesBatch, 由他负责表单的生成,验证和保存(甚至做事务处理),这样 Controller 会非常干净,至于 form_tag 的写法,可以看作 nested form 处理。

#11 楼 @everett 你的意思是不是用一个 button 按钮代替 submit,然后对该 button 写 javascript 函数,相当于吧 controller 中的函数放入 js 中来实现?

可以考虑这个函数 http://apidock.com/rails/ActiveRecord/Relation/update_all 只生成一条 SQL 语句

@greenmoon55 +1 update_all 是一个稳妥的方案。

#13 楼 @greenmoon55 我试了一下,用 update_all 不能将 id 去出来,只能一个一个的来取 id,如下面所示:

UserCourse.all.each do |user_course|
  if !user_course.grade
    user_course.update_attribute(:grade, params[user_course.id])
  end
end

但是这样有一个问题,params[user_course.id] 不能够取出值并赋给 grade,不知道是怎么回事?

@sylan401 我完全不是这个意思。我的建议是创建一个类专门负责这个表单的处理。主要理由是提供了表单验证和校正输入的可能,我觉得像成绩录入这类的操作,难免会遇到录入错误的问题,系统应该在做保存前对输入内容做验证。

class UserCoursesForm
  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  // user_coursesarrayhash
  attr_accessor :user_courses

  // 自定义验证规则
  validate :custom_validate
  // rails验证规则
  validates 

  // 构建规则
  def initialize(option)
    @user_courses = ...
  end

  def persisted?
    false
  end

  def submit(params)
    // mass assignment
    @user_courses = ...
    if valid?
       // 保存逻辑,例如
      @user_courses.each do |user_course|
          user_course.save!
      end
      true
    else
      false
    end
  end

  def custom_validate
    ...
  end
end

然后就可以通过 form_for 或 simple_form_for 做对象表单构建。 在 controller 中也可以做常规写法

// or update
def create 
  @user_courses_form = UserCoursesForm.new(...)
  if @user_courses_form.submit(params[:user_courses_form])
    // 其他处理
    ... 
    redirect_to ...
  else
    // or edit
    render "new" 
  end
end

@sylan401 https://github.com/apotonick/reform这是另外一种可能,不过我未曾用他做过批处理。

#16 楼 @everett 恩,我看一下这个 gem,自己也写一个这样的类,谢谢!!! 另外,针对 15 楼出现的 params[user_course.id],这个并不能将值取出,相反的 user_course.id 是一个整数,需要将其转化为字符串,应该这样写 params[user_course.id.to_s],不能加双引号。 问题已经解决,再次谢谢大家的帮助,thank you very much !!!

需要 登录 后方可回复, 如果你还没有账号请 注册新账号