1. import datetime
    
  2. import pickle
    
  3. 
    
  4. import django
    
  5. from django.db import models
    
  6. from django.test import TestCase
    
  7. 
    
  8. from .models import (
    
  9.     BinaryFieldModel,
    
  10.     Container,
    
  11.     Event,
    
  12.     Group,
    
  13.     Happening,
    
  14.     M2MModel,
    
  15.     MyEvent,
    
  16. )
    
  17. 
    
  18. 
    
  19. class PickleabilityTestCase(TestCase):
    
  20.     @classmethod
    
  21.     def setUpTestData(cls):
    
  22.         cls.happening = (
    
  23.             Happening.objects.create()
    
  24.         )  # make sure the defaults are working (#20158)
    
  25. 
    
  26.     def assert_pickles(self, qs):
    
  27.         self.assertEqual(list(pickle.loads(pickle.dumps(qs))), list(qs))
    
  28. 
    
  29.     def test_binaryfield(self):
    
  30.         BinaryFieldModel.objects.create(data=b"binary data")
    
  31.         self.assert_pickles(BinaryFieldModel.objects.all())
    
  32. 
    
  33.     def test_related_field(self):
    
  34.         g = Group.objects.create(name="Ponies Who Own Maybachs")
    
  35.         self.assert_pickles(Event.objects.filter(group=g.id))
    
  36. 
    
  37.     def test_datetime_callable_default_all(self):
    
  38.         self.assert_pickles(Happening.objects.all())
    
  39. 
    
  40.     def test_datetime_callable_default_filter(self):
    
  41.         self.assert_pickles(Happening.objects.filter(when=datetime.datetime.now()))
    
  42. 
    
  43.     def test_string_as_default(self):
    
  44.         self.assert_pickles(Happening.objects.filter(name="test"))
    
  45. 
    
  46.     def test_standalone_method_as_default(self):
    
  47.         self.assert_pickles(Happening.objects.filter(number1=1))
    
  48. 
    
  49.     def test_staticmethod_as_default(self):
    
  50.         self.assert_pickles(Happening.objects.filter(number2=1))
    
  51. 
    
  52.     def test_filter_reverse_fk(self):
    
  53.         self.assert_pickles(Group.objects.filter(event=1))
    
  54. 
    
  55.     def test_doesnotexist_exception(self):
    
  56.         # Ticket #17776
    
  57.         original = Event.DoesNotExist("Doesn't exist")
    
  58.         unpickled = pickle.loads(pickle.dumps(original))
    
  59. 
    
  60.         # Exceptions are not equal to equivalent instances of themselves, so
    
  61.         # can't just use assertEqual(original, unpickled)
    
  62.         self.assertEqual(original.__class__, unpickled.__class__)
    
  63.         self.assertEqual(original.args, unpickled.args)
    
  64. 
    
  65.     def test_doesnotexist_class(self):
    
  66.         klass = Event.DoesNotExist
    
  67.         self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
    
  68. 
    
  69.     def test_multipleobjectsreturned_class(self):
    
  70.         klass = Event.MultipleObjectsReturned
    
  71.         self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
    
  72. 
    
  73.     def test_forward_relatedobjectdoesnotexist_class(self):
    
  74.         # ForwardManyToOneDescriptor
    
  75.         klass = Event.group.RelatedObjectDoesNotExist
    
  76.         self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
    
  77.         # ForwardOneToOneDescriptor
    
  78.         klass = Happening.event.RelatedObjectDoesNotExist
    
  79.         self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
    
  80. 
    
  81.     def test_reverse_one_to_one_relatedobjectdoesnotexist_class(self):
    
  82.         klass = Event.happening.RelatedObjectDoesNotExist
    
  83.         self.assertIs(pickle.loads(pickle.dumps(klass)), klass)
    
  84. 
    
  85.     def test_manager_pickle(self):
    
  86.         pickle.loads(pickle.dumps(Happening.objects))
    
  87. 
    
  88.     def test_model_pickle(self):
    
  89.         """
    
  90.         A model not defined on module level is picklable.
    
  91.         """
    
  92.         original = Container.SomeModel(pk=1)
    
  93.         dumped = pickle.dumps(original)
    
  94.         reloaded = pickle.loads(dumped)
    
  95.         self.assertEqual(original, reloaded)
    
  96.         # Also, deferred dynamic model works
    
  97.         Container.SomeModel.objects.create(somefield=1)
    
  98.         original = Container.SomeModel.objects.defer("somefield")[0]
    
  99.         dumped = pickle.dumps(original)
    
  100.         reloaded = pickle.loads(dumped)
    
  101.         self.assertEqual(original, reloaded)
    
  102.         self.assertEqual(original.somefield, reloaded.somefield)
    
  103. 
    
  104.     def test_model_pickle_m2m(self):
    
  105.         """
    
  106.         Test intentionally the automatically created through model.
    
  107.         """
    
  108.         m1 = M2MModel.objects.create()
    
  109.         g1 = Group.objects.create(name="foof")
    
  110.         m1.groups.add(g1)
    
  111.         m2m_through = M2MModel._meta.get_field("groups").remote_field.through
    
  112.         original = m2m_through.objects.get()
    
  113.         dumped = pickle.dumps(original)
    
  114.         reloaded = pickle.loads(dumped)
    
  115.         self.assertEqual(original, reloaded)
    
  116. 
    
  117.     def test_model_pickle_dynamic(self):
    
  118.         class Meta:
    
  119.             proxy = True
    
  120. 
    
  121.         dynclass = type(
    
  122.             "DynamicEventSubclass",
    
  123.             (Event,),
    
  124.             {"Meta": Meta, "__module__": Event.__module__},
    
  125.         )
    
  126.         original = dynclass(pk=1)
    
  127.         dumped = pickle.dumps(original)
    
  128.         reloaded = pickle.loads(dumped)
    
  129.         self.assertEqual(original, reloaded)
    
  130.         self.assertIs(reloaded.__class__, dynclass)
    
  131. 
    
  132.     def test_specialized_queryset(self):
    
  133.         self.assert_pickles(Happening.objects.values("name"))
    
  134.         self.assert_pickles(Happening.objects.values("name").dates("when", "year"))
    
  135.         # With related field (#14515)
    
  136.         self.assert_pickles(
    
  137.             Event.objects.select_related("group")
    
  138.             .order_by("title")
    
  139.             .values_list("title", "group__name")
    
  140.         )
    
  141. 
    
  142.     def test_pickle_prefetch_related_idempotence(self):
    
  143.         g = Group.objects.create(name="foo")
    
  144.         groups = Group.objects.prefetch_related("event_set")
    
  145. 
    
  146.         # First pickling
    
  147.         groups = pickle.loads(pickle.dumps(groups))
    
  148.         self.assertSequenceEqual(groups, [g])
    
  149. 
    
  150.         # Second pickling
    
  151.         groups = pickle.loads(pickle.dumps(groups))
    
  152.         self.assertSequenceEqual(groups, [g])
    
  153. 
    
  154.     def test_pickle_prefetch_queryset_usable_outside_of_prefetch(self):
    
  155.         # Prefetch shouldn't affect the fetch-on-pickle behavior of the
    
  156.         # queryset passed to it.
    
  157.         Group.objects.create(name="foo")
    
  158.         events = Event.objects.order_by("id")
    
  159.         Group.objects.prefetch_related(models.Prefetch("event_set", queryset=events))
    
  160.         with self.assertNumQueries(1):
    
  161.             events2 = pickle.loads(pickle.dumps(events))
    
  162.         with self.assertNumQueries(0):
    
  163.             list(events2)
    
  164. 
    
  165.     def test_pickle_prefetch_queryset_still_usable(self):
    
  166.         g = Group.objects.create(name="foo")
    
  167.         groups = Group.objects.prefetch_related(
    
  168.             models.Prefetch("event_set", queryset=Event.objects.order_by("id"))
    
  169.         )
    
  170.         groups2 = pickle.loads(pickle.dumps(groups))
    
  171.         self.assertSequenceEqual(groups2.filter(id__gte=0), [g])
    
  172. 
    
  173.     def test_pickle_prefetch_queryset_not_evaluated(self):
    
  174.         Group.objects.create(name="foo")
    
  175.         groups = Group.objects.prefetch_related(
    
  176.             models.Prefetch("event_set", queryset=Event.objects.order_by("id"))
    
  177.         )
    
  178.         list(groups)  # evaluate QuerySet
    
  179.         with self.assertNumQueries(0):
    
  180.             pickle.loads(pickle.dumps(groups))
    
  181. 
    
  182.     def test_pickle_prefetch_related_with_m2m_and_objects_deletion(self):
    
  183.         """
    
  184.         #24831 -- Cached properties on ManyToOneRel created in QuerySet.delete()
    
  185.         caused subsequent QuerySet pickling to fail.
    
  186.         """
    
  187.         g = Group.objects.create(name="foo")
    
  188.         m2m = M2MModel.objects.create()
    
  189.         m2m.groups.add(g)
    
  190.         Group.objects.all().delete()
    
  191. 
    
  192.         m2ms = M2MModel.objects.prefetch_related("groups")
    
  193.         m2ms = pickle.loads(pickle.dumps(m2ms))
    
  194.         self.assertSequenceEqual(m2ms, [m2m])
    
  195. 
    
  196.     def test_pickle_boolean_expression_in_Q__queryset(self):
    
  197.         group = Group.objects.create(name="group")
    
  198.         Event.objects.create(title="event", group=group)
    
  199.         groups = Group.objects.filter(
    
  200.             models.Q(
    
  201.                 models.Exists(
    
  202.                     Event.objects.filter(group_id=models.OuterRef("id")),
    
  203.                 )
    
  204.             ),
    
  205.         )
    
  206.         groups2 = pickle.loads(pickle.dumps(groups))
    
  207.         self.assertSequenceEqual(groups2, [group])
    
  208. 
    
  209.     def test_pickle_exists_queryset_still_usable(self):
    
  210.         group = Group.objects.create(name="group")
    
  211.         Event.objects.create(title="event", group=group)
    
  212.         groups = Group.objects.annotate(
    
  213.             has_event=models.Exists(
    
  214.                 Event.objects.filter(group_id=models.OuterRef("id")),
    
  215.             ),
    
  216.         )
    
  217.         groups2 = pickle.loads(pickle.dumps(groups))
    
  218.         self.assertSequenceEqual(groups2.filter(has_event=True), [group])
    
  219. 
    
  220.     def test_pickle_exists_queryset_not_evaluated(self):
    
  221.         group = Group.objects.create(name="group")
    
  222.         Event.objects.create(title="event", group=group)
    
  223.         groups = Group.objects.annotate(
    
  224.             has_event=models.Exists(
    
  225.                 Event.objects.filter(group_id=models.OuterRef("id")),
    
  226.             ),
    
  227.         )
    
  228.         list(groups)  # evaluate QuerySet.
    
  229.         with self.assertNumQueries(0):
    
  230.             self.assert_pickles(groups)
    
  231. 
    
  232.     def test_pickle_exists_kwargs_queryset_not_evaluated(self):
    
  233.         group = Group.objects.create(name="group")
    
  234.         Event.objects.create(title="event", group=group)
    
  235.         groups = Group.objects.annotate(
    
  236.             has_event=models.Exists(
    
  237.                 queryset=Event.objects.filter(group_id=models.OuterRef("id")),
    
  238.             ),
    
  239.         )
    
  240.         list(groups)  # evaluate QuerySet.
    
  241.         with self.assertNumQueries(0):
    
  242.             self.assert_pickles(groups)
    
  243. 
    
  244.     def test_pickle_subquery_queryset_not_evaluated(self):
    
  245.         group = Group.objects.create(name="group")
    
  246.         Event.objects.create(title="event", group=group)
    
  247.         groups = Group.objects.annotate(
    
  248.             event_title=models.Subquery(
    
  249.                 Event.objects.filter(group_id=models.OuterRef("id")).values("title"),
    
  250.             ),
    
  251.         )
    
  252.         list(groups)  # evaluate QuerySet.
    
  253.         with self.assertNumQueries(0):
    
  254.             self.assert_pickles(groups)
    
  255. 
    
  256.     def test_pickle_filteredrelation(self):
    
  257.         group = Group.objects.create(name="group")
    
  258.         event_1 = Event.objects.create(title="Big event", group=group)
    
  259.         event_2 = Event.objects.create(title="Small event", group=group)
    
  260.         Happening.objects.bulk_create(
    
  261.             [
    
  262.                 Happening(event=event_1, number1=5),
    
  263.                 Happening(event=event_2, number1=3),
    
  264.             ]
    
  265.         )
    
  266.         groups = Group.objects.annotate(
    
  267.             big_events=models.FilteredRelation(
    
  268.                 "event",
    
  269.                 condition=models.Q(event__title__startswith="Big"),
    
  270.             ),
    
  271.         ).annotate(sum_number=models.Sum("big_events__happening__number1"))
    
  272.         groups_query = pickle.loads(pickle.dumps(groups.query))
    
  273.         groups = Group.objects.all()
    
  274.         groups.query = groups_query
    
  275.         self.assertEqual(groups.get().sum_number, 5)
    
  276. 
    
  277.     def test_pickle_filteredrelation_m2m(self):
    
  278.         group = Group.objects.create(name="group")
    
  279.         m2mmodel = M2MModel.objects.create(added=datetime.date(2020, 1, 1))
    
  280.         m2mmodel.groups.add(group)
    
  281.         groups = Group.objects.annotate(
    
  282.             first_m2mmodels=models.FilteredRelation(
    
  283.                 "m2mmodel",
    
  284.                 condition=models.Q(m2mmodel__added__year=2020),
    
  285.             ),
    
  286.         ).annotate(count_groups=models.Count("first_m2mmodels__groups"))
    
  287.         groups_query = pickle.loads(pickle.dumps(groups.query))
    
  288.         groups = Group.objects.all()
    
  289.         groups.query = groups_query
    
  290.         self.assertEqual(groups.get().count_groups, 1)
    
  291. 
    
  292.     def test_annotation_with_callable_default(self):
    
  293.         # Happening.when has a callable default of datetime.datetime.now.
    
  294.         qs = Happening.objects.annotate(latest_time=models.Max("when"))
    
  295.         self.assert_pickles(qs)
    
  296. 
    
  297.     def test_annotation_values(self):
    
  298.         qs = Happening.objects.values("name").annotate(latest_time=models.Max("when"))
    
  299.         reloaded = Happening.objects.all()
    
  300.         reloaded.query = pickle.loads(pickle.dumps(qs.query))
    
  301.         self.assertEqual(
    
  302.             reloaded.get(),
    
  303.             {"name": "test", "latest_time": self.happening.when},
    
  304.         )
    
  305. 
    
  306.     def test_annotation_values_list(self):
    
  307.         # values_list() is reloaded to values() when using a pickled query.
    
  308.         tests = [
    
  309.             Happening.objects.values_list("name"),
    
  310.             Happening.objects.values_list("name", flat=True),
    
  311.             Happening.objects.values_list("name", named=True),
    
  312.         ]
    
  313.         for qs in tests:
    
  314.             with self.subTest(qs._iterable_class.__name__):
    
  315.                 reloaded = Happening.objects.all()
    
  316.                 reloaded.query = pickle.loads(pickle.dumps(qs.query))
    
  317.                 self.assertEqual(reloaded.get(), {"name": "test"})
    
  318. 
    
  319.     def test_filter_deferred(self):
    
  320.         qs = Happening.objects.all()
    
  321.         qs._defer_next_filter = True
    
  322.         qs = qs.filter(id=0)
    
  323.         self.assert_pickles(qs)
    
  324. 
    
  325.     def test_missing_django_version_unpickling(self):
    
  326.         """
    
  327.         #21430 -- Verifies a warning is raised for querysets that are
    
  328.         unpickled without a Django version
    
  329.         """
    
  330.         qs = Group.missing_django_version_objects.all()
    
  331.         msg = "Pickled queryset instance's Django version is not specified."
    
  332.         with self.assertRaisesMessage(RuntimeWarning, msg):
    
  333.             pickle.loads(pickle.dumps(qs))
    
  334. 
    
  335.     def test_unsupported_unpickle(self):
    
  336.         """
    
  337.         #21430 -- Verifies a warning is raised for querysets that are
    
  338.         unpickled with a different Django version than the current
    
  339.         """
    
  340.         qs = Group.previous_django_version_objects.all()
    
  341.         msg = (
    
  342.             "Pickled queryset instance's Django version 1.0 does not match "
    
  343.             "the current version %s." % django.__version__
    
  344.         )
    
  345.         with self.assertRaisesMessage(RuntimeWarning, msg):
    
  346.             pickle.loads(pickle.dumps(qs))
    
  347. 
    
  348.     def test_order_by_model_with_abstract_inheritance_and_meta_ordering(self):
    
  349.         group = Group.objects.create(name="test")
    
  350.         event = MyEvent.objects.create(title="test event", group=group)
    
  351.         event.edition_set.create()
    
  352.         self.assert_pickles(event.edition_set.order_by("event"))
    
  353. 
    
  354. 
    
  355. class InLookupTests(TestCase):
    
  356.     @classmethod
    
  357.     def setUpTestData(cls):
    
  358.         for i in range(1, 3):
    
  359.             group = Group.objects.create(name="Group {}".format(i))
    
  360.         cls.e1 = Event.objects.create(title="Event 1", group=group)
    
  361. 
    
  362.     def test_in_lookup_queryset_evaluation(self):
    
  363.         """
    
  364.         Neither pickling nor unpickling a QuerySet.query with an __in=inner_qs
    
  365.         lookup should evaluate inner_qs.
    
  366.         """
    
  367.         events = Event.objects.filter(group__in=Group.objects.all())
    
  368. 
    
  369.         with self.assertNumQueries(0):
    
  370.             dumped = pickle.dumps(events.query)
    
  371. 
    
  372.         with self.assertNumQueries(0):
    
  373.             reloaded = pickle.loads(dumped)
    
  374.             reloaded_events = Event.objects.none()
    
  375.             reloaded_events.query = reloaded
    
  376. 
    
  377.         self.assertSequenceEqual(reloaded_events, [self.e1])
    
  378. 
    
  379.     def test_in_lookup_query_evaluation(self):
    
  380.         events = Event.objects.filter(group__in=Group.objects.values("id").query)
    
  381. 
    
  382.         with self.assertNumQueries(0):
    
  383.             dumped = pickle.dumps(events.query)
    
  384. 
    
  385.         with self.assertNumQueries(0):
    
  386.             reloaded = pickle.loads(dumped)
    
  387.             reloaded_events = Event.objects.none()
    
  388.             reloaded_events.query = reloaded
    
  389. 
    
  390.         self.assertSequenceEqual(reloaded_events, [self.e1])