Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?).

like image 947
Dušan Maďar Avatar asked Feb 17 '20 22:02

Dušan Maďar


People also ask

What is the Django used for?

What is Django? Django is a high-level Python web framework that enables rapid development of secure and maintainable websites. Built by experienced developers, Django takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel.

Is Django same as Python?

Python and Django are intertwined but not the same. Python is a programming language that's used for many different applications: artificial intelligence, machine learning, desktop apps, etc. On the other hand, Django is a Python framework for full-stack web application development and server development.

Is Django backend or frontend?

Django and Ruby on Rails — two of the leading backend web development frameworks and both open source — have been on the scene since the mid-2000s. They're no longer the trendy young bucks in web development, but they are still the seventh and 11th most-loved web frameworks respectively.

What Django means?

The name Django is boy's name of Romani origin meaning "I awake". Django — the D is silent as most everyone now knows — the nickname of the great Belgian-born jazz guitarist Django (originally Jean Baptiste) Reinhardt, makes a dynamic musical choice for any jazz aficionado.


2 Answers

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.

like image 138
natka_m Avatar answered Sep 24 '22 19:09

natka_m


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")
    )
like image 33
Stefan_EOX Avatar answered Sep 22 '22 19:09

Stefan_EOX