Optimizando consultas M2M con django-batch-select

Es una idea original para evitarnos consultas innecesarias cuando
queremos, para una lista de objetos, sacar una lista de propiedades del
mismo (definidas como un atributo m2m del objeto).

Lo explica muy bien aquí:

   http://github.com/lilspikey/django-batch-select

Voy a resumirlo en español y ejemplificando un caso de uso posible.
Supongamos estos modelos:

class Image(models.Model):
    modification_date = models.DateTimeField(...)
    ...

class Artist(models.Model):
    name = models.CharField(...)
    images = models.ManyToManyField(Image)

Supongamos que queremos el típico listado de artistas (paginado de N en
N) con las últimas imágenes de cada uno, todo en la misma página. Como
sabéis, un Artist.objects.all().select_related() no sigue los m2m (ver razón),
con lo que nunca se hará JOIN con Image a menos que se realice a mano
con un extra(tables=...). Esto implica que o lo hacemos a mano o se
ejecutarán N*2 consultas (donde N es el tamaño de la paginación).

Pues bien, con django-batch-select se reducirían las consultas a 2, sea
cual sea el tamaño de página, con una secuencia como la que sigue (ojo,
código no probado):

>>> artists = Artist.objects.batch_select('images').all()
>>> artists[0].images_all # imagenes del primer artista
[<Image image object>, ...]

Esto realiza estos dos selects:

SELECT * FROM app_artist; # guardando los ids en python, p.ej. (1, 3, 5)
SELECT * FROM app_image JOIN app_artist_image ON (app_image.id =
app_artist_image.image_id) WHERE artist_id in (1, 3, 5)

Pero como algún perspicaz habrá adivinado, esto no devuelve las últimas
fotos del artista, sino todas, y sin ordenación. Podemos realizarlo de
la siguiente forma:

>>> batch = Batch('images').order_by('-modification_date')[:5]
>>> artists = Artist.objects.batch_select(batch).all()

O cambiar el descriptor:

>>> artists = Artist.objects.batch_select(last_images=batch).all()
>>> artists[0].last_images
[<Image image object>, ...]

La verdad es que una cosa así bien merece la pena integrarla en el
BaseManager de merengue, para posibles usos futuros.