Fixed #21410 -- prefetch_related() for ForeignKeys with related_name='+'
Regression introduced by commit 9777442
.
Thanks to trac username troygrosfield for the report and test case.
This commit is contained in:
parent
0048ed77c7
commit
cb83448891
|
@ -274,7 +274,17 @@ class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjec
|
||||||
rel_obj_attr = self.field.get_foreign_related_value
|
rel_obj_attr = self.field.get_foreign_related_value
|
||||||
instance_attr = self.field.get_local_related_value
|
instance_attr = self.field.get_local_related_value
|
||||||
instances_dict = dict((instance_attr(inst), inst) for inst in instances)
|
instances_dict = dict((instance_attr(inst), inst) for inst in instances)
|
||||||
query = {'%s__in' % self.field.related_query_name(): instances}
|
related_field = self.field.foreign_related_fields[0]
|
||||||
|
|
||||||
|
# FIXME: This will need to be revisited when we introduce support for
|
||||||
|
# composite fields. In the meantime we take this practical approach to
|
||||||
|
# solve a regression on 1.6 when the reverse manager in hidden
|
||||||
|
# (related_name ends with a '+'). Refs #21410.
|
||||||
|
if self.field.rel.is_hidden():
|
||||||
|
query = {'%s__in' % related_field.name: set(instance_attr(inst)[0] for inst in instances)}
|
||||||
|
else:
|
||||||
|
query = {'%s__in' % self.field.related_query_name(): instances}
|
||||||
|
|
||||||
qs = self.get_queryset(instance=instances[0]).filter(**query)
|
qs = self.get_queryset(instance=instances[0]).filter(**query)
|
||||||
# Since we're going to assign directly in the cache,
|
# Since we're going to assign directly in the cache,
|
||||||
# we must manage the reverse relation cache manually.
|
# we must manage the reverse relation cache manually.
|
||||||
|
|
|
@ -3,8 +3,8 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
## Basic tests
|
|
||||||
|
|
||||||
|
## Basic tests
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Author(models.Model):
|
class Author(models.Model):
|
||||||
|
@ -80,8 +80,8 @@ class BookReview(models.Model):
|
||||||
book = models.ForeignKey(BookWithYear)
|
book = models.ForeignKey(BookWithYear)
|
||||||
notes = models.TextField(null=True, blank=True)
|
notes = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
## Models for default manager tests
|
|
||||||
|
|
||||||
|
## Models for default manager tests
|
||||||
|
|
||||||
class Qualification(models.Model):
|
class Qualification(models.Model):
|
||||||
name = models.CharField(max_length=10)
|
name = models.CharField(max_length=10)
|
||||||
|
@ -167,7 +167,6 @@ class Comment(models.Model):
|
||||||
|
|
||||||
## Models for lookup ordering tests
|
## Models for lookup ordering tests
|
||||||
|
|
||||||
|
|
||||||
class House(models.Model):
|
class House(models.Model):
|
||||||
address = models.CharField(max_length=255)
|
address = models.CharField(max_length=255)
|
||||||
owner = models.ForeignKey('Person', null=True)
|
owner = models.ForeignKey('Person', null=True)
|
||||||
|
@ -212,7 +211,7 @@ class Employee(models.Model):
|
||||||
ordering = ['id']
|
ordering = ['id']
|
||||||
|
|
||||||
|
|
||||||
### Ticket 19607
|
## Ticket #19607
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class LessonEntry(models.Model):
|
class LessonEntry(models.Model):
|
||||||
|
@ -230,3 +229,18 @@ class WordEntry(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s (%s)" % (self.name, self.id)
|
return "%s (%s)" % (self.name, self.id)
|
||||||
|
|
||||||
|
|
||||||
|
## Ticket #21410: Regression when related_name="+"
|
||||||
|
|
||||||
|
@python_2_unicode_compatible
|
||||||
|
class Author2(models.Model):
|
||||||
|
name = models.CharField(max_length=50, unique=True)
|
||||||
|
first_book = models.ForeignKey('Book', related_name='first_time_authors+')
|
||||||
|
favorite_books = models.ManyToManyField('Book', related_name='+')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['id']
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.utils import six
|
||||||
from .models import (Author, Book, Reader, Qualification, Teacher, Department,
|
from .models import (Author, Book, Reader, Qualification, Teacher, Department,
|
||||||
TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge,
|
TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge,
|
||||||
BookWithYear, BookReview, Person, House, Room, Employee, Comment,
|
BookWithYear, BookReview, Person, House, Room, Employee, Comment,
|
||||||
LessonEntry, WordEntry)
|
LessonEntry, WordEntry, Author2)
|
||||||
|
|
||||||
|
|
||||||
class PrefetchRelatedTests(TestCase):
|
class PrefetchRelatedTests(TestCase):
|
||||||
|
@ -973,3 +973,29 @@ class Ticket19607Tests(TestCase):
|
||||||
|
|
||||||
def test_bug(self):
|
def test_bug(self):
|
||||||
list(WordEntry.objects.prefetch_related('lesson_entry', 'lesson_entry__wordentry_set'))
|
list(WordEntry.objects.prefetch_related('lesson_entry', 'lesson_entry__wordentry_set'))
|
||||||
|
|
||||||
|
|
||||||
|
class Ticket21410Tests(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.book1 = Book.objects.create(title="Poems")
|
||||||
|
self.book2 = Book.objects.create(title="Jane Eyre")
|
||||||
|
self.book3 = Book.objects.create(title="Wuthering Heights")
|
||||||
|
self.book4 = Book.objects.create(title="Sense and Sensibility")
|
||||||
|
|
||||||
|
self.author1 = Author2.objects.create(name="Charlotte",
|
||||||
|
first_book=self.book1)
|
||||||
|
self.author2 = Author2.objects.create(name="Anne",
|
||||||
|
first_book=self.book1)
|
||||||
|
self.author3 = Author2.objects.create(name="Emily",
|
||||||
|
first_book=self.book1)
|
||||||
|
self.author4 = Author2.objects.create(name="Jane",
|
||||||
|
first_book=self.book4)
|
||||||
|
|
||||||
|
self.author1.favorite_books.add(self.book1, self.book2, self.book3)
|
||||||
|
self.author2.favorite_books.add(self.book1)
|
||||||
|
self.author3.favorite_books.add(self.book2)
|
||||||
|
self.author4.favorite_books.add(self.book3)
|
||||||
|
|
||||||
|
def test_bug(self):
|
||||||
|
list(Author2.objects.prefetch_related('first_book', 'favorite_books'))
|
||||||
|
|
Loading…
Reference in New Issue