How to use Python type hints with Django QuerySet?

后端 未结 9 878
一个人的身影
一个人的身影 2021-01-30 08:25

Is it possible to specify type of records in Django QuerySet with Python type hints? Something like QuerySet[SomeModel]?

For example, we have model:

相关标签:
9条回答
  • 2021-01-30 08:34

    I made this helper class to get a generic type hint:

    from django.db.models import QuerySet
    from typing import Iterator, Union, TypeVar, Generic
    
    T = TypeVar("T")
    
    class ModelType(Generic[T]):
        def __iter__(self) -> Iterator[Union[T, QuerySet]]:
            pass
    

    Then use it like this:

    def somefunc(row: ModelType[SomeModel]):
        pass
    

    This reduces the noise everytime I use this type and it make it usable between models (like ModelType[DifferentModel]).

    0 讨论(0)
  • 2021-01-30 08:35

    I've found myself that using typing.Sequence to solve a similar issue:

    from typing import Sequence
    
    
    def print_emails(users: Sequence[User]):
        for user in users:
            print(user.email)
    
    
    users = User.objects.all()
    
    
    print_emails(users=users)
    

    As far as I know from docs:

    A Sequence is anything that supports len() and .getitem(), independent of its actual type.

    0 讨论(0)
  • 2021-01-30 08:39

    This is an improved helper class of Or Duan.

    from django.db.models import QuerySet
    from typing import Iterator, TypeVar, Generic
    
    _Z = TypeVar("_Z")  
    
    class QueryType(Generic[_Z], QuerySet):
        def __iter__(self) -> Iterator[_Z]: ...
    

    This class is used specifically for QuerySet object such as when you use filter in a query.
    Sample:

    from some_file import QueryType
    
    sample_query: QueryType[SampleClass] = SampleClass.objects.filter(name=name)
    

    Now the interpreter recognizes the sample_query as a QuerySet object and you will get suggestions such as count() and while looping through the objects, you will get suggestions for the SampleClass

    Note
    This format of type hinting is available from python3.6 onwards.


    You can also use django_hint which has hinting classes specifically for Django.

    0 讨论(0)
  • 2021-01-30 08:43

    IMHO, the proper way to do it is to define a type that inherits QuerySet and specify a generic return type for the iterator.

        from django.db.models import QuerySet
        from typing import Iterator, TypeVar, Generic, Optional
    
        T = TypeVar("T")
    
    
        class QuerySetType(Generic[T], QuerySet):  # QuerySet + Iterator
    
            def __iter__(self) -> Iterator[T]:
                pass
    
            def first(self) -> Optional[T]:
                pass
    
            # ... add more refinements
    
    
    

    Then you can use it like this:

    users: QuerySetType[User] = User.objects.all()
    for user in users:
       print(user.email)  # typing OK!
    user = users.first()  # typing OK!
    
    
    0 讨论(0)
  • 2021-01-30 08:44
    from typing import Iterable
    
    def func(queryset_or_list: Iterable[MyModel]): 
        pass
    

    Both of queryset and list of model instance is iterable object.

    0 讨论(0)
  • 2021-01-30 08:46
    from typing import (TypeVar, Generic, Iterable, Optional)
    from django.db.models import Model
    from django.db.models import QuerySet
    _T = TypeVar("_T", bound=Model)
    
    
    class QuerySetType(Generic[_T], QuerySet):
        def __iter__(self) -> Iterable[_T]:
            pass
    
        def first(self) -> Optional[_T]:
            pass
    
    0 讨论(0)
提交回复
热议问题