【问题标题】:Grails - Removing an item from a hasMany association List on data bind?Grails - 从数据绑定的hasMany关联列表中删除项目?
【发布时间】:2010-04-28 14:35:52
【问题描述】:

Grails 提供了自动创建域对象并将其绑定到 hasMany 列表的功能,如grails user guide 中所述。

因此,例如,如果我的域对象“作者”有一个包含许多“书”对象的列表,我可以使用以下标记(来自用户指南)创建和绑定这些对象:

<g:textField name="books[0].title" value="the Stand" /> 
<g:textField name="books[1].title" value="the Shining" /> 
<g:textField name="books[2].title" value="Red Madder" /> 

在这种情况下,如果指定的任何书籍尚不存在,Grails 将创建它们并适当地设置它们的标题。如果指定索引中已经有书籍,则它们的标题将被更新并被保存。我的问题是:是否有一些简单的方法可以告诉 Grails 从数据绑定的“书籍”关联中删除其中一本书?

最明显的方法是省略与您要删除的域实例对应的表单元素;不幸的是,根据用户指南,这不起作用:

然后Grails会自动创建 为您在定义的新实例 位置。如果你“跳过”了几个 中间的元素......然后是Grails 将自动创建实例 之间。

我意识到可以将特定的解决方案设计为命令对象的一部分,或者作为特定控制器的一部分——但是,在我的应用程序中、跨多个域对象和许多不同的关联时,对这个功能的需求反复出现对象的类型。因此,一个通用的解决方案将是理想的。有谁知道 Grails 中是否包含类似的东西?

【问题讨论】:

    标签: data-binding grails groovy dns associations


    【解决方案1】:

    removeFrom*

    与 addTo 方法相反,它从关联中删除实例。

    例子

    def author = Author.findByName("Stephen King")
    
    def book = author.books.find { it.title = 'The Stand' }
    
    author.removeFromBooks(book)
    

    【讨论】:

    • 这真的行不通。我最终得到一个错误,说“书”有一个验证错误,作者为空。
    【解决方案2】:

    我自己也遇到了这个问题。这很容易解决。 Grails 使用 java.util.Set 来表示列表。您可以使用clear() 方法擦除数据,然后添加您想要的数据。

    //clear all documents
    bidRequest.documents.clear()
    
    //add the selected ones back
    params.documentId.each() {
        def Document document = Document.get(it)
        bidRequest.documents.add(document)
        log.debug("in associateDocuments: added " + document)
    };
    
    //try to save the changes
    if (!bidRequest.save(flush: true)) {
        return error()
    } else {
        flash.message = "Successfully associated documents"
    }
    

    我敢打赌,在您不想“清除()”所有数据的情况下,您可以使用“remove()”方法来做同样的事情。

    【讨论】:

    • 我发现“bidRequest.documents = []”效果更好,因为文档可能为空。
    【解决方案3】:

    有关使用 GORM 删除子对象集合的详细说明,请查看这篇博文的“删除子对象”部分 - GORM gotchas part 2

    推荐阅读,该系列的第 1 部分和第 3 部分也是如此。

    【讨论】:

      【解决方案4】:

      我自己刚刚开始学习 Grails,并认为您的问题对我来说是一个有趣的研究练习。我不认为你可以使用传统的数据绑定机制——因为它在幕后使用某种惰性映射来填补空白。因此,为了实现您的目标,您的“保存”方法(还是一个函数?)不太可能包含以下内容:

      def Book = new Book(params)
      

      您需要一种机制来修改控制器的“保存”方法。

      经过一番研究,我了解到您可以修改负责生成控制器代码或运行时方法的脚手架模板。您可以通过运行“grails install-templates”获取 Grails 使用的所有模板的副本,您需要修改的模板文件称为“Controller.groovy” .

      所以理论上,您可以通过这种方式修改整个应用程序的“保存”方法。

      太棒了!您会认为您现在需要做的就是修改模板中的保存方法,以便它遍历参数映射中的对象条目(例如书籍),随时保存和删除。


      但是,我认为您需要的解决方案仍然很难实现。我的直觉告诉我,你建议的机制是一个坏主意的原因有很多。

      出于一个原因,在我的脑海中,想象一下你有一个分页的书单。这是否意味着您的“保存”可以删除除当前可见页面之外的整个数据库表?好的,假设您设法计算出每个页面上显示了多少项目,如果列表已排序,不再按数字顺序排列 - 您现在要删除什么?

      也许表单中的多个提交按钮会是更好的方法(例如,保存更改、添加、删除)。我没有在 Grails 中尝试过这种事情,但了解actionSubmit 应该可以帮助您实现多个提交按钮。我当然曾经在 Struts 中做过这种事情!

      HTH

      【讨论】:

        【解决方案5】:

        我也遇到了同样的问题。

        我的应用程序的域非常简单:它具有与 Header 对象具有 hasMany 关系的 Stub 对象。由于 Header 对象没有自己的生命,它们完全由 Stub 控制器和视图管理。

        领域类定义:

        class Stub {
        List headers = new ArrayList(); 
        static hasMany = [headers:Header]
        static mapping = {headers lazy: false}
        }
        
        class Header {
        String value
        static belongsTo = Stub     
        }
        

        我尝试了“清除和绑定”方法,但最终结果是“清除”对象留在数据库中,grails 只会为未从关系中删除的对象创建新实例。从用户的角度来看,它似乎确实有效,但它会在数据库中留下大量垃圾对象。

        控制器的update()方法中的代码是:

        stubInstance.headers.clear()
        stubInstance.properties = params
        

        一个例子:在编辑这种关系的 -many 方面时(对于给定的 id=1 存根):

        <g:textField name="headers[0].value" value="zero" id=1 />
        <g:textField name="headers[1].value" value="one" id=2 />
        <g:textField name="headers[2].value" value="two" id=3 />
        

        数据库中有3个Header实例:

        id=1;value="zero"
        id=2;value="one"
        id=3;value"two"
        

        删除标题“一”并保存存根对象后,数据库将具有标题:

        id=1;value="zero"
        id=2;value="one"
        id=3;value"two"
        id=4;value="zero"
        id=5;value="two"
        

        并且 Stub 对象现在将与 id=4 和 id=5 的 Headers 关联...

        此外,在不清除列表的情况下,如果提交的 request.headers 列表中不存在索引,则数据绑定 grails 将保持该位置的现有对象不变。

        我想到的解决方案是绑定数据,然后检查 Stub 的标头中是否存在提交列表中不存在的元素并将其删除。

        这看起来是一个非常简单的场景,难道没有任何内置功能可以解决它吗? 必须编写自己的同步逻辑来维护关系有点矫枉过正,尤其是当 grails 本身引起的怪癖时。

        那么删除呢,不应该从数据库中删除 clear()'ed 元素吗?我是否遗漏了关系或域对象定义中的某些内容?

        【讨论】:

        • 我最终在这里找到了所有问题的答案:omarello.com/2010/08/grails-one-to-many-dynamic-forms。简单地说:您向对象添加一个新属性以在视图中切换为“已删除”,然后在控制器中使用 deleted=true 绑定和删除所有对象。
        【解决方案6】:
        class Stub {
          List headers = new ArrayList(); 
          static hasMany = [headers:Header]
          static mapping = {
          headers lazy: false
          **headers cascade: "all-delete-orphan"**
           }
          }
        
        class Header {
            String value
            static belongsTo = Stub     
         }
        

        我已经在关系的拥有方添加了级联属性,现在如果您尝试保存存根,它将负责从集合中删除已删除的项目并从数据库中删除它们。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-15
          • 2015-03-14
          • 1970-01-01
          • 2013-08-02
          • 1970-01-01
          • 1970-01-01
          • 2011-11-17
          • 1970-01-01
          相关资源
          最近更新 更多