¿Se supone que django prefetch_related funciona con GenericRelation?

ACTUALIZAR: Un Open Ticked sobre este tema:24272

¿De qué se trata todo esto?

Django tiene unRelación genérica clase, que agrega unRelación genérica "inversa" para habilitar un adicionalAPI.

Resulta que podemos usar estoreverse-generic-relation parafiltering oordering, pero no podemos usarlo adentroprefetch_related.

Me preguntaba si esto es un error, o no se supone que funcione, o es algo que se puede implementar en la función.

Déjame mostrarte con algunos ejemplos lo que quiero decir.

Digamos que tenemos dos modelos principales:Movies yBooks.

Movies tener unDirectorBooks tienen unaAuthor

Y queremos asignar etiquetas a nuestroMovies yBooks, pero en lugar de usarMovieTag yBookTag modelos, queremos usar un soloTaggedItem clase con unGFK aMovie oBook.

Aquí está la estructura del modelo:

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType


class TaggedItem(models.Model):
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __unicode__(self):
        return self.tag


class Director(models.Model):
    name = models.CharField(max_length=100)

    def __unicode__(self):
        return self.name


class Movie(models.Model):
    name = models.CharField(max_length=100)
    director = models.ForeignKey(Director)
    tags = GenericRelation(TaggedItem, related_query_name='movies')

    def __unicode__(self):
        return self.name


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

    def __unicode__(self):
        return self.name


class Book(models.Model):
    name = models.CharField(max_length=100)
    author = models.ForeignKey(Author)
    tags = GenericRelation(TaggedItem, related_query_name='books')

    def __unicode__(self):
        return self.name

Y algunos datos iniciales:

>>> from tags.models import Book, Movie, Author, Director, TaggedItem
>>> a = Author.objects.create(name='E L James')
>>> b1 = Book.objects.create(name='Fifty Shades of Grey', author=a)
>>> b2 = Book.objects.create(name='Fifty Shades Darker', author=a)
>>> b3 = Book.objects.create(name='Fifty Shades Freed', author=a)
>>> d = Director.objects.create(name='James Gunn')
>>> m1 = Movie.objects.create(name='Guardians of the Galaxy', director=d)
>>> t1 = TaggedItem.objects.create(content_object=b1, tag='roman')
>>> t2 = TaggedItem.objects.create(content_object=b2, tag='roman')
>>> t3 = TaggedItem.objects.create(content_object=b3, tag='roman')
>>> t4 = TaggedItem.objects.create(content_object=m1, tag='action movie')

Entonces como eldocs mostrar que podemos hacer cosas como esta.

>>> b1.tags.all()
[<TaggedItem: roman>]
>>> m1.tags.all()
[<TaggedItem: action movie>]
>>> TaggedItem.objects.filter(books__author__name='E L James')
[<TaggedItem: roman>, <TaggedItem: roman>, <TaggedItem: roman>]
>>> TaggedItem.objects.filter(movies__director__name='James Gunn')
[<TaggedItem: action movie>]
>>> Book.objects.all().prefetch_related('tags')
[<Book: Fifty Shades of Grey>, <Book: Fifty Shades Darker>, <Book: Fifty Shades Freed>]
>>> Book.objects.filter(tags__tag='roman')
[<Book: Fifty Shades of Grey>, <Book: Fifty Shades Darker>, <Book: Fifty Shades Freed>]

Pero si tratamos deprefetch&nbsp;algunosrelated data&nbsp;deTaggedItem&nbsp;a través de estoreverse generic relationvamos a obtener unAttributeError.

>>> TaggedItem.objects.all().prefetch_related('books')
Traceback (most recent call last):
  ...
AttributeError: 'Book' object has no attribute 'object_id'

Algunos de ustedes pueden preguntar, ¿por qué simplemente no usocontent_object&nbsp;en lugar debooks&nbsp;¿aquí? La razón es porque esto solo funciona cuando queremos:

1)prefetch&nbsp;solo un nivel de profundidad desdequerysets&nbsp;que contiene diferentes tipos decontent_object.

>>> TaggedItem.objects.all().prefetch_related('content_object')
[<TaggedItem: roman>, <TaggedItem: roman>, <TaggedItem: roman>, <TaggedItem: action movie>]

2)prefetch&nbsp;muchos niveles pero desdequerysets&nbsp;que contiene solo un tipo decontent_object.

>>> TaggedItem.objects.filter(books__author__name='E L James').prefetch_related('content_object__author')
[<TaggedItem: roman>, <TaggedItem: roman>, <TaggedItem: roman>]

Pero, si queremos tanto 1) como 2) (paraprefetch&nbsp;muchos niveles dequeryset&nbsp;que contiene diferentes tipos decontent_objects, no podemos usarcontent_object.

>>> TaggedItem.objects.all().prefetch_related('content_object__author')
Traceback (most recent call last):
  ...
AttributeError: 'Movie' object has no attribute 'author_id'

Django&nbsp;piensa que todoscontent_objects&nbsp;sonBooksy por lo tanto tienen unAuthor.

Ahora imagine la situación donde queremosprefetch&nbsp;no solo elbooks&nbsp;con suauthor, pero también elmovies&nbsp;con sudirector. Aquí hay algunos intentos.

La manera tonta:

>>> TaggedItem.objects.all().prefetch_related(
...     'content_object__author',
...     'content_object__director',
... )
Traceback (most recent call last):
  ...
AttributeError: 'Movie' object has no attribute 'author_id'

Tal vez con costumbrePrefetch&nbsp;¿objeto?

>>>
>>> TaggedItem.objects.all().prefetch_related(
...     Prefetch('content_object', queryset=Book.objects.all().select_related('author')),
...     Prefetch('content_object', queryset=Movie.objects.all().select_related('director')),
... )
Traceback (most recent call last):
  ...
ValueError: Custom queryset can't be used for this lookup.

Se muestran algunas soluciones a este problema.aquí. Pero eso es mucho masaje sobre los datos que quiero evitar. Realmente me gusta la API que viene dereversed generic relations, sería muy bueno poder hacerprefetchs&nbsp;como eso:

>>> TaggedItem.objects.all().prefetch_related(
...     'books__author',
...     'movies__director',
... )
Traceback (most recent call last):
  ...
AttributeError: 'Book' object has no attribute 'object_id'

O asi:

>>> TaggedItem.objects.all().prefetch_related(
...     Prefetch('books', queryset=Book.objects.all().select_related('author')),
...     Prefetch('movies', queryset=Movie.objects.all().select_related('director')),
... )
Traceback (most recent call last):
  ...
AttributeError: 'Book' object has no attribute 'object_id'

Pero como puedes ver, siempre lo entendemosAttributeError. Estoy usando Django1.7.3&nbsp;y Python2.7.6. ¿Y tengo curiosidad por qué Django está arrojando ese error? ¿Por qué Django está buscando unobject_id&nbsp;en elBook&nbsp;¿modelo?¿Por qué creo que esto puede ser un error?&nbsp;Usualmente cuando preguntamosprefetch_related&nbsp;para resolver algo que no puede, vemos:

>>> TaggedItem.objects.all().prefetch_related('some_field')
Traceback (most recent call last):
  ...
AttributeError: Cannot find 'some_field' on TaggedItem object, 'some_field' is an invalid parameter to prefetch_related()

Pero aquí, es diferente. Django en realidad intenta resolver la relación ... y falla. ¿Es este un error que se debe informar? Nunca le he informado nada a Django, por eso pregunto aquí primero. No puedo rastrear el error y decidir por mí mismo si se trata de un error o de una característica que podría implementarse.