Skip to content

Commit 5a1dd9f

Browse files
committed
feat: Typehint inbuilt models and fields
1 parent 74feabd commit 5a1dd9f

File tree

17 files changed

+218
-69
lines changed

17 files changed

+218
-69
lines changed

django-stubs/contrib/admin/models.pyi

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from typing import Any, ClassVar, Literal, overload
22
from uuid import UUID
33

4+
from django.contrib.auth.models import AbstractUser
5+
from django.contrib.contenttypes.models import ContentType
46
from django.db import models
57
from django.db.models import QuerySet
68
from django.db.models.base import Model
9+
from django.db.models.expressions import Combinable
710
from typing_extensions import deprecated
811

912
ADDITION: int
@@ -44,10 +47,14 @@ class LogEntryManager(models.Manager[LogEntry]):
4447
) -> list[LogEntry]: ...
4548

4649
class LogEntry(models.Model):
50+
id: models.AutoField
51+
pk: models.AutoField
4752
action_time: models.DateTimeField
48-
user: models.ForeignKey
49-
content_type: models.ForeignKey
50-
object_id: models.TextField
53+
user: models.ForeignKey[AbstractUser | Combinable, AbstractUser]
54+
user_id: Any
55+
content_type: models.ForeignKey[ContentType | Combinable | None, ContentType | None]
56+
content_type_id: int | None
57+
object_id: models.TextField[str | int | Combinable | None, str | None]
5158
object_repr: models.CharField
5259
action_flag: models.PositiveSmallIntegerField
5360
change_message: models.TextField

django-stubs/contrib/auth/base_user.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections.abc import Iterable
2+
from datetime import date, datetime
23
from typing import Any, ClassVar, Literal, TypeVar, overload
34

45
from django.db import models
@@ -19,7 +20,7 @@ class AbstractBaseUser(models.Model):
1920
REQUIRED_FIELDS: ClassVar[list[str]]
2021

2122
password = models.CharField(max_length=128)
22-
last_login = models.DateTimeField(blank=True, null=True)
23+
last_login = models.DateTimeField[str | datetime | date | Combinable, datetime | None](blank=True, null=True)
2324
is_active: bool | BooleanField[bool | Combinable, bool]
2425

2526
def get_username(self) -> str: ...

django-stubs/contrib/auth/models.pyi

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from collections.abc import Iterable
2-
from typing import Any, ClassVar, Literal, TypeVar
2+
from typing import Any, ClassVar, Literal, TypeVar, type_check_only
33

44
from django.contrib.auth.base_user import AbstractBaseUser as AbstractBaseUser
55
from django.contrib.auth.base_user import BaseUserManager as BaseUserManager
@@ -8,6 +8,7 @@ from django.contrib.contenttypes.models import ContentType
88
from django.db import models
99
from django.db.models import QuerySet
1010
from django.db.models.base import Model
11+
from django.db.models.expressions import Combinable
1112
from django.db.models.manager import EmptyManager
1213
from django.utils.functional import _StrOrPromise
1314
from typing_extensions import Self, TypeAlias
@@ -20,22 +21,40 @@ class PermissionManager(models.Manager[Permission]):
2021
def get_by_natural_key(self, codename: str, app_label: str, model: str) -> Permission: ...
2122

2223
class Permission(models.Model):
23-
content_type_id: int
2424
objects: ClassVar[PermissionManager]
2525

26+
id: models.AutoField
27+
pk: models.AutoField
2628
name = models.CharField(max_length=255)
27-
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
29+
content_type = models.ForeignKey[ContentType | Combinable, ContentType](ContentType, on_delete=models.CASCADE)
30+
content_type_id: int
2831
codename = models.CharField(max_length=100)
32+
group_set: models.ManyToManyField[Group, Group_permissions]
2933
def natural_key(self) -> tuple[str, str, str]: ...
3034

3135
class GroupManager(models.Manager[Group]):
3236
def get_by_natural_key(self, name: str) -> Group: ...
3337

38+
# This is a model that only exists in Django's model registry and doesn't have any
39+
# class statement form. It's the through model between 'Group' and 'Permission'.
40+
@type_check_only
41+
class Group_permissions(models.Model):
42+
objects: ClassVar[models.Manager[Self]]
43+
44+
id: models.AutoField
45+
pk: models.AutoField
46+
group: models.ForeignKey[Group | Combinable, Group]
47+
group_id: int
48+
permission: models.ForeignKey[Permission | Combinable, Permission]
49+
permission_id: int
50+
3451
class Group(models.Model):
3552
objects: ClassVar[GroupManager]
3653

54+
id: models.AutoField
55+
pk: models.AutoField
3756
name = models.CharField(max_length=150)
38-
permissions = models.ManyToManyField(Permission)
57+
permissions = models.ManyToManyField[Permission, Group_permissions](Permission)
3958
def natural_key(self) -> tuple[str]: ...
4059

4160
_T = TypeVar("_T", bound=Model)
@@ -90,7 +109,9 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin):
90109
self, subject: _StrOrPromise, message: _StrOrPromise, from_email: str = ..., **kwargs: Any
91110
) -> None: ...
92111

93-
class User(AbstractUser): ...
112+
class User(AbstractUser):
113+
id: models.AutoField
114+
pk: models.AutoField
94115

95116
class AnonymousUser:
96117
id: None

django-stubs/contrib/contenttypes/models.pyi

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
from typing import Any, ClassVar
22

3+
from django.contrib.admin.models import LogEntry
4+
from django.contrib.auth.models import Permission
35
from django.db import models
46
from django.db.models.base import Model
7+
from django.db.models.fields.related_descriptors import ReverseManyToOneDescriptor
58
from django.db.models.query import QuerySet
69

710
class ContentTypeManager(models.Manager[ContentType]):
@@ -12,13 +15,16 @@ class ContentTypeManager(models.Manager[ContentType]):
1215
def clear_cache(self) -> None: ...
1316

1417
class ContentType(models.Model):
15-
id: int
18+
id: models.AutoField
19+
pk: models.AutoField
1620
app_label = models.CharField(max_length=100)
1721
model = models.CharField(max_length=100)
22+
logentry_set: ReverseManyToOneDescriptor[LogEntry]
23+
permission_set: ReverseManyToOneDescriptor[Permission]
1824
objects: ClassVar[ContentTypeManager]
1925
@property
2026
def name(self) -> str: ...
2127
def model_class(self) -> type[Model] | None: ...
2228
def get_object_for_this_type(self, **kwargs: Any) -> Model: ...
23-
def get_all_objects_for_this_type(self, **kwargs: Any) -> QuerySet: ...
29+
def get_all_objects_for_this_type(self, **kwargs: Any) -> QuerySet[Model]: ...
2430
def natural_key(self) -> tuple[str, str]: ...
Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
1+
from typing import ClassVar, type_check_only
2+
13
from django.contrib.sites.models import Site
24
from django.db import models
5+
from django.db.models.expressions import Combinable
6+
from typing_extensions import Self
7+
8+
# This is a model that only exists in Django's model registry and doesn't have any
9+
# class statement form. It's the through model between 'FlatPage' and 'Site'.
10+
@type_check_only
11+
class FlatPage_sites(models.Model):
12+
objects: ClassVar[models.Manager[Self]]
13+
14+
id: models.AutoField
15+
pk: models.AutoField
16+
site: models.ForeignKey[Site | Combinable, Site]
17+
site_id: int
18+
flatpage: models.ForeignKey[FlatPage | Combinable, FlatPage]
19+
flatpage_id: int
320

421
class FlatPage(models.Model):
22+
id: models.AutoField
23+
pk: models.AutoField
524
url: models.CharField
625
title: models.CharField
726
content: models.TextField
827
enable_comments: models.BooleanField
928
template_name: models.CharField
1029
registration_required: models.BooleanField
11-
sites: models.ManyToManyField[Site, Site]
30+
sites: models.ManyToManyField[Site, FlatPage_sites]
1231
def get_absolute_url(self) -> str: ...
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
from django.contrib.sites.models import Site
12
from django.db import models
3+
from django.db.models.expressions import Combinable
24

35
class Redirect(models.Model):
4-
site: models.ForeignKey
6+
id: models.AutoField
7+
pk: models.AutoField
8+
site: models.ForeignKey[Site | Combinable, Site]
9+
site_id: int
510
old_path: models.CharField
611
new_path: models.CharField

django-stubs/contrib/sessions/base_session.pyi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ from typing import Any, ClassVar, TypeVar
33

44
from django.contrib.sessions.backends.base import SessionBase
55
from django.db import models
6+
from django.db.models.expressions import Combinable
67
from typing_extensions import Self
78

89
_T = TypeVar("_T", bound=AbstractBaseSession)
@@ -12,7 +13,8 @@ class BaseSessionManager(models.Manager[_T]):
1213
def save(self, session_key: str, session_dict: dict[str, Any], expire_date: datetime) -> _T: ...
1314

1415
class AbstractBaseSession(models.Model):
15-
session_key = models.CharField(primary_key=True)
16+
pk: models.CharField[str | int | Combinable | None, str]
17+
session_key = models.CharField[str | int | Combinable | None, str](primary_key=True)
1618
session_data = models.TextField()
1719
expire_date = models.DateTimeField()
1820
objects: ClassVar[BaseSessionManager[Self]]
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
from typing import TypeVar
1+
from typing import ClassVar, TypeVar
22

33
from django.contrib.sessions.base_session import AbstractBaseSession, BaseSessionManager
4+
from typing_extensions import Self
45

56
_T = TypeVar("_T", bound=Session)
67

78
class SessionManager(BaseSessionManager[_T]): ...
8-
class Session(AbstractBaseSession): ...
9+
10+
class Session(AbstractBaseSession):
11+
objects: ClassVar[SessionManager[Self]] # type: ignore[assignment]

django-stubs/contrib/sites/models.pyi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from typing import Any, ClassVar
22

3+
from django.contrib.flatpages.models import FlatPage, FlatPage_sites
4+
from django.contrib.redirects.models import Redirect
35
from django.db import models
6+
from django.db.models.fields.related_descriptors import ManyToManyDescriptor, ReverseManyToOneDescriptor
47
from django.http.request import HttpRequest
58

69
SITE_CACHE: Any
@@ -13,8 +16,12 @@ class SiteManager(models.Manager[Site]):
1316
class Site(models.Model):
1417
objects: ClassVar[SiteManager]
1518

19+
id: models.AutoField
20+
pk: models.AutoField
1621
domain = models.CharField(max_length=100)
1722
name = models.CharField(max_length=50)
23+
flatpage_set: ManyToManyDescriptor[FlatPage, FlatPage_sites]
24+
redirect_set: ReverseManyToOneDescriptor[Redirect]
1825
def natural_key(self) -> tuple[str]: ...
1926

2027
def clear_site_cache(sender: type[Site], **kwargs: Any) -> None: ...

0 commit comments

Comments
 (0)