Але*_*дин 43 python django type-hinting
是否可以使用Python类型提示在Django QuerySet中指定记录类型?有点像QuerySet[SomeModel]?
例如,我们有模型:
class SomeModel(models.Model):
smth = models.IntegerField()
Run Code Online (Sandbox Code Playgroud)
我们想在func中将该模型的QuerySet作为param传递:
def somefunc(rows: QuerySet):
pass
Run Code Online (Sandbox Code Playgroud)
但是如何在QuerySet中指定记录类型,如List[SomeModel]:
def somefunc(rows: List[SomeModel]):
pass
Run Code Online (Sandbox Code Playgroud)
但是使用QuerySet?
A. *_*arr 31
一种解决方案可能是使用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
Run Code Online (Sandbox Code Playgroud)
现在,当您对row参数进行切片时,它将知道返回的类型是MyModel的另一个列表或MyModel的实例,同时还暗示QuerySet该类的方法也可用于row参数.
sob*_*evn 24
有一个称为django-stubs(名称后跟PEP561)的特殊包可以键入您的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')
Run Code Online (Sandbox Code Playgroud)
输出:
» 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'
Run Code Online (Sandbox Code Playgroud)
并使用模型和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,
)
Run Code Online (Sandbox Code Playgroud)
输出:
reveal_type(published_posts().first())
# => Union[server.apps.main.models.BlogPost*, None]
Run Code Online (Sandbox Code Playgroud)
django:https : //github.com/typeddjango/django-stubsdrf:https : //github.com/typeddjango/djangorestframework-stubsOr *_*uan 17
我制作了此帮助器类以获取泛型类型提示:
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
Run Code Online (Sandbox Code Playgroud)
然后像这样使用它:
def somefunc(row: ModelType[SomeModel]):
pass
Run Code Online (Sandbox Code Playgroud)
每次使用此类型时,这都会减少噪音,并使它可在模型之间使用(例如ModelType[DifferentModel])。
Kra*_*mar 14
QuerySet是返回任何模型的任何查询集的函数/方法的好方法。Django 查询集是可迭代的。但是,当返回类型非常特定于一种模型时,使用QuerySet[Model]over可能会更好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)
Run Code Online (Sandbox Code Playgroud)
上面的函数签名比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')
Run Code Online (Sandbox Code Playgroud)
MyPy 输出
"Iterable[User]" has no attribute "filter"
Run Code Online (Sandbox Code Playgroud)
Ram*_*tin 10
这是 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]: ...
Run Code Online (Sandbox Code Playgroud)
此类专门用于QuerySet对象,例如filter在查询中使用时。
样本:
from some_file import QueryType
sample_query: QueryType[SampleClass] = SampleClass.objects.filter(name=name)
Run Code Online (Sandbox Code Playgroud)
现在解释器将 识别sample_query为QuerySet对象,您将获得建议,例如count(),在遍历对象时,您将获得有关对象的建议SampleClass
注意
这种类型提示的格式从python3.6以后可用。
您还可以使用django_hint,它具有专门针对 Django 的提示类。
如果导入注释模块,您实际上可以做您想做的事情:
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()
Run Code Online (Sandbox Code Playgroud)
MyPy 和 Python 解释器都不会抱怨或引发异常(在 python 3.7 上测试)。MyPy 可能无法对其进行类型检查,但如果您想要的只是记录您的返回类型,那么这应该足够了。
恕我直言,正确的方法是定义一个继承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
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用它:
users: QuerySetType[User] = User.objects.all()
for user in users:
print(user.email) # typing OK!
user = users.first() # typing OK!
Run Code Online (Sandbox Code Playgroud)