Ist Django Prefetch_Related angeblich mit GenericRelation arbeiten

AKTUALISIEREN Ein offenes Häkchen zu diesem Problem: 24272

Worum geht es?

Django hat ein GenericRelation Klasse, die ein @ hinzufü "Umgekehrte" generische Beziehung, um ein zusätzliches @ zu aktivier API.

Es stellt sich heraus, dass wir diese verwenden könnenreverse-generic-relation zumfiltering oderordering, aber wir können es nicht in @ verwendprefetch_related.

Ich habe mich gefragt, ob dies ein Fehler ist oder ob er nicht funktionieren soll oder ob er in der Funktion implementiert werden kann.

Lassen Sie mich Ihnen anhand einiger Beispiele zeigen, was ich meine.

Nehmen wir an, wir haben zwei Hauptmodelle:Movies undBooks.

Movies haben eineDirectorBooks einen habenAuthor

Und wir möchten unseren @ Tags zuweisMovies undBooks, aber anstatt @ zu verwendMovieTag undBookTag Modelle, wir wollen ein einzelnes @ verwendTaggedItem Klasse mit einemGFK zuMovie oderBook.

Hier ist die Modellstruktur:

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

Und einige anfängliche Daten:

>>> 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')

So wie das docsshow wir können sowas machen.

>>> 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>]

Aber, wenn wir versuchen,prefetch etwasrelated data vonTaggedItem über diesesreverse generic relation, wir werden ein @ bekomm AttributeError.

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

Einige von Ihnen fragen sich vielleicht, warum ich @ einfach nicht benutzcontent_object Anstatt vonbooks Hier? Der Grund ist, weil dies nur funktioniert, wenn wir wollen:

1)prefetch nur eine Ebene tief vonquerysets enthält verschiedene Arten voncontent_object.

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

2)prefetch viele Ebenen, aber vonquerysets enthält nur einen Typ voncontent_object.

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

Aber, wenn wir beide wollen 1) und 2) (zuprefetch viele Ebenen vonqueryset enthält verschiedene Arten voncontent_objects, wir können nicht @ verwendcontent_object.

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

Django denkt, dass allecontent_objects sindBooks, und so haben sie einAuthor.

Jetzt stellen Sie sich die Situation vor, in der wir wollenprefetch nicht nur dasbooks mit derenauthor, aber auch dasmovies mit derendirector. Hier sind einige Versuche.

Der dumme Weg:

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

Vielleicht mit benutzerdefiniertenPrefetch Objekt

>>>
>>> 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.

Einige Lösungen für dieses Problem werden angezeigtHie. Aber das ist eine Menge Ärger über die Daten, die ich vermeiden möchte. Ich mag die API aus demreversed generic relations, es wäre sehr schön, @ tun zu könnprefetchs so wie das

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

Oder so:

>>> 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'

Aber wie Sie sehen können, bekommen wir immer das AttributeError. Ich benutze Django1.7.3 und Python2.7.6. Und ich bin gespannt, warum Django diesen Fehler auslöst. Warum sucht Django nach einemobject_id in demBook model?Warum denke ich, dass dies ein Fehler sein kann? Normalerweise wenn wir fragenprefetch_related Um etwas zu lösen, das es nicht kann, sehen wir:

>>> 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()

Aber hier ist es anders. Django versucht tatsächlich, die Beziehung aufzulösen ... und schlägt fehl. Ist das ein Fehler, der gemeldet werden sollte? Ich habe Django noch nie etwas gemeldet, deshalb frage ich zuerst hier. Ich kann den Fehler nicht nachvollziehen und selbst entscheiden, ob es sich um einen Fehler oder eine Funktion handelt, die implementiert werden könnte.

Antworten auf die Frage(4)

Ihre Antwort auf die Frage