Repository: adamchainz/django-perf-rec

Issue:
Perf rec seems to be messing up later annotates
This fails with `Cannot resolve keyword 'submitted' into field.`

```
from django.test import TestCase
from django_perf_rec import TestCaseMixin

class TestExportLogic(TestCaseMixin, TestCase):
    def test_b(self):
        with self.record_performance():
            pass

        models.Row.objects.annotate(
            submitted=F("state"),
            for_ron=F("submitted"),
        )
```

state is a legit field on Row

If you comment out the record_performance bit it passes.

Django==3.1.4
django-perf-rec==4.10.0
Python 3.8.6
psycopg2==2.8.6


Discussion:
```
python manage.py test approval.tests.TestExportLogic
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_b (approval.tests.TestExportLogic)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<redacted>\tests.py", line 2127, in test_b
    models.Row.objects.annotate(
  File "<redacted>\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "<patchy>", line 36, in annotate
  File "<redacted>\site-packages\django\db\models\sql\query.py", line 1018, in add_annotation
    annotation = annotation.resolve_expression(self, allow_joins=True, reuse=None,
  File "<redacted>\site-packages\django\db\models\expressions.py", line 539, in resolve_expression
    return query.resolve_ref(self.name, allow_joins, reuse, summarize)
  File "<redacted>\site-packages\django\db\models\sql\query.py", line 1683, in resolve_ref
    join_info = self.setup_joins(field_list, self.get_meta(), self.get_initial_alias(), can_reuse=reuse)
  File "<redacted>\site-packages\django\db\models\sql\query.py", line 1565, in setup_joins
    path, final_field, targets, rest = self.names_to_path(
  File "<redacted>\site-packages\django\db\models\sql\query.py", line 1481, in names_to_path
    raise FieldError("Cannot resolve keyword '%s' into field. "
django.core.exceptions.FieldError: Cannot resolve keyword 'submitted' into field. Choices are: <redacted>

```

Resolving patch:
diff --git a/src/django_perf_rec/db.py b/src/django_perf_rec/db.py
--- a/src/django_perf_rec/db.py
+++ b/src/django_perf_rec/db.py
@@ -5,7 +5,6 @@
 from django.db import DEFAULT_DB_ALIAS, connections
 
 from django_perf_rec.operation import AllSourceRecorder, Operation
-from django_perf_rec.orm import patch_ORM_to_be_deterministic
 from django_perf_rec.settings import perf_rec_settings
 from django_perf_rec.sql import sql_fingerprint
 
@@ -36,8 +35,6 @@ def __enter__(self):
         library. Here we wrap this function on the connection to grab the SQL
         as it comes out.
         """
-        patch_ORM_to_be_deterministic()
-
         connection = connections[self.alias]
         self.orig_force_debug_cursor = connection.force_debug_cursor
         connection.force_debug_cursor = True
diff --git a/src/django_perf_rec/orm.py b/src/django_perf_rec/orm.py
deleted file mode 100644
--- a/src/django_perf_rec/orm.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import patchy
-from django.db.models.query import QuerySet
-from django.db.models.sql.query import Query
-
-
-def patch_ORM_to_be_deterministic():
-    """
-    Django's ORM is non-deterministic with regards to the queries it outputs
-    for e.g. OR clauses. We need it to be deterministic so that we can compare
-    queries between runs, so we make a couple patches to its internals to do
-    this. Mostly this is done by adding sorted() in some places so we're not
-    affected by the vagaries of random dict iteration order.
-
-    There is no undo for this, but it doesn't make the ORM much slower or
-    anything bad.
-    """
-    if patch_ORM_to_be_deterministic.have_patched:
-        return
-    patch_ORM_to_be_deterministic.have_patched = True
-
-    patch_QuerySet()
-    patch_Query()
-
-
-patch_ORM_to_be_deterministic.have_patched = False
-
-
-def patch_QuerySet():
-    patchy.patch(
-        QuerySet.annotate,
-        """\
-        @@ -17,7 +17,7 @@
-                 except (AttributeError, TypeError):
-                     raise TypeError("Complex annotations require an alias")
-                 annotations[arg.default_alias] = arg
-        -    annotations.update(kwargs)
-        +    annotations.update(sorted(kwargs.items()))
-
-             clone = self._clone()
-             names = self._fields
-    """,
-    )
-
-
-def patch_Query():
-    patchy.patch(
-        Query.add_extra,
-        """\
-        @@ -13,7 +13,7 @@
-                     param_iter = iter(select_params)
-                 else:
-                     param_iter = iter([])
-        -        for name, entry in select.items():
-        +        for name, entry in sorted(select.items()):
-                     entry = str(entry)
-                     entry_params = []
-                     pos = entry.find("%s")
-    """,
-    )


Test patch:
diff --git a/tests/test_api.perf.yml b/tests/test_api.perf.yml
--- a/tests/test_api.perf.yml
+++ b/tests/test_api.perf.yml
@@ -3,6 +3,8 @@ RecordTests.test_delete_on_cascade_called_twice:
 - db: DELETE FROM "testapp_award" WHERE "testapp_award"."author_id" IN (#)
 - db: DELETE FROM "testapp_contract_author" WHERE "testapp_contract_author"."author_id" IN (#)
 - db: DELETE FROM "testapp_author" WHERE "testapp_author"."id" IN (#)
+RecordTests.test_dependent_QuerySet_annotate:
+- db: SELECT ... FROM "testapp_author"
 RecordTests.test_get_or_set:
 - cache|get_or_set: foo
 RecordTests.test_multiple_cache_ops:
diff --git a/tests/test_api.py b/tests/test_api.py
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -2,7 +2,7 @@
 
 import pytest
 from django.core.cache import caches
-from django.db.models import Q
+from django.db.models import F, Q
 from django.db.models.functions import Upper
 from django.test import SimpleTestCase, TestCase, override_settings
 
@@ -57,6 +57,10 @@ def test_non_deterministic_QuerySet_annotate(self):
         with record():
             list(Author.objects.annotate(x=Upper("name"), y=Upper("name")))
 
+    def test_dependent_QuerySet_annotate(self):
+        with record():
+            list(Author.objects.annotate(y=Upper("name"), x=F("y")))
+
     def test_non_deterministic_QuerySet_extra(self):
         with record():
             list(Author.objects.extra(select={"x": "1", "y": "1"}))
diff --git a/tests/test_orm.py b/tests/test_orm.py
deleted file mode 100644
--- a/tests/test_orm.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.test import SimpleTestCase
-
-from django_perf_rec.orm import patch_ORM_to_be_deterministic
-
-
-class PatchORMToBeDeterministicTests(SimpleTestCase):
-    def test_call_it(self):
-        patch_ORM_to_be_deterministic()
-
-    def test_call_it_again(self):
-        patch_ORM_to_be_deterministic()