Skip to content

Commit d1acff9

Browse files
committed
Fix Django db type errors
1 parent 031683c commit d1acff9

File tree

5 files changed

+147
-120
lines changed

5 files changed

+147
-120
lines changed

db/_settings.py

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@
88
from pathlib import Path
99
from typing import TYPE_CHECKING
1010

11+
import django_stubs_ext
12+
1113
if TYPE_CHECKING:
12-
from collections.abc import Sequence
14+
from collections.abc import Mapping, Sequence
1315
from typing import Final
1416

1517
__all__: "Sequence[str]" = ()
1618

17-
# Build paths inside the project like this: BASE_DIR / "subdir".
18-
BASE_DIR = Path(__file__).resolve().parent
19+
20+
# Monkeypatching Django, so stubs will work for all generics,
21+
# SOURCE: https://github.com/typeddjango/django-stubs
22+
django_stubs_ext.monkeypatch()
23+
24+
25+
# NOTE: Build paths inside the project like this: BASE_DIR / "subdir".
26+
BASE_DIR: "Final[Path]" = Path(__file__).resolve().parent
1927

2028
# NOTE: settings.py is called when setting up the mypy_django_plugin & when running Pytest. When mypy/Pytest runs no config settings variables are set, so they should not be accessed
2129
IMPORTED_BY_MYPY_OR_PYTEST: "Final[bool]" = any(
@@ -32,31 +40,29 @@
3240
SECRET_KEY = settings.DISCORD_BOT_TOKEN
3341

3442

35-
# Application definition
36-
37-
INSTALLED_APPS = ["django.contrib.contenttypes", "db.core.app_config.CoreConfig"]
38-
39-
MIDDLEWARE = ["django.middleware.common.CommonMiddleware"]
40-
41-
42-
# Database
43-
# https://docs.djangoproject.com/en/stable/ref/settings/#databases
43+
# Application Definition
4444

45-
DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "core.db"}}
45+
INSTALLED_APPS: "Final[Sequence[str]]" = [
46+
"django.contrib.contenttypes",
47+
"db.core.app_config.CoreConfig",
48+
]
4649

50+
MIDDLEWARE: "Final[Sequence[str]]" = ["django.middleware.common.CommonMiddleware"]
4751

48-
# Internationalization
49-
# https://docs.djangoproject.com/en/stable/topics/i18n/
5052

51-
LANGUAGE_CODE = "en-gb"
53+
# Database Settings
5254

53-
TIME_ZONE = "Europe/London"
55+
DATABASES: "Final[Mapping[str, object]]" = { # SOURCE: https://docs.djangoproject.com/en/stable/ref/settings#databases
56+
"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "core.db"}
57+
}
5458

55-
USE_I18N = True
59+
DEFAULT_AUTO_FIELD: "Final[str]" = "django.db.models.BigAutoField" # SOURCE: https://docs.djangoproject.com/en/stable/ref/settings#default-auto-field
5660

57-
USE_TZ = True
5861

59-
# Default primary key field type
60-
# https://docs.djangoproject.com/en/stable/ref/settings/#default-auto-field
62+
# Internationalisation, Language & Time Settings
63+
# SOURCE: https://docs.djangoproject.com/en/stable/topics/i18n
6164

62-
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
65+
LANGUAGE_CODE: "Final[str]" = "en-gb"
66+
TIME_ZONE: "Final[str]" = "Europe/London"
67+
USE_I18N: "Final[bool]" = True
68+
USE_TZ: "Final[bool]" = True

db/core/models/__init__.py

Lines changed: 71 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@
99
from django.core.validators import MinValueValidator, RegexValidator
1010
from django.db import models
1111
from django.utils.translation import gettext_lazy as _
12+
from django_stubs_ext.db.models import TypedModelMeta
1213

1314
from .utils import AsyncBaseModel, DiscordMember
1415

1516
if TYPE_CHECKING:
1617
from collections.abc import Sequence
17-
from typing import Final
18+
from collections.abc import Set as AbstractSet
19+
from typing import ClassVar, Final
20+
21+
from django.db.models.constraints import BaseConstraint
22+
from django_stubs_ext import StrOrPromise
23+
1824

1925
__all__: "Sequence[str]" = (
2026
"AssignedCommitteeAction",
@@ -47,23 +53,23 @@ class Status(models.TextChoices):
4753
DiscordMember,
4854
on_delete=models.CASCADE,
4955
related_name="assigned_committee_actions",
50-
verbose_name="Discord Member",
56+
verbose_name=_("Discord Member"),
5157
blank=False,
5258
null=False,
5359
unique=False,
5460
)
55-
description = models.TextField("Description", max_length=200, null=False, blank=False)
61+
description = models.TextField(_("Description"), max_length=200, null=False, blank=False)
5662
status = models.CharField(
5763
max_length=3, choices=Status, default=Status.NOT_STARTED, null=False, blank=False
5864
)
5965

60-
class Meta: # noqa: D106
61-
verbose_name = "Assigned Committee Action"
62-
constraints = [ # noqa: RUF012
66+
class Meta(TypedModelMeta): # noqa: D106
67+
verbose_name: "ClassVar[StrOrPromise]" = _("Assigned Committee Action")
68+
constraints: "ClassVar[list[BaseConstraint] | tuple[BaseConstraint, ...]]" = (
6369
models.UniqueConstraint(
6470
fields=["discord_member", "description"], name="unique_user_action"
65-
)
66-
]
71+
),
72+
)
6773

6874
@override
6975
def __repr__(self) -> str:
@@ -88,15 +94,19 @@ class IntroductionReminderOptOutMember(AsyncBaseModel):
8894
DiscordMember,
8995
on_delete=models.CASCADE,
9096
related_name="opted_out_of_introduction_reminders",
91-
verbose_name="Discord Member",
97+
verbose_name=_("Discord Member"),
9298
blank=False,
9399
null=False,
94100
primary_key=True,
95101
)
96102

97-
class Meta: # noqa: D106
98-
verbose_name = "Discord Member that has Opted-Out of Introduction Reminders"
99-
verbose_name_plural = "Discord Members that have Opted-Out of Introduction Reminders"
103+
class Meta(TypedModelMeta): # noqa: D106
104+
verbose_name: "ClassVar[StrOrPromise]" = _(
105+
"Discord Member that has Opted-Out of Introduction Reminders"
106+
)
107+
verbose_name_plural: "ClassVar[StrOrPromise]" = _(
108+
"Discord Members that have Opted-Out of Introduction Reminders"
109+
)
100110

101111

102112
class SentOneOffIntroductionReminderMember(AsyncBaseModel):
@@ -114,17 +124,17 @@ class SentOneOffIntroductionReminderMember(AsyncBaseModel):
114124
DiscordMember,
115125
on_delete=models.CASCADE,
116126
related_name="sent_one_off_introduction_reminder",
117-
verbose_name="Discord Member",
127+
verbose_name=_("Discord Member"),
118128
blank=False,
119129
null=False,
120130
primary_key=True,
121131
)
122132

123-
class Meta: # noqa: D106
124-
verbose_name = (
133+
class Meta(TypedModelMeta): # noqa: D106
134+
verbose_name: "ClassVar[StrOrPromise]" = _(
125135
"Discord Member that has had a one-off Introduction reminder sent to their DMs"
126136
)
127-
verbose_name_plural = (
137+
verbose_name_plural: "ClassVar[StrOrPromise]" = _(
128138
"Discord Members that have had a one-off Introduction reminder sent to their DMs"
129139
)
130140

@@ -147,15 +157,17 @@ class SentGetRolesReminderMember(AsyncBaseModel):
147157
DiscordMember,
148158
on_delete=models.CASCADE,
149159
related_name="sent_get_roles_reminder",
150-
verbose_name="Discord Member",
160+
verbose_name=_("Discord Member"),
151161
blank=False,
152162
null=False,
153163
primary_key=True,
154164
)
155165

156-
class Meta: # noqa: D106
157-
verbose_name = 'Discord Member that has had a "Get Roles" reminder sent to their DMs'
158-
verbose_name_plural = (
166+
class Meta(TypedModelMeta): # noqa: D106
167+
verbose_name: "ClassVar[StrOrPromise]" = _(
168+
'Discord Member that has had a "Get Roles" reminder sent to their DMs'
169+
)
170+
verbose_name_plural: "ClassVar[StrOrPromise]" = _(
159171
'Discord Members that have had a "Get Roles" reminder sent to their DMs'
160172
)
161173

@@ -175,22 +187,26 @@ class GroupMadeMember(AsyncBaseModel):
175187
INSTANCES_NAME_PLURAL: str = "Group Made Members"
176188

177189
hashed_group_member_id = models.CharField(
178-
"Hashed Group Member ID",
190+
_("Hashed Group Member ID"),
179191
unique=True,
180192
null=False,
181193
blank=False,
182194
max_length=64,
183195
validators=[
184196
RegexValidator(
185197
r"\A[A-Fa-f\d]{64}\Z",
186-
"hashed_group_member_id must be a valid sha256 hex-digest.",
198+
_("hashed_group_member_id must be a valid sha256 hex-digest."),
187199
)
188200
],
189201
)
190202

191-
class Meta: # noqa: D106
192-
verbose_name = "Hashed Group ID of User that has been made Member"
193-
verbose_name_plural = "Hashed Group IDs of Users that have been made Member"
203+
class Meta(TypedModelMeta): # noqa: D106
204+
verbose_name: "ClassVar[StrOrPromise]" = _(
205+
"Hashed Group ID of User that has been made Member"
206+
)
207+
verbose_name_plural: "ClassVar[StrOrPromise]" = _(
208+
"Hashed Group IDs of Users that have been made Member"
209+
)
194210

195211
@override
196212
def __setattr__(self, name: str, value: object) -> None:
@@ -236,8 +252,8 @@ def hash_group_member_id(
236252

237253
@classmethod
238254
@override
239-
def get_proxy_field_names(cls) -> set[str]:
240-
return super().get_proxy_field_names() | {"group_member_id"}
255+
def _get_proxy_field_names(cls) -> "AbstractSet[str]":
256+
return {*super()._get_proxy_field_names(), "group_member_id"}
241257

242258

243259
class DiscordReminder(AsyncBaseModel):
@@ -249,51 +265,52 @@ class DiscordReminder(AsyncBaseModel):
249265
DiscordMember,
250266
on_delete=models.CASCADE,
251267
related_name="reminders",
252-
verbose_name="Discord Member",
268+
verbose_name=_("Discord Member"),
253269
blank=False,
254270
null=False,
255271
unique=False,
256272
)
257273
message = models.TextField(
258-
"Message to remind User", max_length=1500, null=False, blank=True
274+
_("Message to remind User"), max_length=1500, null=False, blank=True
259275
)
260276
_channel_id = models.CharField(
261-
"Discord Channel ID of the channel that the reminder needs to be sent in",
277+
_("Discord Channel ID of the channel that the reminder needs to be sent in"),
262278
unique=False,
263279
null=False,
264280
blank=False,
265281
max_length=30,
266282
validators=[
267283
RegexValidator(
268284
r"\A\d{17,20}\Z",
269-
"channel_id must be a valid Discord channel ID (see https://docs.pycord.dev/en/stable/api/abcs.html#discord.abc.Snowflake.id)",
285+
_(
286+
"channel_id must be a valid Discord channel ID (see https://docs.pycord.dev/en/stable/api/abcs.html#discord.abc.Snowflake.id)"
287+
),
270288
)
271289
],
272290
)
273291
_channel_type = models.IntegerField(
274-
"Discord Channel Type of the channel that the reminder needs to be sent in",
292+
_("Discord Channel Type of the channel that the reminder needs to be sent in"),
275293
choices=[
276294
(channel_type.value, channel_type.name) for channel_type in discord.ChannelType
277295
],
278296
null=True,
279297
blank=True,
280298
)
281299
send_datetime = models.DateTimeField(
282-
"Date & time to send reminder", unique=False, null=False, blank=False
300+
_("Date & time to send reminder"), unique=False, null=False, blank=False
283301
)
284302

285303
@property
286-
def channel_id(self) -> int:
287-
"""The ID of the channel that the reminder needs to be sent in."""
304+
def channel_id(self) -> int: # noqa: D102
288305
return int(self._channel_id)
289306

290307
@channel_id.setter
291308
def channel_id(self, channel_id: str | int) -> None:
292309
self._channel_id = str(channel_id)
293310

294311
@property
295-
def channel_type(self) -> discord.ChannelType:
296-
"""The type of channel that the reminder needs to be sent in."""
312+
def channel_type(self) -> discord.ChannelType: # noqa: D102
313+
# NOTE: This finds the type of channel that the reminder needs to be sent in."""
297314
return discord.ChannelType(self._channel_type)
298315

299316
@channel_type.setter
@@ -309,15 +326,15 @@ def channel_type(self, channel_type: discord.ChannelType | int) -> None:
309326

310327
self._channel_type = channel_type
311328

312-
class Meta: # noqa: D106
313-
verbose_name = "A Reminder for a Discord Member"
314-
verbose_name_plural = "Reminders for Discord Members"
315-
constraints = [ # noqa: RUF012
329+
class Meta(TypedModelMeta): # noqa: D106
330+
verbose_name: "ClassVar[StrOrPromise]" = _("A Reminder for a Discord Member")
331+
verbose_name_plural: "ClassVar[StrOrPromise]" = _("Reminders for Discord Members")
332+
constraints: "ClassVar[list[BaseConstraint] | tuple[BaseConstraint, ...]]" = (
316333
models.UniqueConstraint(
317334
fields=["discord_member", "message", "_channel_id"],
318335
name="unique_user_channel_message",
319-
)
320-
]
336+
),
337+
)
321338

322339
@override
323340
def __str__(self) -> str:
@@ -358,8 +375,8 @@ def get_formatted_message(self, user_mention: str | None) -> str:
358375

359376
@classmethod
360377
@override
361-
def get_proxy_field_names(cls) -> set[str]:
362-
return super().get_proxy_field_names() | {"channel_id", "channel_type"}
378+
def _get_proxy_field_names(cls) -> "AbstractSet[str]":
379+
return {*super()._get_proxy_field_names(), "channel_id", "channel_type"}
363380

364381

365382
class LeftDiscordMember(AsyncBaseModel):
@@ -375,20 +392,19 @@ class LeftDiscordMember(AsyncBaseModel):
375392
_roles = models.JSONField("List of roles a Discord Member had")
376393

377394
@property
378-
def roles(self) -> set[str]:
379-
"""Retrieve the set of roles the member had when they left your Discord guild."""
395+
def roles(self) -> set[str]: # noqa: D102
380396
return set(self._roles)
381397

382398
@roles.setter
383399
def roles(self, roles: set[str]) -> None:
384400
self._roles = list(roles)
385401

386-
class Meta: # noqa: D106
387-
verbose_name = (
402+
class Meta(TypedModelMeta): # noqa: D106
403+
verbose_name: "ClassVar[StrOrPromise]" = _(
388404
"A List of Roles that a Discord Member had "
389405
"when they left your group's Discord guild"
390406
)
391-
verbose_name_plural = (
407+
verbose_name_plural: "ClassVar[StrOrPromise]" = _(
392408
"Lists of Roles that Discord Members had when they left your group's Discord guild"
393409
)
394410

@@ -412,8 +428,8 @@ def clean(self) -> None:
412428

413429
@classmethod
414430
@override
415-
def get_proxy_field_names(cls) -> set[str]:
416-
return super().get_proxy_field_names() | {"roles"}
431+
def _get_proxy_field_names(cls) -> "AbstractSet[str]":
432+
return {*super()._get_proxy_field_names(), "roles"}
417433

418434

419435
class DiscordMemberStrikes(AsyncBaseModel):
@@ -449,12 +465,12 @@ class DiscordMemberStrikes(AsyncBaseModel):
449465
default=0,
450466
)
451467

452-
class Meta: # noqa: D106
453-
verbose_name = (
468+
class Meta(TypedModelMeta): # noqa: D106
469+
verbose_name: "ClassVar[StrOrPromise]" = _(
454470
"Discord Member that has been previously given one or more strikes "
455471
"because they broke one or more of your group's Discord guild rules"
456472
)
457-
verbose_name_plural = (
473+
verbose_name_plural: "ClassVar[StrOrPromise]" = _(
458474
"Discord Members that have been previously given one or more strikes "
459475
"because they broke one or more of your group's Discord guild rules"
460476
)

0 commit comments

Comments
 (0)