2009-07-28 52 views
13

He aquí un ejemplo:¿Cómo actualizar el campo manytomany en Django?

Si tengo estas clases

class Author(models.Model): 
    name = models.CharField(max_length=45) 

class Book(models.Model): 
    name = models.CharField(max_length=45) 
    authors = models.ManyToManyField(Author) 

En la base de datos que tengo un autor con el nombre "George" y otra con el nombre de "Georfe". El último es un error. Entonces, lo que quiero es que en cada Libro que tenga "Georfe" como uno de sus Autor lo reemplace por el Autor "George".

En SQL es realmente fácil de hacer. Si ID de "George" = 3 y la identificación de "Georfe" = 7 y el nombre de la tabla relación es "author_book":

UPDATE author_book SET id=3 WHERE id=7; 

¿Es posible hacer eso con el ORM de Django?

he encontrado una manera de hacerlo: a través de bucle que todos los libros relacionados del autor introducida por error y hacer:

book.authors.add(Author.objects.get(id=3)) 
book.authors.remove(Author.objects.get(id=7)) 

Pero no encuentro esta solución muy elegante y eficiente. ¿Hay una solución sin el bucle?

+0

Si esta es una operación única para corregir algunos errores tipográficos, ¿por qué no simplemente ejecutar el SQL sin procesar? – thedz

+0

Los datos en la base de datos provienen de una antigua base de datos de creación de archivos y hay muchos errores tipográficos que corregir. No quiero arreglar todos los errores tipográficos solo. Deseo que mi cliente pueda hacerlo en la interfaz de administración. – Etienne

Respuesta

14

Nota: Este código será eliminar el autor malo 'georfe', así como la actualización de los libros para apuntar al autor correcto. Si no quiere hacer eso, entonces use .remove() como la respuesta de @ jcdyer.

¿Puedes hacer algo como esto?

george_author = Author.objects.get(name="George") 
for book in Book.objects.filter(authors__name="Georfe"): 
    book.authors.add(george_author.id) 
    book.authors.filter(name="Georfe").delete() 

sospecho que esto sería más fácil si tuviera una tabla explícita que une los dos modelos (con la "a través de" palabra clave arg) - en ese caso, se debe tener acceso a la tabla de relación directa, y podría hacer un .update(id=george_author.id) en él.

+0

Vea mi comentario a jcd, su solución es básicamente la misma (excepto la eliminación, pero no quiero eliminar el autor mal escrito necesariamente, así que eliminarlo es mejor en mi caso). Creo que su solución con la actualización es la forma correcta de hacerlo. Acepto esta respuestaDesafortunadamente, mi caso es más complejo porque extiendo ModelAdmin y todo tiene que ser genérico y no creo que pueda usar la tabla de relaciones directamente (porque no podría estar seguro si la argumentación "mínima" está establecida). ¿Estoy en lo cierto? – Etienne

+0

En una instancia, puede ver el argumento 'through': book.authors.through = 'ModelName' - No estoy seguro de si puede acceder a él a través de la clase misma. –

+0

Compruebo el argumento "through" en una instancia y si no está establecido en ManyToManyField, through es None. Entonces, por el momento, mantendré la solución del ciclo con el agregar/eliminar (o eliminar). Tal vez no sea eficiente y eficiente, pero funciona lo suficientemente bien para nuestras necesidades. Gracias Ian, jcd – Etienne

14

Con la tabla directa generada automáticamente, puede hacer una inserción y eliminación de dos pasos, lo que es bueno para la legibilidad.

george = Author.objects.get(name='George') 
georfe = Author.objects.get(name='Georfe') 

book.authors.add(george) 
book.authors.remove(georfe) 
assert george in book.authors 

Si ha definido explícitamente a través de una mesa (autores = models.ManyToManyField (Autor, a través de BookAuthors =), entonces puede cambiar la relación de forma explícita en BookAuthor. Es que este modelo ya existe un hecho poco conocido, se es generada por Django automáticamente. por lo general, sólo se debe crear una explícita a través del modelo si tiene datos adicionales que desea guardar (por ejemplo, los capítulos de un determinado autor escribió, en un libro de varios autores).

# This line is only needed without a custom through model. 
BookAuthor = Book.authors.through 
book_author = BookAuthor.objects.get(author=georfe, book=great_american_novel) 
book_author.author = george 
book_author.save() 
assert george in book.authors 
+0

¡Ha, escribimos sobre todo la misma cosa al mismo tiempo! Pero no sabía sobre el argumento "mínimo" de ManyToManyField. Voy a investigar eso. Pero la forma en que Ian usa la tabla de relaciones con la actualización parece ser más eficiente. – Etienne

Cuestiones relacionadas