【问题标题】:Validating nested models?验证嵌套模型?
【发布时间】:2009-07-01 22:53:36
【问题描述】:

更具体地说,“我如何验证一个模型是否需要创建至少 x 个有效的关联模型?”。我一直在尝试验证以与父级相同的形式创建的嵌套模型(并最终通过 jQuery 显示即时验证)。作为一个流行的例子,让我们假设以下模型和架构。

class Project
  include DataMapper::Resource

  property :id,     Serial
  property :title,  String, :nullable => false

  has 2..n, :tasks
end

class Task
  include DataMapper::Resource

  property :id,         Serial
  property :project_id, Integer,  :key => true
  property :title,      String,   :nullable => false

  belongs_to :project
end

如您所见,所有验证都在模式定义中完成。这里重要的是“has 2..n, :tasks”。鉴于 params 哈希中的嵌套任务属性将产生 valid 任务,此验证实际上正常工作。但是,如果他们生成了一个 invalid 任务,那么该任务将不会被创建,您最终会得到一个少于 2 个任务的项目,因此是一个 invalid 项目对象。

据我了解,这是因为它无法确定任务属性是否有效,直到它尝试保存任务,并且因为 - 据我所知 -任务无法在项目之前保存,项目不知道任务是否有效。我的假设是否正确?

无论如何,我希望有一个快速的答案,但它似乎没有我希望的那么琐碎。如果您有任何建议,将不胜感激。

【问题讨论】:

    标签: validation model associations sinatra datamapper


    【解决方案1】:

    实际上,我在这里使用 DataMapper 中的事务找到了一个不错的解决方案。基本上,此事务尝试保存父对象以及所有子对象。一旦保存失败,事务就会停止并且不会创建任何内容。如果一切顺利,则对象将成功保存。

    class Project
      def make
        transaction do |trans|
          trans.rollback unless save
          tasks.each do |task|
            unless task.save
              trans.rollback
              break
            end
          end
        end
      end
    end
    

    这可确保在保存任何内容之前一切都是有效的。我只需要将我的#save 和#update 方法更改为我的控制器代码中的#make。

    【讨论】:

    • 你认为你可以用一个实际的例子来说明这一点吗?
    【解决方案2】:

    如果您的数据库引擎支持,SET CONSTRAINTS DEFERRED 可能会很有用。

    否则,可能会编写一个存储过程来进行插入,然后说它的存储过程的责任是确保只插入正确的、经过验证的数据。

    【讨论】:

    • 谢谢克里斯,这些都是很好的建议,虽然我不是在寻找特定于数据库的解决方案。我希望逻辑和验证保留在应用程序本身中。如果最坏的情况发生,我只会写一个保存后的钩子来验证记录(丑,我知道,但可能是唯一明智的方法)。
    【解决方案3】:

    有一个模型方法valid? 在保存模型对象之前对其进行验证。因此,验证关联的简单方法是使用 validates_with_block' or 'validates_with_method 检查关联的验证。

    看起来像这样

    validates_with_block do
      if @tasks.all?{|t|t.valid?}
        true
      else
        [false, "you have an invalid task"]
      end
    end
    

    或者您可以查看dm-association-validator 或dm-accepts-nested-attributes

    编辑: 更加疯狂。对任务运行验证,然后检查是否只有与关联相关的错误。

    validates_with_block do
      if @tasks.all?{|t|t.valid?;!t.errors.any?{|e|e[0]==:project}}
        true
      else
        [false, "you have an invalid task"]
      end
    end
    

    【讨论】:

    • 不幸的是,此时所有任务都将无效,因为它们尚未保存(具有 ID=>nil、project_id=>nil 等),因此验证将始终返回 false .这就是问题所在,因为父级和子级是同时以相同的形式创建的。
    • 我也尝试了 dm-accepts_nested_attributes,但没有成功。我在 IRC 中与 snusnu(插件作者)聊天,我们似乎无法弄清楚。这是一个已知问题,但似乎是一个灰色地带。
    • 你可以做一些比较老套的事情,检查任务的错误对象,看看它们是否只有与项目相关的错误。
    猜你喜欢
    • 2012-12-12
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    • 2011-07-05
    • 1970-01-01
    • 1970-01-01
    • 2015-12-28
    相关资源
    最近更新 更多