Grails - Удаление элемента из ассоциации hasMany Список привязки данных?
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?
Ответы
Ответ 1
Просто наткнулся на эту проблему сам. Это легко решить. 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()" в случае, если вы не хотите "очищать" все данные.
Ответ 2
removeFrom *
Напротив метода addTo, поскольку он удаляет экземпляры из ассоциации.
Примеры
def author = Author.findByName("Stephen King")
def book = author.books.find { it.title = 'The Stand' }
author.removeFromBooks(book)
Ответ 3
Для хорошего объяснения удаления коллекции дочерних объектов с помощью GORM см. раздел "Удаление детей" этого блога - GORM gotchas part 2
Рекомендуется чтение, как и части 1 и 3 серии.
Ответ 4
Я только начинаю изучать самого Grails и рассматриваю ваш вопрос как интересное исследование для меня. Я не думаю, что вы можете использовать обычный механизм привязки данных - поскольку он заполняет пробелы, используя какую-то Lazy карту за кулисами. Итак, для достижения своей цели ваш метод "сохранить" (или это функция?) Вряд ли будет содержать что-то вроде:
def Book = new Book(params)
Вам нужен механизм для изменения метода "сохранения" вашего контроллера.
После некоторых исследований я понимаю, что вы можете изменить шаблон своего леса, который отвечает за генерацию кода контроллера или методов выполнения. Вы можете получить копию всех шаблонов, используемых Grails, запустив " grails install-templates", а файл шаблона, который вам нужно изменить, называется " Controller.groovy.
Итак, теоретически вы можете изменить метод "save" для всего вашего приложения таким образом.
Отлично! Вы могли бы подумать, что все, что вам нужно сделать сейчас, это изменить ваш метод сохранения в шаблоне, чтобы он повторялся через записи объектов (например, книги) в карте params, сохраняя и удаляя по ходу.
Однако, я думаю, что ваше требуемое решение все еще может быть довольно проблематичным для достижения. Мой инстинкт говорит мне, что есть много причин, почему механизм, который вы предлагаете, является плохим.
По какой-то причине, с моей головы, представьте, что у вас есть список книг с разбивкой по страницам. Может ли это означать, что ваше "сохранение" может удалить всю таблицу базы данных, кроме текущей видимой страницы? Хорошо, скажем, вам удастся определить, сколько элементов отображается на каждой странице, что, если список был отсортирован, поэтому он больше не числился - что вы сейчас удаляете?
Возможно, несколько кнопок отправки в вашей форме будут лучше подходят (например, сохранить изменения, добавить, удалить). Я не пробовал подобные вещи в Grails, но понимаю actionSubmit должен помочь вам достичь нескольких кнопок отправки. Я определенно делал это в Struts!
НТН
Ответ 5
Я просто столкнулся с этой проблемой.
Мой домен приложения довольно прост: он имеет объекты-заглушки, которые имеют hasMany-отношения с объектами заголовка. Поскольку объекты заголовка не имеют собственной жизни, они полностью управляются контроллером Stub и представлениями.
Определения класса домена:
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
Пример: при редактировании -несколько сторон этого отношения у меня есть (для данного Stub с 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"
после удаления заголовка "один" и сохранения объекта Stub в базе данных будут заголовки:
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, то привязка данных к grails будет сохранять существующий объект в этом месте без изменений.
Решение, которое возникает для меня, это привязать данные, а затем проверить заголовки Stub для элементов, которые не присутствуют в представленном списке, и удалить их.
Это выглядит довольно простой сценарий, нет ли встроенных функций для его решения?
Немного перехитрить, чтобы написать собственную логику синхронизации для поддержания отношений, особенно когда причуды, которые делают ее нетривиальной, вызваны самими граалями.
Как насчет удаления, не должны ли очищенные элементы() удалить из базы данных? Я что-то упустил в определении отношений или определения объектов домена?
Ответ 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
}
Я добавил свойство каскада на стороне владельца отношений. Теперь, если вы попытаетесь сохранить заглушку, он позаботится о удалении удаленных элементов из коллекции и удалит их из базы данных.