장고 쿼리셋과 함께 파이썬 타입 힌트를 사용하는 방법?
파이썬 타입 힌트로 장고 쿼리셋에서 레코드 타입을 지정할 수 있나요?뭐 이런 거.QuerySet[SomeModel]?
예를 들어, 다음과 같은 모델이 있습니다.
class SomeModel(models.Model):
smth = models.IntegerField()
그리고 이 모델의 QuerySet을 func의 param으로 전달하고자 합니다.
def somefunc(rows: QuerySet):
pass
그러나 QuerySet에서 레코드 유형을 지정하는 방법(예:List[SomeModel]:
def somefunc(rows: List[SomeModel]):
pass
QuerySet으로?
하나의 해결책은 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
이제 당신이 자를 때.row그것은 반환된 유형이 MyModel의 다른 목록 또는 MyModel의 인스턴스라는 것을 알 것이고, 또한 방법을 암시할 것입니다.QuerySet수업은 에서 가능합니다.row논쟁도.
라는 특별한 소포가 있습니다.django-stubs(이름 뒤에 ) 입력합니다.django암호를 매기다
이렇게 작동합니다.
# 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]
- 자습서 작성하기: https://sobolevn.me/2019/08/typechecking-django-and-drf
- 유형:
django: https://github.com/typeddjango/django-stubs - 유형:
drf: https://github.com/typeddjango/djangorestframework-stubs
일반적인 유형 힌트를 얻기 위해 이 도우미 클래스를 만들었습니다.
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]).
업데이트 2023
django_hint 0.3.0에서는 모델과 공통 함수의 반환 유형을 확장할 수 있습니다.objects예를 들어filter,get, 등이 자동으로 감지됩니다.
from django_hint import StandardModelType
class SampleModel(models.Model, StandardModelType['SampleModel']):
name: str = models.CharField(max_length=300)
원답
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로서QuerySetobject 그러면 다음과 같은 제안을 받을 것입니다.count()그리고 객체들을 순환하는 동안, 당신은 제안들을 얻을 것입니다.SampleClass
메모
이 형식의 유형 암시는 다음에서 사용할 수 있습니다.python3.6앞으로
또한 Django에 대한 힌트 클래스가 있는 django_hint를 사용할 수 있습니다.
면책 사항:저는 의 저자입니다.django_hint
QuerySet는 모든 모델의 쿼리 세트를 반환하는 함수/ method에 적합한 접근 방식입니다.장고 쿼리 집합은 반복 가능합니다.하지만 반품 유형이 한 모델에 매우 특정적인 경우 사용하는 것이 더 나을 수 있습니다.QuerySet[Model]위에QuerySet.
예:회사의 모든 활성 사용자 필터링
import datetime
from django.utils import timezone
from myapp.models import User
from collections.abc import Iterable
def get_active_users(company_id: int) -> QuerySet[User]:
one_month_ago = (timezone.now() - datetime.timedelta(days=30)).timestamp()
return User.objects.filter(company_id=company_id, is_active=True,
last_seen__gte=one_month_ago)
위의 함수 서명은 다음보다 더 가독성이 좋습니다.def get_active_users(company_id: int) -> QuerySet:
Iterable[User]또한 다른 메서드에서 반환된 쿼리 세트가 호출될 때 형식 검사자가 불만을 제기합니다.
def func() -> Iterable[User]:
return User.objects.all()
users = func()
users.filter(email__startswith='support')
MyPy 출력
"Iterable[User]" has no attribute "filter"
은 IMHO, 입니다를 입니다.QuerySet그리고 반복기에 대한 일반 반환 유형을 지정합니다.
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
그러면 다음과 같이 사용할 수 있습니다.
users: QuerySetType[User] = User.objects.all()
for user in users:
print(user.email) # typing OK!
user = users.first() # typing OK!
주석 모듈을 가져오면 실제로 원하는 작업을 수행할 수 있습니다.
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()
MyPy와 Python 인터프리터는 이에 대해 불평하거나 예외를 제기하지 않습니다(python 3.7에서 테스트됨).MyPy는 아마 타이핑을 할 수 없을 것입니다. 하지만 반품 유형을 문서화하는 것만 원하신다면, 이 정도면 충분합니다.
from typing import Iterable
def func(queryset_or_list: Iterable[MyModel]):
pass
쿼리 세트와 모델 인스턴스 목록은 모두 반복 가능 개체입니다.
를 사용한다는 되었습니다.typing.Sequence유사한 문제를 해결하는 방법:
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)
제가 문서로 아는 한:
시퀀스는 실제 유형에 관계없이 len() 및 .getitem()을 지원하는 모든 것입니다.
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
언급URL : https://stackoverflow.com/questions/42397502/how-to-use-python-type-hints-with-django-queryset
'programing' 카테고리의 다른 글
| Controller As 표기법을 사용하여 상위 속성에 액세스하는 방법 (0) | 2023.10.09 |
|---|---|
| ViewPager를 사용하지 않고 탭 레이아웃 (0) | 2023.10.09 |
| 워드프레스 API 호출 후 무효 cf7 (0) | 2023.10.09 |
| 스크롤IntoView는 모든 브라우저에서 작동합니까? (0) | 2023.10.04 |
| j수정 방법 정의되지 않은 '_DT_CellIndex' 속성을 설정할 수 없습니까? (0) | 2023.10.04 |