update through query manytomany many how django django-queryset manytomanyfield django-orm

through - Django: filtro ManyToMany que coincide en TODOS los elementos de una lista



model many to many relationship django (4)

Tengo tal modelo de libro:

class Book(models.Model): authors = models.ManyToManyField(Author, ...) ...

En breve:

Me gustaría recuperar los libros cuyos autores son estrictamente iguales a un conjunto dado de autores. No estoy seguro de si hay una sola consulta que lo haga, pero cualquier sugerencia será útil.

En largo

Esto es lo que intenté (que no se pudo ejecutar para obtener un AttributeError)

# A sample set of authors target_authors = set((author_1, author_2)) # To reduce the search space, # first retrieve those books with just 2 authors. candidate_books = Book.objects.annotate(c=Count(''authors'')).filter(c=len(target_authors)) final_books = QuerySet() for author in target_authors: temp_books = candidate_books.filter(authors__in=[author]) final_books = final_books and temp_books

... y aquí está lo que tengo:

AttributeError: ''NoneType'' object has no attribute ''_meta''

En general, ¿cómo debo consultar un modelo con la restricción de que su campo ManyToMany contiene un conjunto de objetos dados como en mi caso?

pd: encontré algunas preguntas de SO relevantes pero no pude obtener una respuesta clara. Cualquier buen puntero será útil también. Gracias.


Al igual que en el enfoque de @ goliney, encontré una solución. Sin embargo, creo que la eficiencia podría mejorarse.

# A sample set of authors target_authors = set((author_1, author_2)) # To reduce the search space, first retrieve those books with just 2 authors. candidate_books = Book.objects.annotate(c=Count(''authors'')).filter(c=len(target_authors)) # In each iteration, we filter out those books which don''t contain one of the # required authors - the instance on the iteration. for author in target_authors: candidate_books = candidate_books.filter(authors=author) final_books = candidate_books


Encontré el mismo problema y llegué a la misma conclusión que iuysal, hasta que tuve que hacer una búsqueda de tamaño medio (con 1000 registros con 150 filtros, mi solicitud expiraría).

En mi caso particular, la búsqueda no daría como resultado ningún registro, ya que la posibilidad de que un solo registro se alinee con TODOS los 150 filtros es muy rara, puede evitar los problemas de rendimiento al verificar que haya registros en el QuerySet antes de aplicar más filtros para guardar hora.

# In each iteration, we filter out those books which don''t contain one of the # required authors - the instance on the iteration. for author in target_authors: if candidate_books.count() > 0: candidate_books = candidate_books.filter(authors=author)

Por alguna razón, Django aplica filtros para vaciar QuerySets. Sin embargo, si la optimización se debe aplicar correctamente, es necesario utilizar un QuerySet preparado e índices aplicados correctamente.


Puedes utilizar búsquedas complejas con objetos Q

from django.db.models import Q ... target_authors = set((author_1, author_2)) q = Q() for author in target_authors: q &= Q(authors=author) Books.objects.annotate(c=Count(''authors'')).filter(c=len(target_authors)).filter(q)


Q () y Q () no es igual a .filter (). Filter (). Sus SQL en bruto son diferentes donde al usar Q con &, su SQL solo agrega una condición como WHERE "book"."author" = "author_1" and "book"."author" = "author_2" . debe devolver el resultado vacío.

La única solución es simplemente encadenar el filtro para formar un SQL con unión interna en la misma tabla: ... ON ("author"."id" = "author_book"."author_id") INNER JOIN "author_book" T4 ON ("author"."id" = T4."author_id") WHERE ("author_book"."author_id" = "author_1" AND T4."author_id" = "author_1")