2010-04-28 10 views
13

Grails ofrece la capacidad de crear y vincular automáticamente objetos de dominio a una lista hasMany, como se describe en el grails user guide.Grails: eliminación de un elemento de una asociación hasMany Lista de datos vinculados?

Así, por ejemplo, si mi dominio objeto "autor" tiene una lista de muchos objetos "Libro", que podría crear y enlazar éstos mediante el siguiente marcado (de la guía del usuario):

<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" /> 

En este caso, si alguno de los libros especificados no existe, Grails los creará y establecerá sus títulos de manera apropiada. Si ya hay libros en los índices especificados, sus títulos se actualizarán y se guardarán. Mi pregunta es: ¿hay alguna manera fácil de decirle a Grails que elimine uno de esos libros de la asociación 'libros' en enlace de datos?

La forma más obvia de hacer esto sería omitir el elemento de formulario que corresponde a la instancia de dominio que desea eliminar; Por desgracia, esto no funciona, según la guía del usuario:

Entonces Griales creará automáticamente una nueva instancia para que en el posición definida. Si "saltó" algunos elementos en el medio ... Entonces Grails creará instancias automáticamente en entre.

que se dan cuenta de que una solución específica podría ser diseñado como parte de un objeto de comando, o como parte de un embargo controller- particular, la necesidad de esta funcionalidad aparece en varias ocasiones a lo largo de mi solicitud, a través de múltiples objetos de dominio y de las asociaciones de muchos tipos diferentes de objetos. Una solución general, por lo tanto, sería ideal. ¿Alguien sabe si hay algo como esto incluido en Grails?

Respuesta

0

Estoy empezando a aprender Grails y vi su pregunta como un interesante ejercicio de investigación para mí. No creo que pueda usar el mecanismo de enlace de datos convencional, ya que rellena los espacios en blanco utilizando algún tipo de mapa Lazy detrás de escena. Así que para que usted pueda lograr su objetivo el método de "salvar" (? o se trata de una función) es poco probable que contienen algo como:

def Book = new Book(params) 

Se necesita un mecanismo para modificar método de "salvar" de su controlador.

Después de algunas investigaciones, entiendo que puede modificar su plantilla de andamio que es responsable de generar su código de controlador o métodos de tiempo de ejecución. Puede obtener una copia de todas las plantillas utilizadas por Grails ejecutando "grails install-templates" y el archivo de plantilla que necesitaría modificar se llama "Controller.groovy".

Por lo tanto, en teoría, podría modificar el método de "guardar" para toda su aplicación de esta manera.

¡Estupendo! Usted pensaría que todo lo que necesita hacer ahora es modificar su método de guardado en la plantilla para que recorra las entradas de objetos (por ejemplo, libros) en el mapa de parámetros, guardando y borrando sobre la marcha.


Sin embargo, creo que su solución requerida todavía podría ser bastante problemática. Mi instinto me dice que hay muchas razones por las cuales el mecanismo que sugieres es una mala idea.

Por una razón, fuera de mi cabeza, imagina que tienes una lista paginada de libros. ¿Podría eso significar que su "guardar" podría eliminar toda la tabla de la base de datos, excepto la página actualmente visible? Bien, digamos que logras calcular cuántos elementos se muestran en cada página, ¿qué pasa si la lista se ordenó para que ya no esté en orden numérico? ¿Qué es lo que eliminas ahora?

Quizás sea mejor tener varios botones de envío en su formulario (por ejemplo, guardar cambios, agregar, eliminar). No he probado este tipo de cosas en Grails, pero entiendo que actionSubmit debería ayudarlo a lograr múltiples botones de envío. ¡Ciertamente solía hacer este tipo de cosas en Struts!

HTH

12

Acabo de toparme con este problema. Es fácil de resolver. Grails usa java.util.Set para representar listas. Puede usar el método clear() para borrar los datos y luego agregar los que desee.

//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" 
} 

apuesto a que puede hacer lo mismo utilizando el método "remove()" en el caso de que usted no quiere a "Clear()" todos los datos.

+0

he encontrado que "bidRequest.documents = []" funciona mejor ya que los documentos podrían ser nulo. – mlathe

0

Me acabo de encontrar con este mismo problema.

El dominio de mi aplicación es bastante simple: tiene objetos Stub que tienen una relación hasMany con los objetos de encabezado. Dado que los objetos de encabezado no tienen vida propia, están totalmente gestionados por el controlador y las vistas de Stub.

Las definiciones de clases de dominio:

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

class Header { 
String value 
static belongsTo = Stub  
} 

que he probado el método "clara y se unen", pero el resultado final es que los objetos "despejadas" se dejan sobre la base de datos y griales se acaba de crear una nueva instancias para los que no fueron eliminados de la relación. Parece funcionar desde la perspectiva de un usuario, pero dejará muchos objetos basura en la base de datos.

El código en el método del controlador de actualización() es:

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

Un ejemplo: mientras se edita el lado -muchos de esta relación que tengo (para un código auxiliar dada con 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 /> 

en la base de datos hay 3 casos de cabecera:

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

después de la eliminación de cabecera "uno" y guardando el S bañera de objetar la base de datos tendrá encabezados:

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

y el objeto Stub tendrá ahora una asociación con encabezados con el ID = 4 y id = 5 ...

Además, sin la limpieza de la lista, si un índice no está presente en la lista request.headers enviada, en el enlace de datos Grails mantendrá el objeto existente en esa ubicación sin cambios.

La solución que se me ocurre es enlazar los datos, luego verifica los encabezados del Stub para ver los elementos que no están presentes en la lista enviada y los elimina.

Esto parece un escenario bastante simple, ¿no hay alguna función incorporada para abordarlo? Es un poco excesivo tener que escribir su propia lógica de sincronización para mantener las relaciones, especialmente cuando las peculiaridades que la hacen no trivial son causadas por los propios griales.

¿Qué pasa con la eliminación, no deberían los elementos claros() 'se han ido de la base de datos? ¿Me falta algo en las definiciones de objeto de relación o dominio?

+1

Terminé encontrando una respuesta a todas mis preguntas aquí: http://omarello.com/2010/08/grails-one-to-many-dynamic-forms/. En pocas palabras: agrega una nueva propiedad a sus objetos para alternar como "eliminada" en la vista, luego en el controlador se vincula y elimina todos los objetos con deleted = true. –

3

Para una buena explicación de la supresión de una colección de objetos secundarios con GORM echar un vistazo a la sección Eliminación de los niños de esta entrada del blog - lectura GORM gotchas part 2

Se recomienda, al igual que las partes 1 y 3 de la serie.

0
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  
} 

he añadido la propiedad en cascada en la parte propietaria de la relación y Ahora bien, si intenta guardar el talón, que se hará cargo de la eliminación de elementos eliminados de la recogida y eliminarlos de la base de datos.

11

removeFrom*

Enfrente del método AddTo en que elimina instancias de una asociación.

Ejemplos

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

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

author.removeFromBooks(book) 
Cuestiones relacionadas