How to use Python type hints with Django QuerySet?

后端 未结 9 882
一个人的身影
一个人的身影 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:48

    One solution may be using Union typing class.

    from typing import Union, List
    from django.db.models import QuerySet
    from my_app.models import MyModel
    
    def somefunc(row: Union[QuerySet, List[MyModel]]):
        pass
    

    Now when you slice the row argument it will know that the returned type is either another list of MyModel or an instance of MyModel, whilst also hinting that the methods of the QuerySet class are available on the row argument too.

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

    There's a special package called django-stubs (the name follows PEP561) to type your django code.

    That's how it works:

    # server/apps/main/views.py
    from django.http import HttpRequest, HttpResponse
    from django.shortcuts import render
    
    def index(request: HttpRequest) -> HttpResponse:
        reveal_type(request.is_ajax)
        reveal_type(request.user)
        return render(request, 'main/index.html')
    

    Output:

    » PYTHONPATH="$PYTHONPATH:$PWD" mypy server
    server/apps/main/views.py:14: note: Revealed type is 'def () -> builtins.bool'
    server/apps/main/views.py:15: note: Revealed type is 'django.contrib.auth.models.User'
    

    And with models and QuerySets:

    # server/apps/main/logic/repo.py
    from django.db.models.query import QuerySet
    
    from server.apps.main.models import BlogPost
    
    def published_posts() -> 'QuerySet[BlogPost]':  # works fine!
        return BlogPost.objects.filter(
            is_published=True,
        )
    

    Output:

    reveal_type(published_posts().first())
    # => Union[server.apps.main.models.BlogPost*, None]
    
    • Complete tutorial: https://sobolevn.me/2019/08/typechecking-django-and-drf
    • Types for django: https://github.com/typeddjango/django-stubs
    • Types for drf: https://github.com/typeddjango/djangorestframework-stubs
    0 讨论(0)
  • 2021-01-30 08:56

    You actually can do what you want if you import the annotations module:

    from __future__ import annotations
    from django.db import models
    from django.db.models.query import QuerySet
    
    class MyModel(models.Model):
        pass
    
    def my_function() -> QuerySet[MyModel]:
        return MyModel.objects.all()
    

    Neither MyPy nor the Python interpreter will complain or raise exceptions on this (tested on python 3.7). MyPy will probably be unable to type-check it, but if all you want is to document your return type, this should be good enough.

    0 讨论(0)
提交回复
热议问题