django-stubs
django-stubs copied to clipboard
ValuesQuerySet is removed and not available
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.
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.
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.
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.
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.
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
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')
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