django-stubs icon indicating copy to clipboard operation
django-stubs copied to clipboard

ValuesQuerySet is removed and not available

Open sobolevn opened this issue 5 years ago • 6 comments

I have tried to use ValuesQuerySet that is returned in:

def post_dates_by_author(author_id: int):
    """Returns published blog posts."""
    x = BlogPost.objects.filter(
        author__id=author_id,
    ).values_list('created_at', flat=True)
    reveal_type(x)
    return x

Reproduction link: https://github.com/sobolevn/django_stubs_example/blob/master/server/apps/main/logic/repo.py

Output:

» PYTHONPATH="$PYTHONPATH:$PWD" mypy server
server/apps/main/logic/repo.py:20: note: Revealed type is 'django.db.models.query.ValuesQuerySet[server.apps.main.models.BlogPost, datetime.datetime]'

But, it looks like ValuesQuerySet is removed in 1.9, and there's not such class: https://github.com/django/django/blob/aed89adad54a977829c4f180b036033e031ebcc7/docs/releases/1.9.txt#L1069

So, it is not clear how to annotate your function to return ValuesQuerySet when it is missing from django.

sobolevn avatar Aug 25 '19 18:08 sobolevn

I meant it to be stubs-only class, maybe I should've start it with _ or smth. Anyway, I haven't thought about the annotation of the return value... Is it possible to annotate it with the string based annotation, like 'ValuesQuerySet'? Will mypy be ok with that?

If it's not, then I guess the only way is to go back to the multi-parameter generics then... Support of the annotate() will require third generic param, and it's very unobvious to the users.

mkurnikov avatar Aug 25 '19 20:08 mkurnikov

That's my attempt:

import typing

if typing.TYPE_CHECKING:
    from django.db.models.query import ValuesQuerySet

def test() -> 'ValuesQuerySet[BlogPost]':
    return BlogPost.objects.all().values_list('id', flat=True)

Output:

» PYTHONPATH="$PYTHONPATH:$PWD" mypy server
server/apps/main/logic/repo.py:19: error: "ValuesQuerySet" expects 2 type arguments, but 1 given

I was not sure about the second argument. And I have tried int:

def test() -> 'ValuesQuerySet[BlogPost, int]':
    return BlogPost.objects.all().values_list('id', flat=True)

And it worked.

sobolevn avatar Aug 25 '19 22:08 sobolevn

Yes, this is kind of what I expected users to be doing. I wanted to make QuerySet a single argument type, as it's a most common case, and for other ones provide a special kinds of queryset classes.

Actually, for your case, in the wild, if you're going to return values_list from the function, it's probably going to be a Collection[...], not a QuerySet[...], and it will work with ValuesQuerySet without additional import. But if you do want to return values_list as QuerySet, it's case specific enough to spend some time learning how to do it.

We do need to add some docs for it, though.

mkurnikov avatar Aug 26 '19 00:08 mkurnikov

I have get away with this code in my case:

if typing.TYPE_CHECKING:
    from django.db.models.query import ValuesQuerySet
else:
    from typing import Dict as ValuesQuerySet
ENUM_CHOICES: ValuesQuerySet[AssetType, Tuple[str, str]] = AssetType.objects.values_list("name", "value")

But I consider it a very dirty hack.

PetrDlouhy avatar Jul 08 '20 16:07 PetrDlouhy

I tried with

from django.db import Model
from typing import Any, Dict, TypeAlias

ValuesQuerySet: TypeAlias[Model, Dict[str, Any]] = Dict

Is workings as expected for now, donno if is the best alternative but it's checking the argument types

nunesvictor avatar May 02 '23 23:05 nunesvictor

I found ValuesQuerySet in django_stubs_ext.aliases: https://github.com/typeddjango/django-stubs/blob/706f41f657bd6b92e0e4d8ec966e28d674787c6a/ext/django_stubs_ext/aliases.py#L9

Using that the following works:

from django_stubs_ext.aliases import ValuesQuerySet

def foo() -> ValuesQuerySet[MyModel, dict[str, Any]]:
    return MyModel.objects.values('field_one', 'field_two', 'another_field')

mschoettle avatar Nov 16 '23 22:11 mschoettle

Indeed as @mschoettle mentions, this has been solved for a long time, it has been publicly exported from django_stubs_ext.

And ValuesQuerySet is now being deprecated in favor of QuerySet[T, U].

  • https://github.com/typeddjango/django-stubs/discussions/2194

intgr avatar May 29 '24 09:05 intgr