对于Rails web应用,涉及到最频繁的操作就是增、删、查、改操作,因此为了满足用户体验,批量删除(或更新)操作是必不可少。

在 Rails自动生成的模板中,每个控制器(Controller)都包含7个基本Action,即index、show、new、create、 edit、update、destroy,每个Action都只能处理一个对象,而批量操作必然要求能够处理多个对象,为此需要自定义一个批量操作的 Action,下面就介绍如何在Rails中实现批量删除功能。

首先,客户要求批量删除功能的效果,如下图所示:

 

 

上图首先以表格的方式列出来库表(departs)里的所以记录(表格每一行,就代表库表的一条记录),然后每一行的第一列设置了一个单选框,用于标记是否选中此行,最后通过  “删除选中”链接,即可完成批量删除被选中记录功能,而且还通过JS实现了选中全部记录的功能。

要实现上面的效果,本人采用的一个解决方案,步骤如下:

①、通过 JS文件实现,“全选”和“反选”功能,js代码如下:

/*全选*/

function checkall (s,k){

var a = document.getElementsByTagName('input');

var n = a.length;

for (var i=0; i<n; i++){

if((a[i].type == "checkbox") && ( a[i].name.substr(0,k-1)==s )){

a[i].checked = true;

}

}

}

 

/*反选*/

function uncheck (s,k){

var a = document.getElementsByTagName('input');

var n = a.length;

for (var i=0; i<n; i++){

if((a[i].type == "checkbox") &&  (a[i].name.substr(0,k-1)==s) ){

if(a[i].checked == true){

a[i].checked = false;

}

else{ a[i].checked = true; }

}

}

}

function doall (s,k,n ){

if( k ){  checkall (s,n ) }

else{ uncheck (s,n ) }

}

而在视图中首先是设计每一行的checkbox的id,然后在最后一个代表“全选”的checkbox中调用上面代码的doall 方法,由参数的不同实现,全选和反选。代码片段如下:每一行的checkbox:

<td width="5%" align='left' height='30' valign='middle' >

<%= check_box_tag  ' depart_ '   + depart.id .to_s ,'yes',false %>

</td>

 

生成的html代码就是如下格式:

<input  id ="depart_ 7198 "  type ="checkbox "  value ="yes "  name ="depart_7198 " />

 

最后代表“全选”的checkbox:

<input id="all" type="checkbox" onclick="doall ( '  depart '  ,this.checked,7 );" /> 全选

 

其中,根据需求要选中的是所有 id 类似于"depart_ 7198 "的 checkbox,但是后面的 7198是 变化的为此应将所有id含有 depart_ 的 checkbox都选择,那么就需要进行配备即JS中的 a[i].name.substr(0,k-1)==s)7 就是字符串“ depart_ ”的长度

 

②、实现 “删除选中”链接的功能。

通 过分析Rails模板自带的删除(destroy)Action,以及视图中实现“删除”链接的代码:<%= link_to '删除 ', depart, :confirm => '是否确定?', :method => :delete %>,可知其实使用了一个 变量 :method,用它来表示 Http的DELETE动作。因为浏览器不支持DELETE动作,所以,Rails 会生成一些javascript 来解决这个问题:例如下面的代码

 

link_to '  删除 '  ,depart,:confirm => '  是否确定? '  ,   :method => :delete

 

会自动生成相应的javascript代码,如下:
<a href="/departs/1"
onclick="var f = document.createElement( '  form '  );
f.style.display = '  none '  ; this.parentNode.appendChild(f);
f.method =  '  POST '  ; f.action = this.href;
var m = document.createElement( '  input '  );
m.setAttribute( '  type '  ,  '  hidden '  );
m.setAttribute( '  name '  , '  _method '  );
m.setAttribute( '  value '  , ’delete’);

f.appendChild(m);f.submit(); return false;">删除</a>
这段javascript代码会生成一个form,把 Http 的DELETE动作放在隐藏变量里传递给服务器,然后,Rails 会判断这个变量,决定是否去调用Controller的destroy 方法。

 

那么仿照这段javascript代码,并加以适当处理,使得其中的form,包含多个记录,再设置链接的地址,使其能提交给事先在Controller里自定义好的批量删除方法:destroy_selected,那么就能够实现批量删除的功能。

由 于这部分代码是使用 link_to 这个Helper 方法来构造一个链接,那么我们要实现的批量删除链接,就应该是重载这个link_to方法,但是 重载link_to方法可能影响到其它链接的生成,为此需要自定义一个Helper方 法:destroy_selected (action_name,paramsname,obj_id,url),即在 application_helper.rb中添加该方法,代码如下:

 

def destroy_selected  ( action_name,paramsname,obj_id,url )

"<a onclick=\"if (confirm('是否确定?')){

var f = document.createElement('form');

f.style.display = 'none';

this.parentNode.appendChild(f);

f.method = 'POST';

var m = document.createElement('input');

m.setAttribute('type', 'hidden');

m.setAttribute('name', '_method');

m.setAttribute('value', '#{action_name}');

f.appendChild(m);

var s = document.createElement('input');

s.setAttribute('type', 'hidden');

s.setAttribute('name', ' authenticity_token ');

s.setAttribute('value', authenticity_token);

f.appendChild(s);

var a = document.getElementsByTagName('input');

var n = a.length;

ii = 0

for (var i=0;i<n;i++){

if((a[i].type == 'checkbox' )&& (a[i].checked == true ) &&

(a[i].id != 'all')){

var s = document.createElement('input');

s.setAttribute('type', 'hidden');

s.setAttribute('name', '#{paramsname}');

s.setAttribute('value', a[i].name.slice(#{obj_id}) );

f.appendChild(s);

ii = ii + 1

}

}

if (ii > 0){ f.action = this.href;  f.submit(); }

};

return false;\"  href=\"" + url_for(url).to_s+"\"  >删除选中</a> "

end

上面的代码主要处理了4个事件,即粗体部分:下面简要分析

1)、 m.setAttribute('value', '#{action_name}');  根据传入的参数确

定链接要提交的目标action.

2)、 s.setAttribute('name', ' authenticity_token '); 由于Rails的安

全机制,需要为该方法传入 authenticity_token。

3)、 s.setAttribute('name', '#{paramsname}');

s.setAttribute('value', a[i].name.slice(#{obj_id}) );

为form添加被选择的需要删除的记录,然后以参数形式,传给action。

4)、 href=\"" + url_for(url).to_s+"\", 设置链接地址。

 

根据上面的方法,在视图中的 “删除选中”链接代码就是 :

<%=destroy_selected ('destroy_selected ','depart_id[] ',7 , [:destroy_selected,:departs]) %>

 

 

接下来就是,DepartsController里的destroy_selected方法了:

def destroy_selected

respond_to do |format|

if not params[:depart_id] .nil?

begin

Depart.transaction do

 params[:depart_id].each do |did|

if did != ""

@depart = Departs.find(did)

@depart.destroy

end

end

end

flash[:notice] = "删除数据成功!"

rescue

flash[:notice] = "删除数据失败!"

end

else

flash[:notice] = '请选择要删除的部门!'

end

format.html { redirect_to(departs_url) }

format.xml  { head :ok }

end

end

 

 

首 先,根据前面的设计,“删除选择”链接提交时会通过form传出参数(数组):depart_id[],然后destroy_selected方法遍历 depart_id[],再通过@depart = Departs.find(id);@depart.destroy 来逐个删除;为了保证数据安全 和同步,代码中还使用了数据库的事务机制:Depart.transaction do,这样只要其中一条记录删除失败,那么所有记录都不会被删除。

 

最后,还要在路由中配置这个action的url ,即:

map.resources :departs,:collection => { :destroy_selected => :post}

 

这样,只要为数据库的每一张表对应的Controller都设计一个action——destroy_selected,那么就可以轻松地实现资源的批量删除工作。而其它批量操作(批量更新、排序....)都可以仿照上面的方法,加以实现。