1
0
mirror of https://github.com/django/django.git synced 2025-06-06 20:19:13 +00:00

[3.0.x] Fixed #30854 -- Fixed QuerySet.select_related() with multiple FilteredRelations.

Backport of 6a75cea76a98c08bf2e20d787be9b14c2cd94860 from master.
This commit is contained in:
Hasan Ramezani 2019-10-10 21:13:21 +02:00 committed by Mariusz Felisiak
parent 0dede9e981
commit c1cfec6b50
2 changed files with 18 additions and 4 deletions

View File

@ -1,6 +1,7 @@
import collections import collections
import re import re
import warnings import warnings
from functools import partial
from itertools import chain from itertools import chain
from django.core.exceptions import EmptyResultSet, FieldError from django.core.exceptions import EmptyResultSet, FieldError
@ -902,6 +903,10 @@ class SQLCompiler:
select, model._meta, alias, cur_depth + 1, select, model._meta, alias, cur_depth + 1,
next, restricted) next, restricted)
get_related_klass_infos(klass_info, next_klass_infos) get_related_klass_infos(klass_info, next_klass_infos)
def remote_setter(name, obj, from_obj):
setattr(from_obj, name, obj)
for name in list(requested): for name in list(requested):
# Filtered relations work only on the topmost level. # Filtered relations work only on the topmost level.
if cur_depth > 1: if cur_depth > 1:
@ -917,15 +922,12 @@ class SQLCompiler:
# Set a reverse fk object when relation is non-empty. # Set a reverse fk object when relation is non-empty.
if from_obj: if from_obj:
f.remote_field.set_cached_value(from_obj, obj) f.remote_field.set_cached_value(from_obj, obj)
def remote_setter(obj, from_obj):
setattr(from_obj, name, obj)
klass_info = { klass_info = {
'model': model, 'model': model,
'field': f, 'field': f,
'reverse': True, 'reverse': True,
'local_setter': local_setter, 'local_setter': local_setter,
'remote_setter': remote_setter, 'remote_setter': partial(remote_setter, name),
'from_parent': from_parent, 'from_parent': from_parent,
} }
related_klass_infos.append(klass_info) related_klass_infos.append(klass_info)

View File

@ -50,6 +50,18 @@ class FilteredRelationTests(TestCase):
(self.author2, self.book3, self.editor_b, self.author2), (self.author2, self.book3, self.editor_b, self.author2),
], lambda x: (x, x.book_join, x.book_join.editor, x.book_join.author)) ], lambda x: (x, x.book_join, x.book_join.editor, x.book_join.author))
def test_select_related_multiple(self):
qs = Book.objects.annotate(
author_join=FilteredRelation('author'),
editor_join=FilteredRelation('editor'),
).select_related('author_join', 'editor_join').order_by('pk')
self.assertQuerysetEqual(qs, [
(self.book1, self.author1, self.editor_a),
(self.book2, self.author2, self.editor_b),
(self.book3, self.author2, self.editor_b),
(self.book4, self.author1, self.editor_a),
], lambda x: (x, x.author_join, x.editor_join))
def test_select_related_with_empty_relation(self): def test_select_related_with_empty_relation(self):
qs = Author.objects.annotate( qs = Author.objects.annotate(
book_join=FilteredRelation('book', condition=Q(pk=-1)), book_join=FilteredRelation('book', condition=Q(pk=-1)),