锁定老帖子 主题:在rails中优雅的进行模型校验
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-09-07
在用rails进行开发时,最常见的操作的是前台提交表单,后台保存相关的模型对象,对于一个复杂的表单,可能需要保存的模型对象不止一个,但这些对象在保存之前都是要经过合法性检验的,请看如下的场景,一个表单提交了订单信息、用户基本资料、用户所在地,那么后台需要对用户、订单、地点这3个对象进行校验并做相关处理,通常会用到error_messages_for做错误信息输出, 具体有以下3种做法:
1
if @user.save && @order.save && @city.save #跳转到成功页面 else #返回原来页面 end
这里存在的问题是,加入前两个模型合法性校验通过,但最后一个模型出现问题,那么原本3个对象都不该被保存,但前两个对象已经被保存,所以存在严重的脏数据问题
2
if @user.valid? && @order.valid? && @city.valid? @user.save @order.save @city.save #跳转到成功页面 else #返回原页面 end
这个方法倒是不存在脏数据问题,但是如果第一个模型对象出现合法性问题,那么程序将停止之后的合法性校验,所以显示返回页面的错误提示将不完整,严重影响了系统的用户体验。
3 利用事务,基于第一种方法之上,如果任何一个模型合法性出现合法性问题,将采取数据库回滚操作,个人认为这种方法不仅复杂,性能也不高。
那么以下有个比较简洁的方法解决这样的问题
def new @users = User.new @city = City.new @order = Order.new end def create @city = City.new params[:city] @user = User.new params[:user] @user.city = @city @order = Order.new params[:order] @order.user = @user unless [@user, @city, @order].map(&:valid?).include?(false) @user.save @city.save @order.save redirect_to "/main/new" else render :action => "new" end end
关键在于这句:
在保存之前就遍历各个模型,并运行valid?方法,之后判断结果列表中是否包括false,以此作为判断合法性的依据,并且不会造成脏数据的问题。
相关的view如下所示,关于错误汉化这里不做讨论
<%= error_messages_for :user %> <%= error_messages_for :city %> <%= error_messages_for :order %> <% form_for :user, :url => "/main/create" do |f| %> <fieldset> <legend>用户信息</legend> <ol> <li> <%= f.label :name %> <%= f.text_field :name %> </li> </ol> </fieldset> <% fields_for :city do |city| %> <fieldset> <legend>地点信息</legend> <ol> <li> <%= city.label :code %> <%= city.text_field :code %> </li> </ol> </fieldset> <% end %> <% fields_for :order do |order| %> <fieldset> <legend>订单信息</legend> <ol> <li> <%= order.label :price %> <%= order.text_field :price %> </li> </ol> </fieldset> <% end %> <%= f.submit '提交' %> <% end %>
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-09-07
引用 3 利用事务,基于第一种方法之上,如果任何一个模型合法性出现合法性问题,将采取数据库回滚操作,个人认为这种方法不仅复杂,性能也不高。 复杂在哪里? 性能不高又在哪里? |
|
返回顶楼 | |
发表时间:2008-09-07
gigix 写道 引用
3 利用事务,基于第一种方法之上,如果任何一个模型合法性出现合法性问题,将采取数据库回滚操作,个人认为这种方法不仅复杂,性能也不高。
复杂在哪里? 性能不高又在哪里?
真快。
前段时间有人倒是问过这个问题, Rails所提供的ActiveRecord 对于复杂的数据库事务控制还是比较吃力,对于跨数据库的分布式事务支持也相对弱。
性能当然相对较低啦,本来不该存的东西你存了,然后你又去撤销他,本来就是多余的操作 |
|
返回顶楼 | |
发表时间:2008-09-07
liuqiang 写道 Rails所提供的ActiveRecord 对于复杂的数据库事务控制还是比较吃力,对于跨数据库的分布式事务支持也相对弱。
显然你这个例子不是这种情况。你需要的只是一个“全对或不做修改”的事务保障,一个filter就搞定了,你甚至都不需要在实现action的时候知道有这么一回事。 liuqiang 写道 性能当然相对较低啦,本来不该存的东西你存了,然后你又去撤销他,本来就是多余的操作
有趣的是我看到很多人在使用MySQL的时候根本就没有关闭它的autocommit功能,似乎他们也没有抱怨什么性能低之类的,你明白什么意思吗? |
|
返回顶楼 | |
发表时间:2008-09-07
gigix 写道
liuqiang 写道
Rails所提供的ActiveRecord 对于复杂的数据库事务控制还是比较吃力,对于跨数据库的分布式事务支持也相对弱。
显然你这个例子不是这种情况。你需要的只是一个“全对或不做修改”的事务保障,一个filter就搞定了,你甚至都不需要在实现action的时候知道有这么一回事。
恩,我明白了,我确实明白了,您老确实是用事务解决这个问题的。
性能:好吧,你总需要撤销对象吧,另外,假如你先前一个对象新建的时候涉及到图片保存操作,这算不算影响性能?您明白这个意思吧。
复杂性:还拿图片说事,你又需要删除图片吧,倒算不上是很复杂,但您和您的系统以及数据库做了很多不必要的事情,所以相对复杂了点。
总之,不必要做的事情为什么不去避免呢,不要把负担全部扔给数据库。建议您还是先看看我提出的方法,希望评批指正,多谢 |
|
返回顶楼 | |
发表时间:2008-09-07
liuqiang 写道 复杂性:还拿图片说事,你又需要删除图片吧,倒算不上是很复杂,但您和您的系统以及数据库做了很多不必要的事情,所以相对复杂了点。
总之,不必要做的事情为什么不去避免呢,不要把负担全部扔给数据库。建议您还是先看看我提出的方法,希望评批指正,多谢 你举的例子本来是没有图片的。我以为我们一直在讨论一个只与数据相关的“全对或不修改”的问题。 我不太欣赏“不必要做的事情就应该避免”这种态度──如果是对机器来说的话。这种态度应该用在人身上,至于机器,没有问题之前就让它这么着,有了问题再说。 如果把负担扔给数据库就意味着减少程序员的负担,经验告诉我这个生意通常值得做。 |
|
返回顶楼 | |
发表时间:2008-09-07
写道
你举的例子本来是没有图片的。我以为我们一直在讨论一个只与数据相关的“全对或不修改”的问题。
我不太欣赏“不必要做的事情就应该避免”这种态度──如果是对机器来说的话。这种态度应该用在人身上,至于机器,没有问题之前就让它这么着,有了问题再说。 如果把负担扔给数据库就意味着减少程序员的负担,经验告诉我这个生意通常值得做。
哈哈,举例子嘛,搞那么复杂干嘛,说真的,捣鼓这几种方法倒是牺牲了我一下午时间。
至于“全对或不修改”的问题,是您主动提出来的:)。 |
|
返回顶楼 | |
发表时间:2008-09-07
liuqiang 写道 “不必要做的事情就应该避免”,关键是我这种方法又不复杂,关键地方就一行代码,你就是指望机器,写的代码也比这个多,要不怎么号称是优雅呢,:)
20个action就是20行 |
|
返回顶楼 | |
发表时间:2008-09-07
gigix 写道
liuqiang 写道
“不必要做的事情就应该避免”,关键是我这种方法又不复杂,关键地方就一行代码,你就是指望机器,写的代码也比这个多,要不怎么号称是优雅呢,:)
20个action就是20行
呵呵,针对20个表单的过滤器,代码肯定不止20行,2者代码都不是很多,没必要斤斤计较啦,综合考虑,您再仔细琢磨琢磨,哪种方法比较爽 |
|
返回顶楼 | |
发表时间:2008-09-07
liuqiang 写道 呵呵,针对20个表单的过滤器,代码肯定不止20行,2者代码都不是很多,没必要斤斤计较啦,综合考虑,您再仔细琢磨琢磨,哪种方法比较爽
8行 class TransactionFilter def filter(controller) return yield if controller.request.get? ActiveRecord::Base.transaction do yield end end end 你大概还需要2~3行把它声明在application.rb里面 作为比较,我这个项目大概有150~200个action 重点在于: (1)重复 (2)不必要的思考 |
|
返回顶楼 | |