Skip to content

Commit c8f40fc

Browse files
committed
Add type stubs and import for model fields
1 parent 236a341 commit c8f40fc

File tree

20 files changed

+1015
-187
lines changed

20 files changed

+1015
-187
lines changed

example/app/users/models.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from plain import models
2+
from plain.models import types
23
from plain.passwords.models import PasswordField
34

45

56
@models.register_model
67
class User(models.Model):
7-
email = models.EmailField()
8+
email: str = types.EmailField()
89
password = PasswordField()
9-
is_admin = models.BooleanField(default=False)
10+
is_admin: bool = types.BooleanField(default=False)
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from plain import models
2+
from plain.models import types
23

34

45
@models.register_model
56
class User(models.Model):
6-
username = models.CharField(max_length=255)
7-
is_admin = models.BooleanField(default=False)
7+
username: str = types.CharField(max_length=255)
8+
is_admin: bool = types.BooleanField(default=False)

plain-api/plain/api/models.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import binascii
22
import os
33
import uuid
4+
from datetime import datetime
5+
from uuid import UUID
46

57
from plain import models
8+
from plain.models import types
69

710

811
def generate_token() -> str:
@@ -11,17 +14,17 @@ def generate_token() -> str:
1114

1215
@models.register_model
1316
class APIKey(models.Model):
14-
uuid = models.UUIDField(default=uuid.uuid4)
15-
created_at = models.DateTimeField(auto_now_add=True)
16-
updated_at = models.DateTimeField(auto_now=True)
17-
expires_at = models.DateTimeField(required=False, allow_null=True)
18-
last_used_at = models.DateTimeField(required=False, allow_null=True)
17+
uuid: UUID = types.UUIDField(default=uuid.uuid4)
18+
created_at: datetime = types.DateTimeField(auto_now_add=True)
19+
updated_at: datetime = types.DateTimeField(auto_now=True)
20+
expires_at: datetime | None = types.DateTimeField(required=False, allow_null=True)
21+
last_used_at: datetime | None = types.DateTimeField(required=False, allow_null=True)
1922

20-
name = models.CharField(max_length=255, required=False)
23+
name: str = types.CharField(max_length=255, required=False)
2124

22-
token = models.CharField(max_length=40, default=generate_token)
25+
token: str = types.CharField(max_length=40, default=generate_token)
2326

24-
api_version = models.CharField(max_length=255, required=False)
27+
api_version: str = types.CharField(max_length=255, required=False)
2528

2629
model_options = models.Options(
2730
constraints=[
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from plain import models
2+
from plain.models import types
23

34

45
@models.register_model
56
class User(models.Model):
6-
username = models.CharField(max_length=255)
7-
is_admin = models.BooleanField(default=False)
7+
username: str = types.CharField(max_length=255)
8+
is_admin: bool = types.BooleanField(default=False)

plain-cache/plain/cache/models.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from __future__ import annotations
22

3-
from typing import Self
3+
from datetime import datetime
4+
from typing import Any, Self
45

56
from plain import models
7+
from plain.models import types
68
from plain.utils import timezone
79

810

@@ -19,11 +21,11 @@ def forever(self) -> Self:
1921

2022
@models.register_model
2123
class CachedItem(models.Model):
22-
key = models.CharField(max_length=255)
23-
value = models.JSONField(required=False, allow_null=True)
24-
expires_at = models.DateTimeField(required=False, allow_null=True)
25-
created_at = models.DateTimeField(auto_now_add=True)
26-
updated_at = models.DateTimeField(auto_now=True)
24+
key: str = types.CharField(max_length=255)
25+
value: Any = types.JSONField(required=False, allow_null=True)
26+
expires_at: datetime | None = types.DateTimeField(required=False, allow_null=True)
27+
created_at: datetime = types.DateTimeField(auto_now_add=True)
28+
updated_at: datetime = types.DateTimeField(auto_now=True)
2729

2830
query = CachedItemQuerySet()
2931

plain-flags/plain/flags/models.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
from __future__ import annotations
2+
13
import re
4+
from datetime import datetime
25

36
from plain import models
47
from plain.exceptions import ValidationError
8+
from plain.models import types
59

610

711
def validate_flag_name(value: str) -> None:
@@ -11,11 +15,11 @@ def validate_flag_name(value: str) -> None:
1115

1216
@models.register_model
1317
class FlagResult(models.Model):
14-
created_at = models.DateTimeField(auto_now_add=True)
15-
updated_at = models.DateTimeField(auto_now=True)
16-
flag = models.ForeignKey("Flag", on_delete=models.CASCADE)
17-
key = models.CharField(max_length=255)
18-
value = models.JSONField()
18+
created_at: datetime = types.DateTimeField(auto_now_add=True)
19+
updated_at: datetime = types.DateTimeField(auto_now=True)
20+
flag: Flag = types.ForeignKey("Flag", on_delete=models.CASCADE)
21+
key: str = types.CharField(max_length=255)
22+
value = types.JSONField()
1923

2024
model_options = models.Options(
2125
constraints=[
@@ -31,19 +35,19 @@ def __str__(self) -> str:
3135

3236
@models.register_model
3337
class Flag(models.Model):
34-
created_at = models.DateTimeField(auto_now_add=True)
35-
updated_at = models.DateTimeField(auto_now=True)
36-
name = models.CharField(max_length=255, validators=[validate_flag_name])
38+
created_at: datetime = types.DateTimeField(auto_now_add=True)
39+
updated_at: datetime = types.DateTimeField(auto_now=True)
40+
name: str = types.CharField(max_length=255, validators=[validate_flag_name])
3741

3842
# Optional description that can be filled in after the flag is used/created
39-
description = models.TextField(required=False)
43+
description: str = types.TextField(required=False)
4044

4145
# To manually disable a flag before completing deleting
4246
# (good to disable first to make sure the code doesn't use the flag anymore)
43-
enabled = models.BooleanField(default=True)
47+
enabled: bool = types.BooleanField(default=True)
4448

4549
# To provide an easier way to see if a flag is still being used
46-
used_at = models.DateTimeField(required=False, allow_null=True)
50+
used_at: datetime | None = types.DateTimeField(required=False, allow_null=True)
4751

4852
model_options = models.Options(
4953
constraints=[

plain-jobs/plain/jobs/models.py

Lines changed: 73 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import datetime
44
import logging
55
import traceback
6-
import uuid
7-
from typing import TYPE_CHECKING, Self
6+
from typing import TYPE_CHECKING, Any, Self
7+
from uuid import UUID, uuid4
88

99
from opentelemetry import trace
1010
from opentelemetry.semconv._incubating.attributes.code_attributes import (
@@ -23,7 +23,7 @@
2323
from opentelemetry.trace import Link, SpanContext, SpanKind
2424

2525
from plain import models
26-
from plain.models import transaction
26+
from plain.models import transaction, types
2727
from plain.models.expressions import F
2828
from plain.runtime import settings
2929
from plain.utils import timezone
@@ -44,25 +44,31 @@ class JobRequest(models.Model):
4444
Keep all pending job requests in a single table.
4545
"""
4646

47-
created_at = models.DateTimeField(auto_now_add=True)
48-
uuid = models.UUIDField(default=uuid.uuid4)
47+
created_at: datetime.datetime = types.DateTimeField(auto_now_add=True)
48+
uuid: UUID = types.UUIDField(default=uuid4)
4949

50-
job_class = models.CharField(max_length=255)
51-
parameters = models.JSONField(required=False, allow_null=True)
52-
priority = models.SmallIntegerField(default=0)
53-
source = models.TextField(required=False)
54-
queue = models.CharField(default="default", max_length=255)
50+
job_class: str = types.CharField(max_length=255)
51+
parameters: dict[str, Any] | None = types.JSONField(required=False, allow_null=True)
52+
priority: int = types.SmallIntegerField(default=0)
53+
source: str = types.TextField(required=False)
54+
queue: str = types.CharField(default="default", max_length=255)
5555

56-
retries = models.SmallIntegerField(default=0)
57-
retry_attempt = models.SmallIntegerField(default=0)
56+
retries: int = types.SmallIntegerField(default=0)
57+
retry_attempt: int = types.SmallIntegerField(default=0)
5858

59-
concurrency_key = models.CharField(max_length=255, required=False)
59+
concurrency_key: str = types.CharField(max_length=255, required=False)
6060

61-
start_at = models.DateTimeField(required=False, allow_null=True)
61+
start_at: datetime.datetime | None = types.DateTimeField(
62+
required=False, allow_null=True
63+
)
6264

6365
# OpenTelemetry trace context
64-
trace_id = models.CharField(max_length=34, required=False, allow_null=True)
65-
span_id = models.CharField(max_length=18, required=False, allow_null=True)
66+
trace_id: str | None = types.CharField(
67+
max_length=34, required=False, allow_null=True
68+
)
69+
span_id: str | None = types.CharField(
70+
max_length=18, required=False, allow_null=True
71+
)
6672

6773
# expires_at = models.DateTimeField(required=False, allow_null=True)
6874

@@ -149,24 +155,30 @@ class JobProcess(models.Model):
149155
All active jobs are stored in this table.
150156
"""
151157

152-
uuid = models.UUIDField(default=uuid.uuid4)
153-
created_at = models.DateTimeField(auto_now_add=True)
154-
started_at = models.DateTimeField(required=False, allow_null=True)
158+
uuid: UUID = types.UUIDField(default=uuid4)
159+
created_at: datetime.datetime = types.DateTimeField(auto_now_add=True)
160+
started_at: datetime.datetime | None = types.DateTimeField(
161+
required=False, allow_null=True
162+
)
155163

156164
# From the JobRequest
157-
job_request_uuid = models.UUIDField()
158-
job_class = models.CharField(max_length=255)
159-
parameters = models.JSONField(required=False, allow_null=True)
160-
priority = models.SmallIntegerField(default=0)
161-
source = models.TextField(required=False)
162-
queue = models.CharField(default="default", max_length=255)
163-
retries = models.SmallIntegerField(default=0)
164-
retry_attempt = models.SmallIntegerField(default=0)
165-
concurrency_key = models.CharField(max_length=255, required=False)
165+
job_request_uuid: UUID = types.UUIDField()
166+
job_class: str = types.CharField(max_length=255)
167+
parameters: dict[str, Any] | None = types.JSONField(required=False, allow_null=True)
168+
priority: int = types.SmallIntegerField(default=0)
169+
source: str = types.TextField(required=False)
170+
queue: str = types.CharField(default="default", max_length=255)
171+
retries: int = types.SmallIntegerField(default=0)
172+
retry_attempt: int = types.SmallIntegerField(default=0)
173+
concurrency_key: str = types.CharField(max_length=255, required=False)
166174

167175
# OpenTelemetry trace context
168-
trace_id = models.CharField(max_length=34, required=False, allow_null=True)
169-
span_id = models.CharField(max_length=18, required=False, allow_null=True)
176+
trace_id: str | None = types.CharField(
177+
max_length=34, required=False, allow_null=True
178+
)
179+
span_id: str | None = types.CharField(
180+
max_length=18, required=False, allow_null=True
181+
)
170182

171183
query = JobQuerySet()
172184

@@ -229,7 +241,7 @@ def run(self) -> JobResult:
229241
self.save(update_fields=["started_at"])
230242

231243
try:
232-
job = jobs_registry.load_job(self.job_class, self.parameters)
244+
job = jobs_registry.load_job(self.job_class, self.parameters or {})
233245
job.job_process = self
234246

235247
try:
@@ -446,36 +458,46 @@ class JobResult(models.Model):
446458
All in-process and completed jobs are stored in this table.
447459
"""
448460

449-
uuid = models.UUIDField(default=uuid.uuid4)
450-
created_at = models.DateTimeField(auto_now_add=True)
461+
uuid: UUID = types.UUIDField(default=uuid4)
462+
created_at: datetime.datetime = types.DateTimeField(auto_now_add=True)
451463

452464
# From the Job
453-
job_process_uuid = models.UUIDField()
454-
started_at = models.DateTimeField(required=False, allow_null=True)
455-
ended_at = models.DateTimeField(required=False, allow_null=True)
456-
error = models.TextField(required=False)
457-
status = models.CharField(
465+
job_process_uuid: UUID = types.UUIDField()
466+
started_at: datetime.datetime | None = types.DateTimeField(
467+
required=False, allow_null=True
468+
)
469+
ended_at: datetime.datetime | None = types.DateTimeField(
470+
required=False, allow_null=True
471+
)
472+
error: str = types.TextField(required=False)
473+
status: str = types.CharField(
458474
max_length=20,
459475
choices=JobResultStatuses.choices,
460476
)
461477

462478
# From the JobRequest
463-
job_request_uuid = models.UUIDField()
464-
job_class = models.CharField(max_length=255)
465-
parameters = models.JSONField(required=False, allow_null=True)
466-
priority = models.SmallIntegerField(default=0)
467-
source = models.TextField(required=False)
468-
queue = models.CharField(default="default", max_length=255)
469-
retries = models.SmallIntegerField(default=0)
470-
retry_attempt = models.SmallIntegerField(default=0)
471-
concurrency_key = models.CharField(max_length=255, required=False)
479+
job_request_uuid: UUID = types.UUIDField()
480+
job_class: str = types.CharField(max_length=255)
481+
parameters: dict[str, Any] | None = types.JSONField(required=False, allow_null=True)
482+
priority: int = types.SmallIntegerField(default=0)
483+
source: str = types.TextField(required=False)
484+
queue: str = types.CharField(default="default", max_length=255)
485+
retries: int = types.SmallIntegerField(default=0)
486+
retry_attempt: int = types.SmallIntegerField(default=0)
487+
concurrency_key: str = types.CharField(max_length=255, required=False)
472488

473489
# Retries
474-
retry_job_request_uuid = models.UUIDField(required=False, allow_null=True)
490+
retry_job_request_uuid: UUID | None = types.UUIDField(
491+
required=False, allow_null=True
492+
)
475493

476494
# OpenTelemetry trace context
477-
trace_id = models.CharField(max_length=34, required=False, allow_null=True)
478-
span_id = models.CharField(max_length=18, required=False, allow_null=True)
495+
trace_id: str | None = types.CharField(
496+
max_length=34, required=False, allow_null=True
497+
)
498+
span_id: str | None = types.CharField(
499+
max_length=18, required=False, allow_null=True
500+
)
479501

480502
query = JobResultQuerySet()
481503

@@ -502,7 +524,7 @@ class JobResult(models.Model):
502524

503525
def retry_job(self, delay: int | None = None) -> JobRequest | None:
504526
retry_attempt = self.retry_attempt + 1
505-
job = jobs_registry.load_job(self.job_class, self.parameters)
527+
job = jobs_registry.load_job(self.job_class, self.parameters or {})
506528

507529
if delay is None:
508530
retry_delay = job.calculate_retry_delay(retry_attempt)

plain-models/plain/models/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from .options import Options
4242
from .query import QuerySet
4343
from .query_utils import Q
44+
from . import types
4445

4546
# This module exports the user-facing API for defining model classes,
4647
# with a secondary focus on the most common query utilities like Q.
@@ -104,4 +105,6 @@
104105
# From registry
105106
"register_model",
106107
"models_registry",
108+
# Typed field imports
109+
"types",
107110
]

plain-models/plain/models/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import warnings
55
from collections.abc import Iterable, Iterator, Sequence
66
from itertools import chain
7-
from typing import TYPE_CHECKING, Any
7+
from typing import TYPE_CHECKING, Any, dataclass_transform
88

99
if TYPE_CHECKING:
1010
from plain.models.meta import Meta
@@ -48,6 +48,7 @@ def __str__(self) -> str:
4848
DEFERRED = Deferred()
4949

5050

51+
@dataclass_transform(kw_only_default=True)
5152
class ModelBase(type):
5253
"""Metaclass for all models."""
5354

0 commit comments

Comments
 (0)