web-dev-qa-db-ja.com

Python QuerySetでDjangoタイプヒントを使用する方法

Djangoタイプヒントを使用してPython QuerySetのレコードタイプを指定することはできますか? QuerySet[SomeModel]のようなものですか?

たとえば、次のモデルがあります。

class SomeModel(models.Model):
    smth = models.IntegerField()

そして、そのモデルのQuerySetをfuncのparamとして渡します:

def somefunc(rows: QuerySet):
    pass

しかし、List[SomeModel]のように、QuerySetでレコードのタイプを指定する方法:

def somefunc(rows: List[SomeModel]):
    pass

しかし、QuerySetで?

1つの解決策は、Union型付けクラスを使用することです。

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

row引数をスライスすると、返された型がMyModelの別のリストまたはMyModelのインスタンスであることがわかります。また、QuerySetクラスのメソッドがrow引数も。

31
A. J. Parr

一般的な型のヒントを取得するために、このヘルパークラスを作成しました。

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

次に、次のように使用します。

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

これにより、このタイプを使用するたびにノイズが減少し、モデル間で使用可能になります(ModelType[DifferentModel])。

13
Or Duan

これは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]: ...
_

このクラスは、クエリでQuerySetを使用する場合など、filterオブジェクト専用に使用されます。
サンプル:

_from some_file import QueryType

sample_query: QueryType[SampleClass] = SampleClass.objects.filter(name=name)
_

インタープリターは_sample_query_をQuerySetオブジェクトとして認識し、count()などの提案を取得し、オブジェクトをループしながら、SampleClassの提案を取得します。


このタイプのヒンティングの形式は、_python3.6_以降で利用できます。


Django専用のヒントクラスを持つ Django_hint も使用できます。

2
Ramtin

私もこれに対する解決策を探しています。 Django Developersリストには thread があり、そこでそのような機能を実装する方法を議論しています。

現在、彼らは mypyのDjango拡張機能 を開発していますが、特定のリクエストに対しては運が悪いようです。 「Provably Never」という見出しの下のロードマップで:

クエリセットは部分的にサポートされている場合がありますが、複雑な引数(フィルタおよび取得クエリの引数など)またはQおよびFオブジェクトは、mypyの表現可能性を超えています。

それを述べると、条件が改善するまでプレーンole QuerySetを使用する必要があります。

2
Bobort

Djangoコードを入力するためのDjango-stubs(名前 PEP561に続く )と呼ばれる特別なパッケージがあります。

それがどのように機能するかです:

# 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')

出力:

» 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'

モデルと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,
    )

出力:

reveal_type(published_posts().first())
# => Union[server.apps.main.models.BlogPost*, None]
0
sobolevn