mirror of
https://github.com/django/django.git
synced 2024-12-23 01:25:58 +00:00
Fixed #31926 -- Fixed recreating queryset with FilteredRelation when using a pickled Query.
In a pickled join, the join_fields had the same values, but weren't the same object (contrary to when not pickling the QuerySet).
This commit is contained in:
parent
292b3be698
commit
c32d8f33d8
@ -11,6 +11,7 @@ they're the closest concept currently available.
|
|||||||
|
|
||||||
from django.core import exceptions
|
from django.core import exceptions
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.hashable import make_hashable
|
||||||
|
|
||||||
from . import BLANK_CHOICE_DASH
|
from . import BLANK_CHOICE_DASH
|
||||||
from .mixins import FieldCacheMixin
|
from .mixins import FieldCacheMixin
|
||||||
@ -115,6 +116,28 @@ class ForeignObjectRel(FieldCacheMixin):
|
|||||||
self.related_model._meta.model_name,
|
self.related_model._meta.model_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def identity(self):
|
||||||
|
return (
|
||||||
|
self.field,
|
||||||
|
self.model,
|
||||||
|
self.related_name,
|
||||||
|
self.related_query_name,
|
||||||
|
tuple(sorted(make_hashable(self.limit_choices_to))),
|
||||||
|
self.parent_link,
|
||||||
|
self.on_delete,
|
||||||
|
self.symmetrical,
|
||||||
|
self.multiple,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
return NotImplemented
|
||||||
|
return self.identity == other.identity
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.identity)
|
||||||
|
|
||||||
def get_choices(
|
def get_choices(
|
||||||
self, include_blank=True, blank_choice=BLANK_CHOICE_DASH,
|
self, include_blank=True, blank_choice=BLANK_CHOICE_DASH,
|
||||||
limit_choices_to=None, ordering=(),
|
limit_choices_to=None, ordering=(),
|
||||||
@ -215,6 +238,10 @@ class ManyToOneRel(ForeignObjectRel):
|
|||||||
state.pop('related_model', None)
|
state.pop('related_model', None)
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def identity(self):
|
||||||
|
return super().identity + (self.field_name,)
|
||||||
|
|
||||||
def get_related_field(self):
|
def get_related_field(self):
|
||||||
"""
|
"""
|
||||||
Return the Field in the 'to' object to which this relationship is tied.
|
Return the Field in the 'to' object to which this relationship is tied.
|
||||||
@ -279,6 +306,14 @@ class ManyToManyRel(ForeignObjectRel):
|
|||||||
self.symmetrical = symmetrical
|
self.symmetrical = symmetrical
|
||||||
self.db_constraint = db_constraint
|
self.db_constraint = db_constraint
|
||||||
|
|
||||||
|
@property
|
||||||
|
def identity(self):
|
||||||
|
return super().identity + (
|
||||||
|
self.through,
|
||||||
|
self.through_fields,
|
||||||
|
self.db_constraint,
|
||||||
|
)
|
||||||
|
|
||||||
def get_related_field(self):
|
def get_related_field(self):
|
||||||
"""
|
"""
|
||||||
Return the field in the 'to' object to which this relationship is tied.
|
Return the field in the 'to' object to which this relationship is tied.
|
||||||
|
@ -219,6 +219,40 @@ class PickleabilityTestCase(TestCase):
|
|||||||
with self.assertNumQueries(0):
|
with self.assertNumQueries(0):
|
||||||
self.assert_pickles(groups)
|
self.assert_pickles(groups)
|
||||||
|
|
||||||
|
def test_pickle_filteredrelation(self):
|
||||||
|
group = Group.objects.create(name='group')
|
||||||
|
event_1 = Event.objects.create(title='Big event', group=group)
|
||||||
|
event_2 = Event.objects.create(title='Small event', group=group)
|
||||||
|
Happening.objects.bulk_create([
|
||||||
|
Happening(event=event_1, number1=5),
|
||||||
|
Happening(event=event_2, number1=3),
|
||||||
|
])
|
||||||
|
groups = Group.objects.annotate(
|
||||||
|
big_events=models.FilteredRelation(
|
||||||
|
'event',
|
||||||
|
condition=models.Q(event__title__startswith='Big'),
|
||||||
|
),
|
||||||
|
).annotate(sum_number=models.Sum('big_events__happening__number1'))
|
||||||
|
groups_query = pickle.loads(pickle.dumps(groups.query))
|
||||||
|
groups = Group.objects.all()
|
||||||
|
groups.query = groups_query
|
||||||
|
self.assertEqual(groups.get().sum_number, 5)
|
||||||
|
|
||||||
|
def test_pickle_filteredrelation_m2m(self):
|
||||||
|
group = Group.objects.create(name='group')
|
||||||
|
m2mmodel = M2MModel.objects.create()
|
||||||
|
m2mmodel.groups.add(group)
|
||||||
|
groups = Group.objects.annotate(
|
||||||
|
first_m2mmodels=models.FilteredRelation(
|
||||||
|
'm2mmodel',
|
||||||
|
condition=models.Q(m2mmodel__pk__lt=10),
|
||||||
|
),
|
||||||
|
).annotate(count_groups=models.Count('first_m2mmodels__groups'))
|
||||||
|
groups_query = pickle.loads(pickle.dumps(groups.query))
|
||||||
|
groups = Group.objects.all()
|
||||||
|
groups.query = groups_query
|
||||||
|
self.assertEqual(groups.get().count_groups, 1)
|
||||||
|
|
||||||
def test_annotation_with_callable_default(self):
|
def test_annotation_with_callable_default(self):
|
||||||
# Happening.when has a callable default of datetime.datetime.now.
|
# Happening.when has a callable default of datetime.datetime.now.
|
||||||
qs = Happening.objects.annotate(latest_time=models.Max('when'))
|
qs = Happening.objects.annotate(latest_time=models.Max('when'))
|
||||||
|
Loading…
Reference in New Issue
Block a user