Dušan Maďar
Dušan Maďar

Reputation: 9909

Django & mypy: ValuesQuerySet type hint

What type hint to use for a function which returns a queryset like the one below?

def _get_cars_for_validation(filter_: dict) -> QuerySet:
    return (
        Car.objects.filter(**filter_)
        .values("id", "brand", "engine")
        .order_by("id")
    )

mypy returns an error

Incompatible return value type (got "ValuesQuerySet[Car, TypedDict({'id': int, 'brand': str, 'engine': str})]", expected "QuerySet[Any]")

I would use ValuesQuerySet but it was removed in Django 1.9. ValuesQuerySet reported by mypy comes from 3rd party lib django-stubs (and I am unable to import it; is it actually possible?).

Upvotes: 10

Views: 5424

Answers (2)

Stefan_EOX
Stefan_EOX

Reputation: 1531

You can also cast to QuerySet:

from typing import cast

from django.db.models import QuerySet

return (
        cast(QuerySet, Car.objects.filter(**filter_))
        .values("id", "brand", "engine")
        .order_by("id")
    )

Upvotes: 2

natka_m
natka_m

Reputation: 1622

I had exactly the same problem, and I found a solution in a GitHub issue:

import typing

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

def _get_cars_for_validation(filter_: dict) -> 'ValuesQuerySet[Car, int]':
    return (
        Car.objects.filter(**filter_)
        .values("id", "brand", "engine")
        .order_by("id")
    )

The if typing.TYPE_CHECKING prevents the ImportError when you run the program, because the nonexistent ValuesQuerySet is only imported during the mypy check. Also note that the annotation has to be a string: 'ValuesQuerySet[Car, int]'.

The second argument to 'ValuesQuerySet[Car, int]' is a mystery to me; the OP of the issue used int "and it worked", in my case I tried a few other types, and all of them worked too; you may just as well use Any, I suppose.

You can use reveal_type() to inspect the type of the variable yourself.

Upvotes: 7

Related Questions