Grails - удаление элемента из списка ассоциаций hasMany при привязке данных? - PullRequest
14 голосов
/ 28 апреля 2010

Grails предлагает возможность автоматического создания и привязки доменных объектов к списку hasMany, как описано в руководстве пользователя grails .

Так, например, если в моем доменном объекте «Автор» есть Список множества объектов «Книга», я мог бы создать и связать их, используя следующую разметку (из руководства пользователя):

<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 удалить одну из этих книг из ассоциации 'books' по связыванию данных?

Самый очевидный способ сделать это - опустить элемент формы, соответствующий экземпляру домена, который вы хотите удалить; к сожалению, это не работает, согласно руководству пользователя:

Тогда Grails автоматически создаст новый экземпляр для вас в определенном позиция. Если вы "пропустили" несколько элементы посередине ... Тогда Грааль автоматически создаст экземпляры в между ними.

Я понимаю, что конкретное решение может быть спроектировано как часть командного объекта или как часть конкретного контроллера, однако необходимость в этой функциональности неоднократно появляется во всем приложении, в нескольких объектах домена и для ассоциаций многих различных типы объектов. Поэтому общее решение было бы идеальным. Кто-нибудь знает, есть ли что-то подобное в Grails?

Ответы [ 6 ]

12 голосов
/ 15 декабря 2010

Просто столкнулся с этим вопросом сам. Это легко решить. 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 ()» на тот случай, если вы не хотите «очистить ()» все данные.

11 голосов
/ 20 июня 2012

removeFrom *

В противоположность методу addTo тем, что он удаляет экземпляры из ассоциации.

Примеры

def author = Author.findByName("Stephen King")

def book = author.books.find { it.title = 'The Stand' }

author.removeFromBooks(book)
4 голосов
/ 23 июня 2011

Чтобы получить подробное описание удаления коллекции дочерних объектов с помощью GORM, ознакомьтесь с разделом «Удаление детей» этого сообщения в блоге - GORM получил часть 2

Рекомендуется прочитать, как и части 1 и 3 серии.

0 голосов
/ 26 мая 2012
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     
 }

Я добавил свойство cascade на стороне-владельце отношений, и теперь, если вы попытаетесь сохранить заглушку, она позаботится об удалении удаленных элементов из коллекции и удалении их из базы данных.

0 голосов
/ 22 июня 2011

Я просто сталкиваюсь с этой же проблемой.

Домен моего приложения довольно прост: у него есть объекты-заглушки, которые имеют отношение hasMany с объектами заголовка. Поскольку объекты Header не имеют собственной жизни, они полностью управляются контроллером-заглушкой и представлениями.

Определения класса домена:

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

class Header {
String value
static belongsTo = Stub     
}

Я попробовал метод «clear and bind», но в результате «очищенные» объекты остались в базе данных, а grails просто создадут новые экземпляры для тех, которые не были удалены из отношения. Похоже, что он работает с точки зрения пользователя, но он оставит много мусорных объектов в базе данных.

Код в методе update () контроллера:

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

Пример: при редактировании -мани стороны этого отношения у меня (для данной заглушки с 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 экземпляра заголовка:

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 ...

Кроме того, без очистки списка, если индекс отсутствует в представленном списке request.headers, в гралях привязки данных будет сохраняться существующий объект в этом месте без изменений.

Решение, которое приходит мне в голову, заключается в том, чтобы связать данные, затем проверить заголовки заглушки на наличие элементов, которых нет в представленном списке, и удалить их.

Это похоже на довольно простой сценарий, разве нет встроенных функций для его решения? Немного излишне писать собственную логику синхронизации для поддержания отношений, особенно когда причуды, которые делают его нетривиальным, вызваны самими граалями.

Как насчет удаления, не следует ли удалять элементы clear () из базы данных? Я что-то упускаю в определениях отношений или доменных объектов?

0 голосов
/ 01 мая 2010

Я только начинаю изучать Grails и вижу в вашем вопросе интересное исследование для меня. Я не думаю, что вы можете использовать обычный механизм привязки данных - так как он заполняет пробелы, используя какую-то Lazy карту за кулисами. Так что для достижения цели ваш метод сохранения ( или это функция? ) вряд ли будет содержать что-то вроде:

def Book = new Book(params)

Вам нужен механизм для изменения метода «сохранения» вашего контроллера.

После некоторых исследований я понимаю, что вы можете изменить свой шаблон скаффолдинга, который отвечает за генерацию кода вашего контроллера или методов времени выполнения. Вы можете получить копию всех шаблонов, используемых Grails, запустив " grails install-templates ", и файл шаблона, который вам нужно изменить, называется " Controller.groovy ".

Таким образом, теоретически, вы можете изменить метод "save" для всего вашего приложения таким образом.

Отлично! Вам может показаться, что все, что вам нужно сейчас сделать, - это изменить метод сохранения в шаблоне, чтобы он перебирал записи объектов (например, книги) на карте параметров, сохраняя и удаляя их по ходу работы.


Однако, я думаю, что ваше требуемое решение все еще может быть довольно проблематичным для достижения. Мой инстинкт подсказывает мне, что существует множество причин, по которым предлагаемый вами механизм является плохой идеей.

По одной причине, вне моей головы, представьте, что у вас был список страниц, разбитых на страницы. Может ли это означать, что ваше «сохранение» может удалить всю таблицу базы данных, кроме видимой в данный момент страницы? Хорошо, позвольте нам сказать, что вам удалось определить, сколько элементов отображается на каждой странице, что, если список был отсортирован так, что он больше не был в числовом порядке - что вы сейчас удаляете?

Может быть, лучше использовать несколько кнопок отправки в вашей форме (например, сохранить изменения, добавить, удалить). Я не пробовал подобные вещи в Grails, но понимаю, что actionSubmit должен помочь вам добиться нескольких кнопок отправки. Я, конечно, делал такие вещи в Struts!

НТН

...