From 2525b5b89fb69280d5e5b55d258c6cd46461ef25 Mon Sep 17 00:00:00 2001
From: Oleg Nykolyn <juravel2@gmail.com>
Date: Sat, 26 Jan 2019 16:32:46 +0200
Subject: [PATCH 1/4] Add missing re-imports to django.db.models.

---
 django-stubs/db/models/__init__.pyi | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/django-stubs/db/models/__init__.pyi b/django-stubs/db/models/__init__.pyi
index 691d945f2..0ccd8ace8 100644
--- a/django-stubs/db/models/__init__.pyi
+++ b/django-stubs/db/models/__init__.pyi
@@ -1,6 +1,15 @@
 from .base import Model as Model
 
-from .aggregates import Aggregate as Aggregate, Sum as Sum, Variance as Variance, Count as Count, Max as Max
+from .aggregates import (
+    Aggregate as Aggregate,
+    Avg as Avg,
+    Count as Count,
+    Max as Max,
+    Min as Min,
+    StdDev as StdDev,
+    Sum as Sum,
+    Variance as Variance,
+)
 
 from .fields import (
     AutoField as AutoField,
@@ -46,7 +55,12 @@ from .deletion import (
     PROTECT as PROTECT,
 )
 
-from .query import QuerySet as QuerySet, RawQuerySet as RawQuerySet
+from .query import (
+    Prefetch as Prefetch,
+    QuerySet as QuerySet,
+    RawQuerySet as RawQuerySet,
+    prefetch_related_objects as prefetch_related_objects,
+)
 
 from .query_utils import Q as Q, FilteredRelation as FilteredRelation
 

From 255e4af9117a4ba44f31370a60e3d3979147bc4c Mon Sep 17 00:00:00 2001
From: Oleg Nykolyn <juravel2@gmail.com>
Date: Thu, 31 Jan 2019 20:29:15 +0200
Subject: [PATCH 2/4] Add annotations for Prefetch, prefetch_related_objects.

---
 django-stubs/db/models/query.pyi | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/django-stubs/db/models/query.pyi b/django-stubs/db/models/query.pyi
index 6ee30aa1e..5dec09d7d 100644
--- a/django-stubs/db/models/query.pyi
+++ b/django-stubs/db/models/query.pyi
@@ -122,3 +122,25 @@ class RawQuerySet(Iterable[_T], Sized):
     def prefetch_related(self, *lookups: Any) -> RawQuerySet[_T]: ...
     def resolve_model_init_order(self) -> Tuple[List[str], List[int], List[Tuple[str, int]]]: ...
     def using(self, alias: Optional[str]) -> RawQuerySet[_T]: ...
+
+class Prefetch(object):
+    def __init__(self, lookup, queryset=None, to_attr=None):
+        # `prefetch_through` is the path we traverse to perform the prefetch.
+        self.prefetch_through = lookup
+        # `prefetch_to` is the path to the attribute that stores the result.
+        self.prefetch_to = lookup
+        if queryset is not None and not issubclass(queryset._iterable_class, ModelIterable):
+            raise ValueError('Prefetch querysets cannot use values().')
+        if to_attr:
+            self.prefetch_to = LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[:-1] + [to_attr])
+
+        self.queryset = queryset
+        self.to_attr = to_attr
+
+    def __getstate__(self) -> Dict[str, Any]: ...
+    def add_prefix(self, prefix: str) -> None: ...
+    def get_current_prefetch_to(self, level: int) -> str: ...
+    def get_current_to_attr(self, level: int) -> Tuple[str,str]: ...
+    def get_current_queryset(self, level) -> Optional[QuerySet]: ...
+
+def prefetch_related_objects(model_instances: Iterable[_T], *related_lookups: str) -> None: ...

From 3ccecebe3fcbc27cafee8c63fa586019befb7251 Mon Sep 17 00:00:00 2001
From: Oleg Nykolyn <juravel2@gmail.com>
Date: Thu, 31 Jan 2019 21:35:35 +0200
Subject: [PATCH 3/4] Fix and uncomment tests.

---
 django-stubs/db/models/query.pyi | 19 +++++--------------
 scripts/typecheck_tests.py       |  4 +++-
 2 files changed, 8 insertions(+), 15 deletions(-)

diff --git a/django-stubs/db/models/query.pyi b/django-stubs/db/models/query.pyi
index f62929fd8..47d38f2b7 100644
--- a/django-stubs/db/models/query.pyi
+++ b/django-stubs/db/models/query.pyi
@@ -144,26 +144,17 @@ class RawQuerySet(Iterable[_T], Sized):
     def using(self, alias: Optional[str]) -> RawQuerySet[_T]: ...
 
 class Prefetch(object):
-    def __init__(self, lookup, queryset=None, to_attr=None):
-        # `prefetch_through` is the path we traverse to perform the prefetch.
-        self.prefetch_through = lookup
-        # `prefetch_to` is the path to the attribute that stores the result.
-        self.prefetch_to = lookup
-        if queryset is not None and not issubclass(queryset._iterable_class, ModelIterable):
-            raise ValueError('Prefetch querysets cannot use values().')
-        if to_attr:
-            self.prefetch_to = LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[:-1] + [to_attr])
-
-        self.queryset = queryset
-        self.to_attr = to_attr
-
+    def __init__(self, lookup: str, queryset: Optional[QuerySet]=None, to_attr: Optional[str]=None) -> None: ...
     def __getstate__(self) -> Dict[str, Any]: ...
     def add_prefix(self, prefix: str) -> None: ...
     def get_current_prefetch_to(self, level: int) -> str: ...
     def get_current_to_attr(self, level: int) -> Tuple[str,str]: ...
     def get_current_queryset(self, level) -> Optional[QuerySet]: ...
 
-def prefetch_related_objects(model_instances: Iterable[_T], *related_lookups: str) -> None: ...
+def prefetch_related_objects(model_instances: Iterable[_T], *related_lookups: Union[str, Prefetch]) -> None: ...
+def get_prefetcher(instance: _T, through_attr: str, to_attr: str) -> Tuple[Any, Any, bool, bool]: ...
+
+class ModelIterable(Iterable[_T]): ...
 
 class InstanceCheckMeta(type): ...
 class EmptyQuerySet(metaclass=InstanceCheckMeta): ...
diff --git a/scripts/typecheck_tests.py b/scripts/typecheck_tests.py
index 1b10eb7ba..7f2720491 100644
--- a/scripts/typecheck_tests.py
+++ b/scripts/typecheck_tests.py
@@ -55,6 +55,8 @@
     'Argument 1 to "bytes"',
     '"full_clean" of "Model" does not return a value',
     '"object" not callable',
+    'Item "GenericForeignKey" of "Union[GenericForeignKey, Model, None]" has no attribute "read_by"',
+    'Item "Model" of "Union[GenericForeignKey, Model, None]" has no attribute "read_by"',
     re.compile('Cannot determine type of \'(objects|stuff|specimens|normal_manager)\''),
     re.compile(r'"Callable\[\[(Any(, )?)+\], Any\]" has no attribute'),
     re.compile(r'"HttpResponseBase" has no attribute "[A-Za-z_]+"'),
@@ -215,9 +217,9 @@
     'or_lookups',
     'order_with_respect_to',
     'ordering',
+    'prefetch_related',
     'pagination',
     # TODO: 'postgres_tests',
-    # TODO: 'prefetch_related',
     'project_template',
     'properties',
     'proxy_model_inheritance',

From 7a7f68ede4d52705d0d8bc4a14001deec037d84f Mon Sep 17 00:00:00 2001
From: Oleg Nykolyn <juravel2@gmail.com>
Date: Thu, 31 Jan 2019 21:44:32 +0200
Subject: [PATCH 4/4] Fix formatting.

---
 django-stubs/db/models/query.pyi | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/django-stubs/db/models/query.pyi b/django-stubs/db/models/query.pyi
index 47d38f2b7..23ab5cfbc 100644
--- a/django-stubs/db/models/query.pyi
+++ b/django-stubs/db/models/query.pyi
@@ -144,17 +144,16 @@ class RawQuerySet(Iterable[_T], Sized):
     def using(self, alias: Optional[str]) -> RawQuerySet[_T]: ...
 
 class Prefetch(object):
-    def __init__(self, lookup: str, queryset: Optional[QuerySet]=None, to_attr: Optional[str]=None) -> None: ...
+    def __init__(self, lookup: str, queryset: Optional[QuerySet] = None, to_attr: Optional[str] = None) -> None: ...
     def __getstate__(self) -> Dict[str, Any]: ...
     def add_prefix(self, prefix: str) -> None: ...
     def get_current_prefetch_to(self, level: int) -> str: ...
-    def get_current_to_attr(self, level: int) -> Tuple[str,str]: ...
+    def get_current_to_attr(self, level: int) -> Tuple[str, str]: ...
     def get_current_queryset(self, level) -> Optional[QuerySet]: ...
 
 def prefetch_related_objects(model_instances: Iterable[_T], *related_lookups: Union[str, Prefetch]) -> None: ...
 def get_prefetcher(instance: _T, through_attr: str, to_attr: str) -> Tuple[Any, Any, bool, bool]: ...
 
 class ModelIterable(Iterable[_T]): ...
-
 class InstanceCheckMeta(type): ...
 class EmptyQuerySet(metaclass=InstanceCheckMeta): ...