Repository: typeddjango/django-stubs

Issue:
models values_list unexpected type warning
using values_list() on django models gets unexpected type(s) warning 


Discussion:
More details? 
cc @syastrov 
![image](https://user-images.githubusercontent.com/26543542/54091628-73569200-43ac-11e9-9bf9-67061db2b8d2.png)

What about mypy, does it show any errors? mypy output is a reference for this project, pycharm can have it's own bugs. 
I think it is due to use of Literal types, which is a mypy extension and also highly experimental according to their docs.
In the interest of compatibility, and because we will probably make values_list smarter in the future, I will make a PR to implement the different return types dependent on parameters in the plugin instead.

Resolving patch:
diff --git a/mypy_django_plugin/main.py b/mypy_django_plugin/main.py
--- a/mypy_django_plugin/main.py
+++ b/mypy_django_plugin/main.py
@@ -229,6 +229,38 @@ def extract_proper_type_for_get_form(ctx: MethodContext) -> Type:
     return ctx.default_return_type
 
 
+def extract_proper_type_for_values_list(ctx: MethodContext) -> Type:
+    object_type = ctx.type
+    if not isinstance(object_type, Instance):
+        return ctx.default_return_type
+
+    flat = helpers.parse_bool(helpers.get_argument_by_name(ctx, 'flat'))
+    named = helpers.parse_bool(helpers.get_argument_by_name(ctx, 'named'))
+
+    ret = ctx.default_return_type
+
+    any_type = AnyType(TypeOfAny.implementation_artifact)
+    if named and flat:
+        ctx.api.fail("'flat' and 'named' can't be used together.", ctx.context)
+        return ret
+    elif named:
+        # TODO: Fill in namedtuple fields/types
+        row_arg = ctx.api.named_generic_type('typing.NamedTuple', [])
+    elif flat:
+        # TODO: Figure out row_arg type dependent on the argument passed in
+        if len(ctx.args[0]) > 1:
+            ctx.api.fail("'flat' is not valid when values_list is called with more than one field.", ctx.context)
+            return ret
+        row_arg = any_type
+    else:
+        # TODO: Figure out tuple argument types dependent on the arguments passed in
+        row_arg = ctx.api.named_generic_type('builtins.tuple', [any_type])
+
+    first_arg = ret.args[0] if len(ret.args) > 0 else any_type
+    new_type_args = [first_arg, row_arg]
+    return helpers.reparametrize_instance(ret, new_type_args)
+
+
 class DjangoPlugin(Plugin):
     def __init__(self, options: Options) -> None:
         super().__init__(options)
@@ -318,6 +350,11 @@ def get_method_hook(self, fullname: str
             if sym and isinstance(sym.node, TypeInfo) and sym.node.has_base(helpers.FORM_MIXIN_CLASS_FULLNAME):
                 return extract_proper_type_for_get_form
 
+        if method_name == 'values_list':
+            sym = self.lookup_fully_qualified(class_name)
+            if sym and isinstance(sym.node, TypeInfo) and sym.node.has_base(helpers.QUERYSET_CLASS_FULLNAME):
+                return extract_proper_type_for_values_list
+
         if fullname in {'django.apps.registry.Apps.get_model',
                         'django.db.migrations.state.StateApps.get_model'}:
             return determine_model_cls_from_string_for_migrations


Test patch:
diff --git a/test-data/typecheck/queryset.test b/test-data/typecheck/queryset.test
--- a/test-data/typecheck/queryset.test
+++ b/test-data/typecheck/queryset.test
@@ -13,6 +13,8 @@ class BlogQuerySet(models.QuerySet[Blog]):
 blog_qs: models.QuerySet[Blog]
 reveal_type(blog_qs) # E: Revealed type is 'django.db.models.query.QuerySet[main.Blog, main.Blog]'
 
+Blog.objects.values_list('id', flat=True, named=True) # E: 'flat' and 'named' can't be used together.
+Blog.objects.values_list('id', 'extra_arg', flat=True) # E: 'flat' is not valid when values_list is called with more than one field.
 
 reveal_type(Blog.objects.in_bulk([1])) # E: Revealed type is 'builtins.dict[Any, main.Blog*]'
 reveal_type(Blog.objects.in_bulk()) # E: Revealed type is 'builtins.dict[Any, main.Blog*]'